I'm converting an array to a record type. Something like:
let value = [|"1";"2";"3";"Not a number";"5"|]
type ValueRecord = {
One: int32
Two: int32
Three: int32
Four: string
Five: int32 }
let convertArrayToRecord (x: string array) =
{ One = x.[0] |> Int.Parse
Two = x.[1] |> Int.Parse
Three = x.[2] |> Int.Parse
Four = x.[3]
Five = x.[4] |> Int.Parse }
let recordValue = convertArrayToRecord value
This works, but has the drawback that adding a value to the middle of the array results in manual editing of all index references thereafter like this:
let value = [|"1";"Not a number - 6";"2";"3";"Not a number";"5"|]
type ValueRecord = {
One: int32
Six: string
Two: int32
Three: int32
Four: string
Five: int32 }
let convertArrayToRecord (x: string array) =
{ One = x.[0] |> Int.Parse
Six = x.[1]
Two = x.[2] |> Int.Parse //<--updated index
Three = x.[3] |> Int.Parse //<--updated index
Four = x.[4] //<--updated index
Five = x.[5] |> Int.Parse } //<--updated index
let recordValue = convertArrayToRecord value
Additionally, its easy to accidentally get the indexes wrong.
The solution I came up with is:
let convertArrayToRecord (x: string array) =
let index = ref 0
let getIndex () =
let result = !index
index := result + 1
result
{ One = x.[getIndex ()] |> Int.Parse
Six = x.[getIndex ()]
Two = x.[getIndex ()] |> Int.Parse
Three = x.[getIndex ()] |> Int.Parse
Four = x.[getIndex ()]
Five = x.[getIndex ()] |> Int.Parse }
This works, but I really dislike the ref cell for something which isn't concurrent. Is there a better/cleaner way to accomplish this?
You could use pattern matching.
let convertArrayToRecord = function
| [|one; two; three; four; five|] ->
{
One = int one
Two = int two
Three = int three
Four = four
Five = int five
}
| _ ->
failwith "How do you want to deal with arrays of a different length"
When adding another entry to the array you'd adjust it by editing the first match to [|one; six; two; three; four; five|].
By the way, for a mutable index like the one you're using in your current example, you can avoid ref by using the mutable keyword instead, like so;
let mutable index = -1
let getIndex =
index <- index + 1
index
And if we hide the mutable inside the getIndex function
let getIndex =
let mutable index = -1
fun () ->
index <- index + 1
index
You could let the indexes be handled with pattern matching, and add an active pattern, like this:
let (|PInt32|_|) (s:string) =
let ok, i = Int32.TryParse(s)
if ok then Some(PInt32(s)) else None
let foo() =
match [|"1"; "2"; "Some str"|] with
| [|PInt32(x); PInt32(y); mystr|] ->
printfn "Yup"
| _ -> printfn "Nope"
Related
I'm working on a single case discriminated union with a module to create instances of the type and return a Result of either Ok if the input was valid or Error otherwise. Here is what I have so far.
type ErrorMessage = string
type NonNegativeInt = private NonNegativeInt of int
module NonNegativeInt =
let create (inputInt:int) : Result<NonNegativeInt, ErrorMessage> =
if inputInt >= 0 then
Ok (NonNegativeInt inputInt)
else
Error ("inputInt must be >= 0")
let value (NonNegativeInt intVal) = intVal
I would like to add an integer to an instance of this type using the create function so it will block negatives. I've got the first test working this way.
[<Fact>]
member this.``NonNegativeInt test`` () =
let nonNegativeResult = NonNegativeInt.create 5
let newNonNegativeResult = match nonNegativeResult with
| Ok result ->
let intVal = NonNegativeInt.value result
let newIntVal = intVal + 1
NonNegativeInt.create newIntVal
| Error _ ->
nonNegativeResult
match newNonNegativeResult with
| Ok result ->
Assert.Equal(6, NonNegativeInt.value result)
| Error _ ->
Assert.Fail("Error creating new NonNegativeInt")
This is pretty much unusable this way. Is there a more concise way to accomplish this task without all the unwrapping, wrapping, and pattern matching? Is Result.bind the way to go?
Update 1 Trying Result.bind
This is a better, but still feels a bit clumsy. Maybe the NonNegativeInt module needs another function besides create and value to make this easier.
[<Fact>]
member this.``NonNegativeInt test2`` () =
let nni1 = NonNegativeInt.create 5
let nni2 = nni1
|> Result.bind (fun x -> NonNegativeInt.create ((NonNegativeInt.value x) + 1))
let expectedResult = NonNegativeInt.create 6
Assert.Equal(expectedResult, nni2)
Suggestion 1
You could use a computation builder to make the code cleaner:
type ResultBuilder() =
member _.Return(x) = Ok x
member _.ReturnFrom(res : Result<_, _>) = res
member _.Bind(res, f) = Result.bind f res
let result = ResultBuilder()
Then your example becomes:
let test () =
result {
let! nni = NonNegativeInt.create 5
return! NonNegativeInt.create (nni.Value + 1)
}
test () |> printfn "%A" // Ok NonNegativeInt 6
I also added a member to make it easier to access a NonNegativeInteger's value:
type NonNegativeInt =
private NonNegativeInt of int
with
member this.Value =
let (NonNegativeInt n) = this in n
Suggestion 2
Having an NNI type and then wrapping it in a Result is like wearing both a belt and suspenders. To simplify things further, you could get rid of the NNI type entirely, and just keep the validation logic:
module NonNegativeInt =
let create (inputInt:int) : Result<int, ErrorMessage> =
if inputInt >= 0 then
Ok inputInt
else
Error ("inputInt must be >= 0")
let test () =
result {
let! n = NonNegativeInt.create 5
return! NonNegativeInt.create (n + 1)
}
test () |> printfn "%A" // Ok 6
Suggestion 3
Alternatively, you could keep the NNI type and trust the caller to use it with valid values (without wrapping in a Result). This is what FsCheck does, for example:
///Represents an int >= 0
type NonNegativeInt = NonNegativeInt of int with
member x.Get = match x with NonNegativeInt r -> r
override x.ToString() = x.Get.ToString()
static member op_Explicit(NonNegativeInt i) = i
I am attempting to generate a series of guesses for the second Taxicab number. What I want to do is is call the Attempt function on a series of integers in a finite sequence. I have my two questions about implementation in the comments.
A taxi cab number, in case your wondering, is the least number that satisfied the sum of 2 unique cubes in for n unique sets of 2 unique cubes. Ta(2) is 1729.
[<EntryPoint>]
let main argv =
let Attempt (start : int) =
let stop = start+20
let integerList = [start..stop]
let list = List.init 3 (fun x -> integerList.[x])
//Is there a simple way to make initialize the list with random indices of integerList?
let Cube x = x*x*x
let newlist = list |> List.map (fun x -> Cube x)
let partitionList (x : List<int>) (y : int) = List.sum [x.[y];x.[y+1]]
let intLIST = [0..2]
let partitionList' = [for i in intLIST do yield partitionList newlist i]
let x = Set.ofList partitionList'
let y = Set.ofList partitionList'
//I was going to try to use some kind of equality operator to determine whether the two sets were equal, which could tell me whether we had actually found a Taxicab number by the weakened definition.
System.Console.Write(list)
System.Console.Write(newlist)
let rnd = System.Random()
//My primary question is how can I convert a random to an integer to use in start for the function Attempt?
System.Console.ReadKey() |> ignore
printfn("%A") argv
0
Dirty way to initialize list with random indexes of another list:
let randomIndexes count myList =
let rand = System.Random()
seq {
for n = 1 to count do
yield rand.Next(List.length myList) }
|> Seq.distinct
//|> Seq.sort // if you need them sorted
|> List.ofSeq
let result = randomIndexes 5 [3;2;4;5]
printfn "%A" result
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
I am new to programming and F# is my first language.
Here is my code:
let areAnagrams (firstString: string) (secondString: string) =
let countCharacters (someString: string) =
someString.ToLower().ToCharArray() |> Array.toSeq
|> Seq.countBy (fun eachChar -> eachChar)
|> Seq.sortBy (snd >> (~-))
countCharacters firstString = countCharacters secondString
let testString1 = "Laity"
let testString2 = "Italy"
printfn "It is %b that %s and %s are anagrams." (areAnagrams testString1 testString2) (testString1) (testString2)
This is the output:
It is false that Laity and Italy are anagrams.
What went wrong? What changes should I make?
Your implementation of countCharacters sorts the tuples just using the second element (the number of occurrences for each character), but if there are multiple characters that appear the same number of times, then the order is not defined.
If you run the countCharacters function on your two samples, you can see the problem:
> countCharacters "Laity";;
val it : seq<char * int> = seq [('l', 1); ('a', 1); ('i', 1); ('t', 1); ...]
> countCharacters "Italy";;
val it : seq<char * int> = seq [('i', 1); ('t', 1); ('a', 1); ('l', 1); ...]
One solution is to just use Seq.sort and sort the tuples using both the letter code and the number of occurrences.
The other problem is that you are comparing two seq<_> values and this does not use structural comparison, so you'll need to turn the result into a list or an array (something that is fully evaluated):
let countCharacters (someString: string) =
someString.ToLower().ToCharArray()
|> Seq.countBy (fun eachChar -> eachChar)
|> Seq.sort
|> List.ofSeq
Note that you do not actually need Seq.countBy - because if you just sort all the characters, it will work equally well (the repeated characters will just be one after another). So you could use just:
let countCharacters (someString: string) =
someString.ToLower() |> Seq.sort |> List.ofSeq
Sorting the characters of the two strings gives you an easy solution but this could be a good example of recursion.
You can immediately exclude strings of different length.
You can also filter out all the occurrences of a char per iteration, by replacing them with an empty string.
let rec areAnagram (x:string) (y:string) =
if x.Lenght <> t.Lenght
then false else
if x.Lenght = 0
then true else
let reply = x.[0].ToString ()
areAnagram
(x.Replace (reply,""))
(y.Replace (reply,""))
The above should be faster than sorting for many use cases.
Anyway we can go further and transform it into a fast Integer Sorting without recursion and string replacements
let inline charToInt c =
int c - int '0'
let singlePassAnagram (x:string) =
let hash : int array = Array.zeroCreate 100
x |> Seq.iter (fun c->
hash.[charToInt c] <- (hash.[charToInt c]+1)
)
let areAnagramsFast
(x:string) (y:string) =
if x.Length <> y.Length
then false else
(singlePassAnagram x) =
(singlePassAnagram y)
Here is a fiddle
I am trying to take a numeric array in F#, and rank all the elements so that ties get the same rank. Basically I'm trying to replicate the algorithm I have below in C#, but just for an array of doubles. Help?
rankMatchNum = 0;
rankMatchSum = 0;
previousScore = -999999999;
for (int i = 0; i < factorStocks.Count; i++)
{
//The 1st time through it won't ever match the previous score...
if (factorStocks[i].factors[factorName + "_R"] == previousScore)
{
rankMatchNum = rankMatchNum + 1; //The count of matching ranks
rankMatchSum = rankMatchSum + i + 1; //The rank itself...
for (int j = 0; j <= rankMatchNum; j++)
{
factorStocks[i - j].factors[factorName + "_WR"] = rankMatchSum / (rankMatchNum + 1);
}
}
else
{
rankMatchNum = 0;
rankMatchSum = i + 1;
previousScore = factorStocks[i].factors[factorName + "_R"];
factorStocks[i].factors[factorName + "_WR"] = i + 1;
}
}
Here's how I would do it, although this isn't a direct translation of your code. I've done things in a functional style, piping results from one transformation to another.
let rank seq =
seq
|> Seq.countBy (fun x -> x) // count repeated numbers
|> Seq.sortBy (fun (k,v) -> k) // order by key
|> Seq.fold (fun (r,l) (_,n) -> // accumulate the number of items seen and the list of grouped average ranks
let r'' = r + n // get the rank after this group is processed
let avg = List.averageBy float [r+1 .. r''] // average ranks for this group
r'', ([for _ in 1 .. n -> avg]) :: l) // add a list with avg repeated
(0,[]) // seed the fold with rank 0 and an empty list
|> snd // get the final list component, ignoring the component storing the final rank
|> List.rev // reverse the list
|> List.collect (fun l -> l) // merge individual lists into final list
Or to copy Mehrdad's style:
let rank arr =
let lt item = arr |> Seq.filter (fun x -> x < item) |> Seq.length
let lte item = arr |> Seq.filter (fun x -> x <= item) |> Seq.length
let avgR item = [(lt item) + 1 .. (lte item)] |> List.averageBy float
Seq.map avgR arr
I think that you'll probably find this problem far easier to solve in F# if you rewrite the above in a declarative manner rather than in an imperative manner. Here's my off-the-top-of-my-head approach to rewriting the above declaratively:
First we need a wrapper class to decorate our items with a property carrying the rank.
class Ranked<T> {
public T Value { get; private set; }
public double Rank { get; private set; }
public Ranked(T value, double rank) {
this.Value = value;
this.Rank = rank;
}
}
Here, then, is your algorithm in a declarative manner. Note that elements is your input sequence and the resulting sequence is in the same order as elements. The delegate func is the value that you want to rank elements by.
static class IEnumerableExtensions {
public static IEnumerable<Ranked<T>> Rank<T, TRank>(
this IEnumerable<T> elements,
Func<T, TRank> func
) {
var groups = elements.GroupBy(x => func(x));
var ranks = groups.OrderBy(g => g.Key)
.Aggregate(
(IEnumerable<double>)new List<double>(),
(x, g) =>
x.Concat(
Enumerable.Repeat(
Enumerable.Range(x.Count() + 1, g.Count()).Sum() / (double)g.Count(),
g.Count()
)
)
)
.GroupBy(r => r)
.Select(r => r.Key)
.ToArray();
var dict = groups.Select((g, i) => new { g.Key, Index = i })
.ToDictionary(x => x.Key, x => ranks[x.Index]);
foreach (T element in elements) {
yield return new Ranked<T>(element, dict[func(element)]);
}
}
}
Usage:
class MyClass {
public double Score { get; private set; }
public MyClass(double score) { this.Score = score; }
}
List<MyClass> list = new List<MyClass>() {
new MyClass(1.414),
new MyClass(2.718),
new MyClass(2.718),
new MyClass(2.718),
new MyClass(1.414),
new MyClass(3.141),
new MyClass(3.141),
new MyClass(3.141),
new MyClass(1.618)
};
foreach(var item in list.Rank(x => x.Score)) {
Console.WriteLine("Score = {0}, Rank = {1}", item.Value.Score, item.Rank);
}
Output:
Score = 1.414, Rank = 1.5
Score = 2.718, Rank = 3
Score = 2.718, Rank = 3
Score = 2.718, Rank = 3
Score = 1.414, Rank = 1.5
Score = 3.141, Rank = 5
Score = 3.141, Rank = 5
Score = 3.141, Rank = 5
Score = 1.618, Rank = 8
Note that I do not require the input sequence to be ordered. The resulting code is simpler if you enforce such a requirement on the input sequence. Note further that we do not mutate the input sequence, nor do we mutate the input items. This makes F# happy.
From here you should be able to rewrite this in F# easily.
This is not a very efficient algorithm (O(n2)), but it's quite short and readable:
let percentile arr =
let rank item = ((arr |> Seq.filter (fun i -> i < item)
|> Seq.length |> float) + 1.0)
/ float (Array.length arr) * 100.0
Array.map rank arr
You might mess with the expression fun i -> i < e (or the + 1.0 expression) to achieve your desired way of ranking results:
let arr = [|1.0;2.0;2.0;4.0;3.0;3.0|]
percentile arr |> print_any;;
[|16.66666667; 33.33333333; 33.33333333; 100.0; 66.66666667; 66.66666667|]
Mehrdad's solution is very nice but a bit slow for my purposes. The initial sorting can be done 1 time. Rather than traversing the lists each time to get the number of items < or <= the target, we can use counters. This is more imperative (could have used a fold):
let GetRanks2 ( arr ) =
let tupleList = arr |> Seq.countBy( fun x -> x ) |> Seq.sortBy( fun (x,count) -> x )
let map = new System.Collections.Generic.Dictionary<int,float>()
let mutable index = 1
for (item, count) in tupleList do
let c = count
let avgRank =
let mutable s = 0
for i = index to index + c - 1 do
s <- s + i
float s / float c
map.Add( item, avgRank )
index <- index + c
//
map