I have two sequences that I would like to combine somehow as I need the result of the second one printed right next to the first. The code is currently where playerItems refers to a list:
seq state.player.playerItems
|> Seq.map (fun i -> i.name)
|> Seq.iter (printfn "You have a %s")
seq state.player.playerItems
|> Seq.map (fun i -> i.description) |> Seq.iter (printfn "Description = %s")
The result currently is
You have a Keycard
You have a Hammer
You have a Wrench
You have a Screw
Description = Swipe to enter
Description = Thump
Description = Grab, Twist, Let go, Repeat
Description = Twisty poke
However, I need it to be
You have a Keycard
Description = Swipe to enter
You have a Hammer
Description = Thump
Any help with this would be very appreciated.
As Foggy Finder said in the comments, in your specific case you really don't have two sequences, you have one sequence and you want to print two lines for each item, which can be done with a single Seq.iter like this:
state.player.playerItems // The "seq" beforehand is not necessary
|> Seq.iter (fun player -> printfn "You have a %s\nDescription = %s" player.name player.description)
However, I'll also tell you about two ways to combine two sequences, for the time when you really do have two different sequences. First, if you want to turn the two sequences into a sequence of tuples, you'd use Seq.zip:
let colors = Seq.ofList ["red"; "green"; "blue"]
let numbers = Seq.ofList [25; 73; 42]
let pairs = Seq.zip colors numbers
printfn "%A" pairs
// Prints: seq [("red", 25); ("green", 73); ("blue", 42)]
If you want to combine the two sequences in some other way than producing tuples, use Seq.map2 and pass it a two-parameter function:
let colors = Seq.ofList ["red"; "green"; "blue"]
let numbers = Seq.ofList [25; 73; 42]
let combined = Seq.map2 (fun clr num -> sprintf "%s: %d" clr num) colors numbers
printfn "%A" combined
// Prints: seq ["red: 25"; "green: 73"; "blue: 42"]
Finally, if all you want is to perform some side-effect for each pair of items in the two sequences, then Seq.iter2 is your friend:
let colors = Seq.ofList ["red"; "green"; "blue"]
let numbers = Seq.ofList [25; 73; 42]
Seq.iter2 (fun clr num -> printfn "%s: %d" clr num)
That would print the following three lines to the console:
red: 25
green: 73
blue: 42
Note how in the Seq.iter function, I'm not storing the result. That's because the result of Seq.iter is always (), the "unit" value that is F#'s equivalent of void. (Except that it's much more useful than void, for reasons that are beyond the scope of this answer. Search Stack Overflow for "[F#] unit" and you should find some interesting questions and answers, like this one.
Related
I am looking for a type similar to sequences in F# where indices could be big integers, rather that being restricted to int. Does there exist anything like this?
By "big integer indices" I mean a type which allows for something equivalent to that:
let s = Seq.initInfinite (fun i -> i + 10I)
The following will generate an infinite series of bigints:
let s = Seq.initInfinite (fun i -> bigint i + 10I)
What i suspect you actually want though is a Map<'Key, 'Value>.
This lets you efficiently use a bigint as an index to look up whatever value it is you care about:
let map =
seq {
1I, "one"
2I, "two"
3I, "three"
}
|> Map.ofSeq
// val map : Map<System.Numerics.BigInteger,string> =
// map [(1, "one"); (2, "two"); (3, "three")]
map.TryFind 1I |> (printfn "%A") // Some "one"
map.TryFind 4I |> (printfn "%A") // None
The equivalent of initInfinite for BigIntegers would be
let inf = Seq.unfold (fun i -> let n = i + bigint.One in Some(n, n)) bigint.Zero
let biggerThanAnInt = inf |> Seq.skip (Int32.MaxValue) |> Seq.head // 2147483648
which takes ~2 min to run on my machine.
However, I doubt this is of any practical use :-) That is unless you start at some known value > Int32.MaxValue and stop reasonably soon (generating less than Int32.MaxValue items), which then could be solved by offsetting the BigInt indexes into the Int32 domain.
Theoretically you could amend the Seq module with functions working with BigIntegers to skip / window / ... an amount of items > Int32.MaxValue (e.g. by repeatedly performing the corresponding Int32 variant)
Since you want to index into a sequence, I assume you want a version of Seq.item that takes a BigInteger as index. There's nothing like that built into F#, but it's easy to define your own:
open System.Numerics
module Seq =
let itemI (index : BigInteger) source =
source |> Seq.item (int index)
Note that no new type is needed unless you're planning to create sequences that are longer than 2,147,483,647 items, which would probably not be practical anyway.
Usage:
let items = [| "moo"; "baa"; "oink" |]
items
|> Seq.itemI 2I
|> printfn "%A" // output: "oink"
I've made a function to read a .txt file and turn it into a string.
From here I need help with collecting how many times a word is shown.
But I'm not sure where to go from here and any kind of help with any of the bulletpoints would be greatly appreciated.
Let's go through this step by step then, creating a function for each bit:
Convert words starting with an upper-case to a lower-case word so that all words are lower case.
Split the string into a sequence of words:
let getWords (s: string) =
s.Split(' ')
Turns "hello world" into ["hello"; "world"]
Sort the amount of times a word is shown. A word in this sense is a sequence of characters without whitespaces or punctuation (!#= etc)
Part #1: Format a word in lower without punctuation:
let isNotPunctuation c =
not (Char.IsPunctuation(c))
let formatWord (s: string) =
let chars =
s.ToLowerInvariant()
|> Seq.filter isNotPunctuation
|> Seq.toArray
new String(chars)
Turns "Hello!" into "hello".
Part #2: Group the list of words by the formatted version of it.
let groupWords (words: string seq) =
words
|> Seq.groupBy formatWord
This returns a tuple, with the first part as the key (formatWord) the second part is a list of the words.
Turns ["hello"; "world"; "hello"] into
[("hello", ["hello"; "hello"]);
("world", ["world"])]
Sort from most frequent word shown and to less frequent word.
let sortWords group =
group
|> Seq.sortByDescending (fun g -> Seq.length (snd g))
Sort the list descending (biggest first) by the length (count) of items in the second part - see the above representation.
Now we just need to clean up the output:
let output group =
group
|> Seq.map fst
This picks the first part of the tuple from the group:
Turns ("hello", ["hello"; "hello"]) into "hello".
Now we have all the functions, we can stick them together into one chain:
let s = "some long string with some repeated words again and some other words"
let finished =
s
|> getWords
|> groupWords
|> sortWords
|> output
printfn "%A" finished
//seq ["some"; "words"; "long"; "string"; ...]
Here's another way using Regex
open System.Text.RegularExpressions
let str = "Some (very) long string with some repeated words again, and some other words, and some punctuation too."
str
|> (Regex #"\W+").Split
|> Seq.choose(fun s -> if s = "" then None else Some (s.ToLower()))
|> Seq.countBy id
|> Seq.sortByDescending snd
I am trying to filter out values from a sequence, that are not in another sequence. I was pretty sure my code worked, but it is taking a long time to run on my computer and because of this I am not sure, so I am here to see what the community thinks.
Code is below:
let statezip =
StateCsv.GetSample().Rows
|> Seq.map (fun row -> row.State)
|> Seq.distinct
type State = State of string
let unwrapstate (State s) = s
let neededstates (row:StateCsv) = Seq.contains (unwrapstate row.State) statezip
I am filtering by the neededstates function. Is there something wrong with the way I am doing this?
let datafilter =
StateCsv1.GetSample().Rows
|> Seq.map (fun row -> row.State,row.Income,row.Family)
|> Seq.filter neededstates
|> List.ofSeq
I believe that it should filter the sequence by the values that are true, since neededstates function is a bool. StateCsv and StateCsv1 have the same exact structure, although from different years.
Evaluation of contains on sequences and lists can be slow. For a case where you want to check for the existence of an element in a collection, the F# Set type is ideal. You can convert your sequences to sets using Set.ofSeq, and then run the logic over the sets instead. The following example uses the numbers from 1 to 10000 and then uses both sequences and sets to filter the result to only the odd numbers by checking that the values are not in a collection of even numbers.
Using Sequences:
let numberSeq = {0..10000}
let evenNumberSeq = seq { for n in numberSeq do if (n % 2 = 0) then yield n }
#time
numberSeq |> Seq.filter (fun n -> evenNumberSeq |> Seq.contains n |> not) |> Seq.toList
#time
This runs in about 1.9 seconds for me.
Using sets:
let numberSet = numberSeq |> Set.ofSeq
let evenNumberSet = evenNumberSeq |> Set.ofSeq
#time
numberSet |> Set.filter (fun n -> evenNumberSet |> Set.contains n |> not)
#time
This runs in only 0.005 seconds. Hopefully you can materialize your sequences to sets before performing your contains operation, thereby getting this level of speedup.
I'm working on a homework assignment. And I'm trying to learn F# so I don't want any shortcuts besides using basic things like List.Map or lambdas or something.
I'm trying to process a list of tuples, but I'm having trouble accessing the tuples in the list.
I want to take the list of tuples, add up the numbers in each tuple, and return that number, printing it out each time.
let listTup = [(2,3,4); (4,5,6); (6,7,8)]
let getSum (a,b,c) =
a+b+c
let rec printSum tpList =
let total = 0
match tpList with
| [] -> total //return 0 if empty list
| hd::tl ->
print (getSum hd)
The first thing you want to do is map your tuples through the getSum function. This can be done very simply by piping the list of tuples into List.map getSum. Then you want to print each element in the list, so you pipe the result of List.map getSum into List.iter with the function printfn "%d". This works because of the functions having curried parameters. printfn "%d" applies the "%d" parameter to printfn and returns a function taking an integer, which it then prints. The whole thing would look like this:
let listTup = [(2,3,4); (4,5,6); (6,7,8)]
let getSum (a,b,c) =
a + b + c
let printSum tpList =
tpList |> List.map getSum |> List.iter (printfn "%d")
This prints:
9
15
21
We can even simplify the function further if we take advantage of function composition (the >> operator). Notice that printSum takes tpList as its parameter, and then just uses it as input to two functions that are pipelined together. Since pipelining just takes the output of one function and passes it as the last parameter of another function, all we really need to do is compose the function List.map getSum, which takes a list of int 3-tuples and returns a list of ints with List.iter (printfn "%d"), which takes a list of ints and returns unit. That would look like this:
let printSum = List.map getSum >> List.iter (printfn "%d")
This will print the same results, but is a simpler way of expressing the function.
F# has imperative loops as well. In this case I think an imperative loop matches the problem most idiomatically.
let listTup = [(2,3,4); (4,5,6); (6,7,8)]
for a,b,c in listTup do
let sum = a + b + c
printfn "%d" sum
a little rusty from my Scheme days, I'd like to take 2 lists: one of numbers and one of strings, and fold them together into a single string where each pair is written like "{(ushort)5, "bla bla bla"},\n". I have most of it, i'm just not sure how to write the Fold properly:
let splitter = [|","|]
let indexes =
indexStr.Split(splitter, System.StringSplitOptions.None) |> Seq.toList
let values =
valueStr.Split(splitter, System.StringSplitOptions.None) |> Seq.toList
let pairs = List.zip indexes values
printfn "%A" pairs
let result = pairs |> Seq.fold
(fun acc a -> String.Format("{0}, \{(ushort){1}, \"{2}\"\}\n",
acc, (List.nth a 0), (List.nth a 1)))
Your missing two things. The initial state of the fold which is an empty string and you can't use list comprehension on tuples in F#.
let splitter = [|","|]
let indexes =
indexStr.Split(splitter, System.StringSplitOptions.None) |> Seq.toList
let values =
valueStr.Split(splitter, System.StringSplitOptions.None) |> Seq.toList
let pairs = List.zip indexes values
printfn "%A" pairs
let result =
pairs
|> Seq.fold (fun acc (index, value) ->
String.Format("{0}{{(ushort){1}, \"{2}\"}},\n", acc, index, value)) ""
fold2 version
let result =
List.fold2
(fun acc index value ->
String.Format("{0}{{(ushort){1}, \"{2}\"}},\n", acc, index, value))
""
indexes
values
If you are concerned with speed you may want to use string builder since it doesn't create a new string every time you append.
let result =
List.fold2
(fun (sb:StringBuilder) index value ->
sb.AppendFormat("{{(ushort){0}, \"{1}\"}},\n", index, value))
(StringBuilder())
indexes
values
|> string
Fold probably isn't the best method for this task. Its a lot easier to map and concat like this:
let l1 = "a,b,c,d,e".Split([|','|])
let l2 = "1,2,3,4,5".Split([|','|])
let pairs =
Seq.zip l1 l2
|> Seq.map (fun (x, y) -> sprintf "(ushort)%s, \"%s\"" x y)
|> String.concat "\n"
I think you want List.fold2. For some reason the List module has a fold2 member but Seq doesn't. Then you can dispense with the zip entirely.
The types of your named variables and the type of the result you hope for are all implicit, so it's difficult to help, but if you are trying to accumulate a list of strings you might consider something along the lines of
let result = pairs |> Seq.fold
(fun prev (l, r) ->
String.Format("{0}, \{(ushort){1}, \"{2}\"\}\n", prev, l, r)
"" pairs
My F#/Caml is very rusty so I may have the order of arguments wrong. Also note your string formation is quadratic; in my own code I would go with something more along these lines:
let strings =
List.fold2 (fun ss l r ->
String.format ("\{(ushort){0}, \"{1}\"\}\n", l, r) :: ss)
[] indexes values
let result = String.concat ", " strings
This won't cost you quadratic time and it's a little easier to follow. I've checked MSDN and believe I have the correct order of arguments on fold2.
Keep in mind I know Caml not F# and so I may have details or order of arguments wrong.
Perhaps this:
let strBuilder = new StringBuilder()
for (i,v) in Seq.zip indexes values do
strBuilder.Append(String.Format("{{(ushort){0}, \"{1}\"}},\n", i,v))
|> ignore
with F# sometimes is better go imperative...
map2 or fold2 is the right way to go. Here's my take, using the (||>) operator:
let l1 = [| "a"; "b"; "c"; "d"; "e" |]
let l2 = [| "1"; "2"; "3"; "4"; "5" |]
let pairs = (l1, l2) ||> Seq.map2 (sprintf ("(ushort)%s, \"%s\""))
|> String.concat "\n"