How to Generate Fuchu Unit testCases in for loop - f#

I'm trying to generate random unit tests in F# with Fuchu.
I'm executing my code in Vs 2017 with a normal Console App.
The issue is that GeneraterandomTests will not compile.
open Fuchu
let OpenOrSenior xs =
[for (x:List<int>) in xs do
if x.[0] >= 55 && x.[1] > 7 then yield "Senior" else yield "Open"]
let rand = new Random()
type ClubMember = {Age:int;Handi:int}
let values = [
{Age = 10;Handi=2}
{Age=25;Handi=9}
{Age=75;Handi=2}
{Age=20;Handi=25}
{Age=1;Handi=1}
{Age=90;Handi=8}
{Age=79;Handi=12}
{Age=99;Handi=18}
{Age=75;Handi=8}
{Age=76;Handi=9}
]
let SelectMember (mem:ClubMember) =
match mem with
| x when (mem.Age >= 55) && (mem.Handi > 7) -> "Senior"
| _ -> "Open"
let PutAttributesInList (mem:ClubMember) =
match mem with
| _ -> [mem.Age; mem.Handi]
let Randomtests expected actual = testCase "Randomness" <|
fun _ -> Assert.Equal("expected == actual?", OpenOrSenior expected, actual)
[<Tests>]
let GeneraterandomTests =
for i in 1..10 do
let mutable expected:int list list = []
let mutable actual: string list = []
for _i in 1..5 do
let mutable rndNumb = rand.Next(0,9)
actual <- SelectMember values.[rndNumb] :: actual
expected <- PutAttributesInList values.[rndNumb] :: expected
Randomtests expected actual

This works in Visual studio 2017 and compiles and tests, it's with Fuchu
// random test cases...
open Fuchu
open System
let rand = new Random()
type ClubMember = {Age:int;Handi:int}
let values = [
{Age = 10;Handi=2}
{Age=25;Handi=9}
{Age=75;Handi=2}
{Age=20;Handi=25}
{Age=1;Handi=1}
{Age=90;Handi=8}
{Age=79;Handi=12}
{Age=99;Handi=18}
{Age=75;Handi=8}
{Age=76;Handi=9}
]
let SelectMember (mem:ClubMember) =
match mem with
| x when (mem.Age >= 55) && (mem.Handi > 7) -> "Senior"
| _ -> "Open"
let PutAttributesInList (mem:ClubMember) =
match mem with
| _ -> [mem.Age; mem.Handi]
let Randomtests expected actual = Assert.Equal("expected == actual?", OpenOrSenior expected, actual)
type _testcases = {_expected : int list list ; _actual : string list}
let mutable testCasesList = []
let GeneraterandomTests =
for i in 1..10 do
let mutable expected:int list list = []
let mutable actual: string list = []
for _i in 1..5 do
let mutable rndNumb = rand.Next(0,9)
actual <- SelectMember values.[rndNumb] :: actual
expected <- PutAttributesInList values.[rndNumb] :: expected
testCasesList <- {_expected = expected; _actual = actual} :: testCasesList
[<Tests>]
let __suite =
testList "Random Tests!" [testCase "Should return test results.." <| (fun _ ->
for i in testCasesList do
Randomtests i._expected i._actual)
]

Related

How can I interpret property based test code?

