2009-11-23 67 views
2

給定了元音列表,我寫了函數startsWithVowel來調查一個單詞是否以元音開頭。正如你所看到的,我使用異常作爲控制流,這並不理想。如何更好地實現這一點?F#中的函數'startsWithVowel'

let vowel = ['a'; 'e'; 'i'; 'o'; 'u'] 

let startsWithVowel(str :string) = 
    try 
     List.findIndex (fun x -> x = str.[0]) vowel 
     true 
    with 
     | :? System.Collections.Generic.KeyNotFoundException -> false 

更新:tx to all:再一次我的經驗:從不猶豫問一個newbee問題。我看到了很多非常有用的言論,讓他們來了:-)

回答

7

使用sets高效查找

let vowels = Set.ofList ['a'; 'e'; 'i'; 'o'; 'u'] 

let startsWithVowel(str : string) = vowels |> Set.mem (str.[0]) 
+1

好主意。我只在我的回答中使用了List,因爲這是我們開始的。 Set對元音的意圖有更接近的含義。就表現而言,嗯,因爲收藏是如此之小。 – 2009-11-23 18:20:17

11

嘗試使用exists方法,而不是

let vowel = ['a'; 'e'; 'i'; 'o'; 'u'] 

let startsWithVowel(str :string) = List.exists (fun x -> x = str.[0]) vowel 

存在返回true,如果列表中的任何元素返回謂詞真,否則爲假。

+0

非常非常newbee錯過了,tx! – Peter 2009-11-23 17:22:36

7

另一種替代方案,tryFindIndex返回一些或無,而不是拋出一個異常:

> let vowel = ['A'; 'E'; 'I'; 'O'; 'U'; 'a'; 'e'; 'i'; 'o'; 'u'] 

let startsWithVowel(str :string) = 
    match List.tryFindIndex (fun x -> x = str.[0]) vowel with 
    | Some(_) -> true 
    | None -> false;; 

val vowel : char list = ['A'; 'E'; 'I'; 'O'; 'U'; 'a'; 'e'; 'i'; 'o'; 'u'] 
val startsWithVowel : string -> bool 

> startsWithVowel "Juliet";; 
val it : bool = false 
> startsWithVowel "Omaha";; 
val it : bool = true 
+0

爲什麼沒有人使用set? – Dario 2009-11-23 17:31:06

+5

因爲...套殺死了我的父親! – Juliet 2009-11-23 17:46:47

2

使用正則表達式:

open System.Text.RegularExpressions 

let startsWithVowel str = Regex.IsMatch(str, "^[AEIOU]", RegexOptions.IgnoreCase) 
+0

幾乎純粹的CLR,但無論如何,另一個讚賞的解決方案 – Peter 2009-11-23 20:37:26

6

我基準測試了此線程中提到的幾種方法(編輯:添加nr。 6)。

  1. 的List.exists方法(〜0.75秒)
  2. 的Set.contains方法(〜0.51秒)
  3. String.IndexOf(〜0.25秒)
  4. 非正則表達式編譯(〜 5 - 6秒)
  5. 編譯的正則表達式(〜1.0秒)
  6. 模式匹配(爲什麼我忘了這第一次)(〜0.17秒)

我用500000個隨機單詞填充了一個列表,並通過各種startsWithVowel函數過濾了它,重複了10次。

測試代碼:

open System.Text.RegularExpressions 

let startsWithVowel1 = 
    let vowels = ['a';'e';'i';'o';'u'] 
    fun (s:string) -> vowels |> List.exists (fun v -> s.[0] = v) 

let startsWithVowel2 = 
    let vowels = ['a';'e';'i';'o';'u'] |> Set.ofList 
    fun (s:string) -> Set.contains s.[0] vowels 

let startsWithVowel3 (s:string) = "aeiou".IndexOf(s.[0]) >= 0 

let startsWithVowel4 str = Regex.IsMatch(str, "^[aeiou]") 

let startsWithVowel5 = 
    let rex = new Regex("^[aeiou]",RegexOptions.Compiled) 
    fun (s:string) -> rex.IsMatch(s) 

let startsWithVowel6 (s:string) = 
    match s.[0] with 
    | 'a' | 'e' | 'i' | 'o' | 'u' -> true 
    | _ -> false 

//5x10^5 random words 
let gibberish = 
    let R = new System.Random() 
    let (word:byte[]) = Array.zeroCreate 5 
    [for _ in 1..500000 -> 
     new string ([|for _ in 3..R.Next(4)+3 -> char (R.Next(26)+97)|]) 
    ] 

//f = startsWithVowelX, use #time in F# interactive for the timing 
let test f = 
    for _ in 1..10 do 
     gibberish |> List.filter f |> ignore 

我卑微的結論: 編輯: 的 勢在必行的IndexOf F#模式匹配贏得速度的較量。

Set.contains方法贏得選美比賽。

+1

+1非常有趣 – Peter 2009-11-24 17:38:30

+0

IMO,模式匹配也贏得選美比賽。 – Daniel 2011-06-01 16:03:36

0
let startsWithVowel (word:string) = 
    let vowels = ['a';'e';'i';'o';'u'] 
    List.exists (fun v -> v = word.[0]) vowels