How can I interpret property based test code?
I'm struggling to translate the instructions on the following snippet:
let myProperty = Prop.forAll fiveAndThrees <| fun number ->
let actual = transform number
let expected = "jbdhjsdhjdsjhsdglsdjlljh"
expected = actual
Check.QuickThrowOnFailure myProperty
Specifically, I'm struggling with the backwards pipeline operator (i.e. "<|").
Here's the test:
[<Fact>]
let ``FizzBuzz.transform returns FizzBuzz`` () =
let fiveAndThrees = Arb.generate<int> |> Gen.map ((*) (3 * 5))
|> Arb.fromGen
let myProperty = Prop.forAll fiveAndThrees <| fun number ->
let actual = transform number
let expected = "jbdhjsdhjdsjhsdglsdjlljh"
expected = actual
Check.QuickThrowOnFailure myProperty
Can someone please guide me step by step on how this code works?
Could this be rewritten using the forward pipe operator (i.e. "|>")?
This answer only covers why <| is used instead of |>.
Here are 5 examples that work toward making use of <| with a large function. The 6th example is to show how the code looks using |> instead of <|. The point is that with the 6th example using |> you have to look into the code to find the primary function funThatNeedsListAndFunc but with <| the primary function funThatNeedsListAndFunc is obvious. So you typically see <| when the last parameter is a function and you want to pass in the function after the primary function for easier comprehension. That's all; don't read more into than that.
After reading the Mark's blog I also learned that <| is useful to remove ( ) around fun. An example using ( ) is given as example 7.
let funThatNeedsListAndFunc list func =
func list
let func = List.sum
let list = Seq.toList { 0 .. 5}
let result1 = funThatNeedsListAndFunc list func
printfn "result1: %A" result1
let result2 = funThatNeedsListAndFunc list <| func
printfn "result2: %A" result2
let result3 = funThatNeedsListAndFunc list <| List.sum
printfn "result3: %A" result3
let result4 = funThatNeedsListAndFunc list <|
fun (list : 'a list) -> List.sum list
printfn "result4: %A" result4
let result5 = funThatNeedsListAndFunc list <|
fun (list : 'a list) ->
// This will be a long function
let a = 1
let b = 2
let c = a * b
let result = List.sum list
let d = "more useless lines"
let (e,f,g) = ("a", 15, 3.0)
result
printfn "result5: %A" result5
.
let result6 =
fun (list : 'a list) ->
// This will be a long function
let a = 1
let b = 2
let c = a * b
let result = List.sum list
let d = "more useless lines"
let (e,f,g) = ("a", 15, 3.0)
result
|> funThatNeedsListAndFunc list
printfn "result6: %A" result6
.
let result7 =
funThatNeedsListAndFunc list (fun (list : 'a list) ->
// This will be a long function
let a = 1
let b = 2
let c = a * b
let result = List.sum list
let d = "more useless lines"
let (e,f,g) = ("a", 15, 3.0)
result)
printfn "result7: %A" result7

Return a list from recursive function

I'm trying to return a list from a function, but I'm getting an error that says that an unit was expected instead. Also, I would like to know if this code appears to be structured correctly in general.
code:
let rec calculateVariants (attList: NewProductAttributeInfo list) (activeCount: int)
(currentList: (int * NewProductAttributeInfo) list) =
// group attribute list by category id
let attGrouped = attList |> List.groupBy (fun x -> x.AttributeCategoryId)
// define mutable list
let mutable stageList = currentList
// begin iteration
for catId,details in attGrouped do
for d in details do
if activeCount = 0
then stageList <- (activeCount,d) :: stageList
let groupLength = attGrouped.Length
if (activeCount + 1) <= groupLength
then
let selectCat,selectDetails = attGrouped.[activeCount + 1]
selectDetails
|> List.filter (fun x ->
stageList
|> List.exists (fun (x') ->
not(x' = (activeCount,x))))
|> (fun x ->
match x with
| [] -> ()
| head :: tail ->
stageList <- (activeCount, head) :: stageList
let currentCategory = activeCount + 1
calculateVariants attList currentCategory stageList
)
stageList // <-- error Unit expected
if .. then .. else should return the same type on both branches. If you omit else branch then compiler assuming that it returns unit. Add else branch returning list.
Edit:
Given your problem description, the easiest way would be something like this:
type NewProductAttributeInfo = {AttributeCategoryId: string; AttributeId: string}
let products = [ { AttributeCategoryId = "Size"; AttributeId = "S"};
{ AttributeCategoryId = "Mat"; AttributeId = "Linen" };
{ AttributeCategoryId = "Mat"; AttributeId = "Poliester" };
{ AttributeCategoryId = "Color"; AttributeId = "White" };
{ AttributeCategoryId = "Color"; AttributeId = "Green" };
{ AttributeCategoryId = "Mat"; AttributeId = "Linen" };
{ AttributeCategoryId = "Mat"; AttributeId = "Cotton" };
{ AttributeCategoryId = "Mat"; AttributeId = "Poliester" };
{ AttributeCategoryId = "Size"; AttributeId = "XL" } ]
let group list =
list
|> Set.ofList // Provides uniqueness of attribute combinations
|> Seq.groupBy (fun x -> x.AttributeCategoryId) // Grouping by CatId
|> List.ofSeq
let res = group products
Result:
val it : (string * seq<NewProductAttributeInfo>) list =
[("Color", seq [{AttributeCategoryId = "Color";
AttributeId = "Green";}; {AttributeCategoryId = "Color";
AttributeId "White";}]);
("Mat",
seq
[{AttributeCategoryId = "Mat";
AttributeId = "Cotton";}; {AttributeCategoryId = "Mat";
AttributeId = "Linen";};
{AttributeCategoryId = "Mat";
AttributeId = "Poliester";}]);
("Size", seq [{AttributeCategoryId = "Size";
AttributeId = "S";}; {AttributeCategoryId = "Size";
AttributeId = "XL";}])]
This is the solution that I came with. It works, but I'm sure it can be optimized quite a bit. I have a duplicate issue that is solved with the Set.ofList function externally after this code runs, which I'm still working on.
type NewProductAttributeInfo = {
AttributeId : string;
AttributeCategoryId : string
}
let rec private returnVariant (curIdx: int) (listLength: int)
(attList: (int * NewProductAttributeInfo * NewProductAttributeInfo) list)
(curList: NewProductAttributeInfo list) =
match curList with
| x when x.Length = listLength -> curList
| x ->
let attTup =
attList
|> List.filter (fun x' ->
let idx1,att1,att2' = x'
idx1 >= curIdx && not(curList
|> List.exists (fun x'' ->
x'' = att2'))
)
let idx1,att1,att2 = attTup |> List.head
let newList = curList # [att2]
returnVariant idx1 newList.Length attList newList
let rec calculateVariants (attList: NewProductAttributeInfo list)
(currentList: (int * NewProductAttributeInfo * NewProductAttributeInfo) list) =
// group attribute list by category id
let attGrouped = attList |> List.groupBy (fun x -> x.AttributeCategoryId)
let (firstGroupCatId,firstGroupDetails) = attGrouped.[0]
match currentList with
| [] ->
let rawVariants = [for nxt in 0 .. (attGrouped.Length - 1) do
if nxt > 0
then
// begin iteration
for d in firstGroupDetails do
let _,det = attGrouped.[nxt]
for det' in det do
yield (nxt, d, det')
]
calculateVariants attList rawVariants
| x ->
let groupLength = x |> List.groupBy (fun (idx,d0,nxtD) -> idx)
|> List.length |> ((+)1)
let sortedGroup = x |> List.sortBy (fun (x,y,z) -> x)
if groupLength > 2
then // below is the block that generates the duplicates
[for att in sortedGroup do
for attCompare in sortedGroup do
let idx1,att1,att2 = att
let idx2,attC1,attC2 = attCompare
if idx2 > idx1 && att2 <> attC2
then
let idString =
returnVariant idx2 groupLength x [att1; att2; attC2]
|> List.map (fun nl -> nl.AttributeId)
yield String.concat "," idString
]
else
[
for att in sortedGroup do
let idx1,att1,att2 = att
let idString =
returnVariant idx1 groupLength x [att1; att2]
|> List.map (fun nl -> nl.AttributeId)
yield String.concat "," idString
]

The mutable variable 'index' is used in an invalid way in seq {}?

In the following code, the compiler gets error on index <- index + 1 with error
Error 3 The mutable variable 'index' is used in an invalid way. Mutable variables cannot be captured by closures. Consider eliminating this use of mutation or using a heap-allocated mutable reference cell via 'ref' and '!'. d:\Users....\Program.fs 11 22 ConsoleApplication2
However, it has been defined as mutable?
let rec iterateTupleMemberTypes (tupleArgTypes: System.Type[]) (columnNames: string[]) startingIndex =
seq {
let mutable index = startingIndex
for t in tupleArgTypes do
match t.IsGenericType with
| true -> iterateTupleMemberTypes (t.GetGenericArguments()) columnNames index |> ignore
| false ->
printfn "Name: %s Type: %A" (columnNames.[index]) t
index <- index + 1
yield (columnNames.[index]), t
} |> Map.ofSeq
let myFile = CsvProvider<"""d:\temp\sample.txt""">.GetSample()
let firstRow = myFile.Rows |> Seq.head
let tupleType = firstRow.GetType()
let tupleArgTypes = tupleType.GetGenericArguments()
let m = iterateTupleMemberTypes tupleArgTypes myFile.Headers.Value 0
An idiomatic version of this might look like the following:
#r #"..\packages\FSharp.Data.2.2.2\lib\net40\FSharp.Data.dll"
open FSharp.Data
open System
type SampleCsv = CsvProvider<"Sample.csv">
let sample = SampleCsv.GetSample()
let rec collectLeaves (typeTree : Type) =
seq {
match typeTree.IsGenericType with
| false -> yield typeTree.Name
| true -> yield! typeTree.GetGenericArguments() |> Seq.collect collectLeaves
}
let columnTypes = (sample.Rows |> Seq.head).GetType() |> collectLeaves
let columnDefinitions = columnTypes |> Seq.zip sample.Headers.Value |> Map.ofSeq
let getDefinitions (sample : SampleCsv) = (sample.Rows |> Seq.head).GetType() |> collectLeaves |> Seq.zip sample.Headers.Value |> Map.ofSeq
Personally, I wouldn't be concerned too much about the performance of Map vs Dictionary (and rather have the immutable Map) unless there are hundreds of columns.
The statement after it, let index = 0, shadows your definition of mutable variable index. Also, to make mutables work in sequences, you need refs. https://msdn.microsoft.com/en-us/library/dd233186.aspx
Suggested by #Ming-Tang, I changed the mutable variable to ref and it works now. However, is it a way not to use mutable/ref variable at all?
let rec iterateTupleMemberTypes (tupleArgTypes: System.Type[]) (columnNames: string[]) startingIndex =
seq {
let index = ref startingIndex
for t in tupleArgTypes do
match t.IsGenericType with
| true ->
yield! iterateTupleMemberTypes (t.GetGenericArguments()) columnNames !index
| false ->
printfn "Name: %s Type: %A" (columnNames.[!index]) t
yield (columnNames.[!index]), t
index := !index + 1
} |> dict
let myFile = CsvProvider<"""d:\temp\sample.txt""">.GetSample()
let firstRow = myFile.Rows |> Seq.head
let tupleType = firstRow.GetType()
let tupleArgTypes = tupleType.GetGenericArguments()
let m = iterateTupleMemberTypes tupleArgTypes myFile.Headers.Value 0

How to execute a function, that creates a lot of objects, in parallel?

I am using Array.Parallel.map on a function but find that it is not executing at anywhere near full processor capacity. I am assuming this is because the function creates a lot of objects when running List.map and List.map2. Would this be causing a synchronization issue and is there a more appropriate way of doing this? At the moment the only way I can think of getting around this is by running each process as a separate executable using something like xargs under Linux.
I put together the script below to demonstrate the problem. It is a very basic data categorizer which relies on a field having a certain value as a rule to determine if this will predict a category:
open System
type CategoryAssessment =
{ fieldIndex: int
value: int
ruleAssessments: list<int> }
let InitAssessment categorizeFields rules =
let ruleAssessments = List.init (List.length rules) (fun x -> 0)
List.map (fun categorizeField ->
let fieldIndex, categoryValue = categorizeField
{ CategoryAssessment.fieldIndex = fieldIndex;
value = categoryValue;
ruleAssessments = ruleAssessments })
categorizeFields
let AssessCategory ruleMatches (row : int[]) categoryAssessment =
let fieldIndex = categoryAssessment.fieldIndex
let categoryValue = categoryAssessment.value
let categoryMatch = categoryValue = row.[fieldIndex]
let newRuleAssessments =
List.map2 (fun ruleAssessment ruleMatch ->
if ruleMatch = categoryMatch then
ruleAssessment + 1
else
ruleAssessment)
categoryAssessment.ruleAssessments
ruleMatches
{ categoryAssessment with ruleAssessments = newRuleAssessments }
let MatchRule (row : int[]) rule =
let fieldIndex, eqVal = rule
row.[fieldIndex] = eqVal
let Assess categorizeFields rules input =
printfn "START - Assess"
let d =
Array.fold (fun categoryAssessment row ->
let ruleMatches = List.map (MatchRule row) rules
List.map (AssessCategory ruleMatches row) categoryAssessment)
(InitAssessment categorizeFields rules)
input
printfn "END - Assess"
d
let JoinAssessments assessments =
let numAssessments = Array.length assessments
Array.fold (fun accAssessment assessment ->
List.map2 (fun accCategory category ->
let newRuleAssessments =
List.map2 (+)
accCategory.ruleAssessments
category.ruleAssessments
{ accCategory with
ruleAssessments = newRuleAssessments })
accAssessment
assessment)
assessments.[0]
assessments.[1..(numAssessments-1)]
let numRecords = 10000
let numFields = 20
let numSplits = 10
let numRules = 10000
let inputs = Array.create numSplits
[| for i in 1 .. (numRecords / numSplits) ->
[| for j in 1 .. numFields ->
(i % 10) + j |] |]
let categorizeFields = [ (1, 6); (2, 3); (2, 4); (3, 2) ]
let rules = [ for i in 1 .. numRules -> (i % numFields, i) ]
let assessments =
Array.Parallel.map (Assess categorizeFields rules) inputs
|> JoinAssessments
printfn "Assessments: %A" assessments
0
After a fair bit of investigation, the ultimate answer to my question seems to be to find a way of not creating lots of objects. The easiest change to do this is moving to using arrays instead of lists. I have written up my findings more fully in an article: Beware of Immutable Lists for F# Parallel Processing.
The above program when altered as follows, runs better between threads and runs much quicker even on a single thread. Further improvements can be made by making the ruleAssessments field mutable as demonstrated in the referenced article.
open System
type CategoryAssessment =
{ fieldIndex: int
value: int
ruleAssessments: int[] }
let InitAssessment categorizeFields rules =
let ruleAssessments = Array.create (Array.length rules) 0
Array.map (fun categorizeField ->
let fieldIndex, categoryValue = categorizeField
{ CategoryAssessment.fieldIndex = fieldIndex;
value = categoryValue;
ruleAssessments = ruleAssessments })
categorizeFields
let AssessCategory ruleMatches (row : int[]) categoryAssessment =
let fieldIndex = categoryAssessment.fieldIndex
let categoryValue = categoryAssessment.value
let categoryMatch = categoryValue = row.[fieldIndex]
let newRuleAssessments =
Array.map2 (fun ruleAssessment ruleMatch ->
if ruleMatch = categoryMatch then
ruleAssessment + 1
else
ruleAssessment)
categoryAssessment.ruleAssessments
ruleMatches
{ categoryAssessment with ruleAssessments = newRuleAssessments }
let MatchRule (row : int[]) rule =
let fieldIndex, eqVal = rule
row.[fieldIndex] = eqVal
let Assess categorizeFields rules input =
printfn "START - Assess"
let d =
Array.fold (fun categoryAssessment row ->
let ruleMatches = Array.map (MatchRule row) rules
Array.map (AssessCategory ruleMatches row) categoryAssessment)
(InitAssessment categorizeFields rules)
input
printfn "END - Assess"
d
let JoinAssessments assessments =
let numAssessments = Array.length assessments
Array.fold (fun accAssessment assessment ->
Array.map2 (fun accCategory category ->
let newRuleAssessments =
Array.map2 (+)
accCategory.ruleAssessments
category.ruleAssessments
{ accCategory with
ruleAssessments = newRuleAssessments })
accAssessment
assessment)
assessments.[0]
assessments.[1..(numAssessments-1)]
let numRecords = 10000
let numFields = 20
let numSplits = 10
let numRules = 10000
let inputs = Array.create numSplits
[| for i in 1 .. (numRecords / numSplits) ->
[| for j in 1 .. numFields ->
(i % 10) + j |] |]
let categorizeFields = [| (1, 6); (2, 3); (2, 4); (3, 2) |]
let rules = [| for i in 1 .. numRules -> (i % numFields, i) |]
let assessments =
Array.Parallel.map (Assess categorizeFields rules) inputs
|> JoinAssessments
printfn "Assessments: %A" assessments
0
This is a version of your program that doesn't require mutability and uses nearly all of the 4 cpus on my iMac.
To pull it off, it's driven by assessing each rule in parallel, not by processing records. That also required the input array to be transposed making it be fields by records.
open System
type CategoryAssessment =
{ fieldIndex: int
value: int
ruleAssessments: list<int> }
let MatchRule rVal fVal =
rVal = fVal
let AssessRule cMatches (inputs:int[][]) (rIndex, rVal) =
// printfn "START - Assess" // uses more cpu than the code itself
let matches = inputs.[rIndex] |>
Array.map2 (fun cVal fVal -> (MatchRule rVal fVal) = cVal) cMatches
let assessment = matches |>
Array.map ( fun v -> if v then 1 else 0 ) |>
Array.sum
// printfn "END - Assess"
assessment
let Assess categorizeFields rules (inputs:int[][]) =
categorizeFields |> List.map (fun (catIndex, catValue) ->
let catMatches = inputs.[catIndex] |> Array.map( fun v -> v = catValue )
let assessments = rules |> Array.Parallel.map
(AssessRule catMatches inputs)
|> Array.toList
{ CategoryAssessment.fieldIndex = catIndex;
value = catValue;
ruleAssessments = assessments }
)
let numRecords = 10000
let numFields = 20
let numRules = 10000
let inputs = [| for j in 1 .. numFields ->
[| for i in 1 .. numRecords -> (i % 10) + j |] |]
let categorizeFields = [ (1, 6); (2, 3); (2, 4); (3, 2) ]
let rules = [| for i in 1 .. numRules -> (i % numFields, i) |]
let assessments = Assess categorizeFields rules inputs
printfn "Assessments: %A" assessments
Assessing by rule allowed the summing of a single integer across all records for a given rule, avoiding mutable state and extra memory allocations.
I used a lot of array iteration to get the speed up but didn't remove all the lists.
I fear I changed the functionality while refactoring or made assumptions that can't be applied to your actual problem, however I do hope it's a useful example.

function 'startsWithVowel' in F#

Given a list of vowels, I have written the function startsWithVowel to investigate if a word starts with a vowel. As you can see I use exception as controlflow, and that's not ideal. How to implement this better?
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
UPDATE : tx to all : once again I experience : never hesitate to ask a newbee question. I see a lot of very useful remarks, keep them coming :-)
try using the exists method instead
let vowel = ['a'; 'e'; 'i'; 'o'; 'u']
let startsWithVowel(str :string) = List.exists (fun x -> x = str.[0]) vowel
exists returns true if any element in the list returns true for the predicate and false otherwise.
Use sets for efficient lookup
let vowels = Set.ofList ['a'; 'e'; 'i'; 'o'; 'u']
let startsWithVowel(str : string) = vowels |> Set.mem (str.[0])
Yet another alternative, tryFindIndex returns Some or None rather than throwing an exception:
> 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
I benchmarked a few approaches mentioned in this thread (Edit: added nr. 6).
The List.exists approach (~0.75 seconds)
The Set.contains approach (~0.51 seconds)
String.IndexOf (~0.25 seconds)
A non-compiled regex (~5 - 6 seconds)
A compiled regex (~1.0 seconds)
Pattern matching (why did I forget this the first time?) (~0.17 seconds)
I filled a list with 500000 random words and filtered it through various startsWithVowel functions, repeated 10 times.
Test code:
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
My humble conclusion:
EDIT:
The imperative IndexOf F# pattern match wins the speed contest.
The Set.contains approach wins the beauty contest.
Note also that a number of exception-throwing functions have non-exception equivalents that return option rather than throwing - these typically have a 'try' prefix in the function name.
List.tryFindIndex:
http://msdn.microsoft.com/en-us/library/ee340224(VS.100).aspx
See also
http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!181.entry
Using regular expressions:
open System.Text.RegularExpressions
let startsWithVowel str = Regex.IsMatch(str, "^[AEIOU]", RegexOptions.IgnoreCase)
let startsWithVowel (word:string) =
let vowels = ['a';'e';'i';'o';'u']
List.exists (fun v -> v = word.[0]) vowels

Resources