In Rust, you can cycle on an iterator. For example:
fn main() {
for i in (1..4).cycle().take(5) {
print!("{} ", i);
}
}
Ouput:
1 2 3 1 2
How to do the same in F#?
[<EntryPoint>]
let main argv =
seq { 1 .. 4 } |> (* Something *) |> Seq.take 5 |> Seq.iter (printf "%d ")
0
I don't know of any built-in function, but you can easily create your own:
let cycle items = seq {
while true do
yield! items
}
or
let cycle' items =
Seq.initInfinite (fun _ -> items) |> Seq.concat
or
let rec cycle'' items = seq {
yield! items
yield! cycle'' items
}
Then
// 1 2 3 1 2
seq { 1 .. 3 } |> cycle |> Seq.take 5 |> Seq.iter (printf "%d ")
Related
I have the following list of tuples ordered by the first item. I want to cluster the times by
If the second item of the tuple is greater then 50, it will be in its own cluster.
Otherwise, cluster the items whose sum is less than 50.
The order cannot be changed.
code:
let values =
[("ACE", 78);
("AMR", 3);
("Aam", 6);
("Acc", 1);
("Adj", 23);
("Aga", 12);
("All", 2);
("Ame", 4);
("Amo", 60);
//....
]
values |> Seq.groupBy(fun (k,v) -> ???)
The expected value will be
[["ACE"] // 78
["AMR"; "Aam"; "Acc"; "Adj"; "Aga"; "All"] // 47
["Ame"] // 4
["Amo"] // 60
....]
Ideally, I want to evenly distribute the second group (["AMR"; "Aam"; "Acc"; "Adj"; "Aga"; "All"] which got the sum of 47) and the third one (["Ame"] which has only 4).
How to implement it in F#?
I had the following solution. It uses a mutable variable. It's not F# idiomatic? Is for ... do imperative in F# or is it a syntactic sugar of some function construct?
seq {
let mutable c = []
for v in values |> Seq.sortBy(fun (k, _) -> k) do
let sum = c |> Seq.map(fun (_, v) -> v) |> Seq.sum
if not(c = []) && sum + (snd v) > 50
then
yield c
c <- [v]
else
c <- List.append c [v]
}
I think I got it. Not the nicest code ever, but works and is immutable.
let foldFn (acc:(string list * int) list) (name, value) =
let addToLast last =
let withoutLast = acc |> List.filter ((<>) last)
let newLast = [((fst last) # [name]), (snd last) + value]
newLast |> List.append withoutLast
match acc |> List.tryLast with
| None -> [[name],value]
| Some l ->
if (snd l) + value <= 50 then addToLast l
else [[name], value] |> List.append acc
values |> List.fold foldFn [] |> List.map fst
Update: Since append can be quite expensive operation, I added prepend only version (still fulfills original requirement to keep order).
let foldFn (acc:(string list * int) list) (name, value) =
let addToLast last =
let withoutLast = acc |> List.filter ((<>) last) |> List.rev
let newLast = ((fst last) # [name]), (snd last) + value
(newLast :: withoutLast) |> List.rev
match acc |> List.tryLast with
| None -> [[name],value]
| Some l ->
if (snd l) + value <= 50 then addToLast l
else ([name], value) :: (List.rev acc) |> List.rev
Note: There is still # operator on line 4 (when creating new list of names in cluster), but since the theoretical maximum amount of names in cluster is 50 (if all of them would be equal 1), the performance here is negligible.
If you remove List.map fst on last line, you would get sum value for each cluster in list.
Append operations are expensive. A straight-forward fold with prepended intermediate results is cheaper, even if the lists need to be reversed after processing.
["ACE", 78; "AMR", 3; "Aam", 6; "Acc", 1; "Adj", 23; "Aga", 12; "All", 2; "Ame", 4; "Amd", 6; "Amo", 60]
|> List.fold (fun (r, s1, s2) (t1, t2) ->
if t2 > 50 then [t1]::s1::r, [], 0
elif s2 + t2 > 50 then s1::r, [t1], t2
else r, t1::s1, s2 + t2 ) ([], [], 0)
|> fun (r, s1, _) -> s1::r
|> List.filter (not << List.isEmpty)
|> List.map List.rev
|> List.rev
// val it : string list list =
// [["ACE"]; ["AMR"; "Aam"; "Acc"; "Adj"; "Aga"; "All"]; ["Ame"; "Amd"];
// ["Amo"]]
Here is a recursive version - working much the same way as fold-versions:
let groupBySums data =
let rec group cur sum acc lst =
match lst with
| [] -> acc |> List.where (not << List.isEmpty) |> List.rev
| (name, value)::tail when value > 50 -> group [] 0 ([(name, value)]::(cur |> List.rev)::acc) tail
| (name, value)::tail ->
match sum + value with
| x when x > 50 -> group [(name, value)] 0 ((cur |> List.rev)::acc) tail
| _ -> group ((name, value)::cur) (sum + value) acc tail
(data |> List.sortBy (fun (name, _) -> name)) |> group [] 0 []
values |> groupBySums |> List.iter (printfn "%A")
Yet another solution using Seq.mapFold and Seq.groupBy:
let group values =
values
|> Seq.mapFold (fun (group, total) (name, count) ->
let newTotal = count + total
let newGroup = group + if newTotal > 50 then 1 else 0
(newGroup, name), (newGroup, if newGroup = group then newTotal else count)
) (0, 0)
|> fst
|> Seq.groupBy fst
|> Seq.map (snd >> Seq.map snd >> Seq.toList)
Invoke it like this:
[ "ACE", 78
"AMR", 3
"Aam", 6
"Acc", 1
"Adj", 23
"Aga", 12
"All", 2
"Ame", 4
"Amo", 60
]
|> group
|> Seq.iter (printfn "%A")
// ["ACE"]
// ["AMR"; "Aam"; "Acc"; "Adj"; "Aga"; "All"]
// ["Ame"]
// ["Amo"]
I want a tool for testing Rx components that would work like this:
Given an order of the events specified as a 'v seq and a key selector function (keySelector :: 'v -> 'k) I want to create a Map<'k, IObservable<'k>> where the guarantee is that the groupped observables yield the values in the global order defined by the above enumerable.
For example:
makeObservables isEven [1;2;3;4;5;6]
...should produce
{ true : -2-4-6|,
false: 1-3-5| }
This is my attempt looks like this:
open System
open System.Reactive.Linq
open FSharp.Control.Reactive
let subscribeAfter (o1: IObservable<'a>) (o2 : IObservable<'b>) : IObservable<'b> =
fun (observer : IObserver<'b>) ->
let tempObserver = { new IObserver<'a> with
member this.OnNext x = ()
member this.OnError e = observer.OnError e
member this.OnCompleted () = o2 |> Observable.subscribeObserver observer |> ignore
}
o1.Subscribe tempObserver
|> Observable.Create
let makeObservables (keySelector : 'a -> 'k) (xs : 'a seq) : Map<'k, IObservable<'a>> =
let makeDependencies : ('k * IObservable<'a>) seq -> ('k * IObservable<'a>) seq =
let makeDep ((_, o1), (k2, o2)) = (k2, subscribeAfter o1 o2)
Seq.pairwise
>> Seq.map makeDep
let makeObservable x = (keySelector x, Observable.single x)
let firstItem =
Seq.head xs
|> makeObservable
|> Seq.singleton
let dependentObservables =
xs
|> Seq.map makeObservable
|> makeDependencies
dependentObservables
|> Seq.append firstItem
|> Seq.groupBy fst
|> Seq.map (fun (k, obs) -> (k, obs |> Seq.map snd |> Observable.concatSeq))
|> Map.ofSeq
[<EntryPoint>]
let main argv =
let isEven x = (x % 2 = 0)
let splits : Map<bool, IObservable<int>> =
[1;2;3;4;5]
|> makeObservables isEven
use subscription =
splits
|> Map.toSeq
|> Seq.map snd
|> Observable.mergeSeq
|> Observable.subscribe (printfn "%A")
Console.ReadKey() |> ignore
0 // return an integer exit code
...but the results are not as expected and the observed values are not in the global order.
Apparently the items in each group are yield correctly but when the groups are merged its more like a concat then a merge
The expected output is: 1 2 3 4 5
...but the actual output is 1 3 5 2 4
What am I doing wrong?
Thanks!
You describe wanting this:
{ true : -2-4-6|,
false: 1-3-5| }
But you're really creating this:
{ true : 246|,
false: 135| }
Since there's no time gaps between the items in the observables, the merge basically has a constant race condition. Rx guarantees that element 1 of a given sequence will fire before element 2, but Merge offers no guarantees around cases like this.
You need to introduce time gaps into your observables if you want Merge to be able to re-sequence in the original order.
How do I split up a collection by number of elements?
For example, if I have the following:
0,1,2,3,4,5,6,7,8
How could I partition the collection into 3 sets:
0,1,2
3,4,5
6,7,8
NOTE:
F# is extremely foreign to me. So forgive my ignorance.
Here's a TicTacToe exercise that I am trying to learn F# with.
In the code, I am using Seq.take and Seq.skip.
How could I write this differently?
module TicTacToe
open FsUnit
open NUnit.Framework
[<Test>]
let ``player has connected row`` () =
let grid = Map.empty
.Add(0, true).Add(1, true).Add(2, true)
.Add(3, true).Add(4, false).Add(5, true)
.Add(6, true).Add(7, true).Add(8, true)
let firstRowIsStreak = grid
|> Seq.take 3
|> Seq.forall (fun x -> x.Value = true)
let secondRowIsStreak = grid
|> Seq.skip 3
|> Seq.take 3
|> Seq.forall (fun x -> x.Value = true)
let thirdRowIsStreak = grid
|> Seq.skip 6
|> Seq.take 3
|> Seq.forall (fun x -> x.Value = true)
firstRowIsStreak |> should equal true
secondRowIsStreak |> should equal false
thirdRowIsStreak |> should equal true
If you have F# 4.0 you can use Seq.chunkBySize
Seq.chunkBySize 3 (seq [0;1;2;3;4;5;6;7;8])
val it : seq<int []> = seq [[|0; 1; 2|]; [|3; 4; 5|]; [|6; 7; 8|]]
The following function files returns seq<seq<R>>. How to make it return seq<R> instead?
type R = { .... }
let files = seqOfStrs |> Seq.choose(fun s ->
match s with
| Helper.ParseRegex "(\w+) xxxxx" month ->
let currentMonth = .....
if currentMonth = month.[0] then
doc.LoadHtml(s)
Some (
doc.DucumentNode.SelectNodes("....")
|> Seq.map(fun tr ->
{ ..... } ) //R. Some code return record type R. Omitted
)
else
printfn "Expect %s found %s." currentMonth month.[0]
None
| _ ->
printfn "No '(Month) Payment Data On Line' prompt."
None
Your snippet is incomplete and so we can't give you a fully working answer. But:
Your code is using Seq.choose and you are returning either None or Some with collection of values. Then you get a sequence of sequences...
You can use Seq.collect which flattens the sequences and replace None with an empty sequence and Some with just the sequence.
Something along those lines (untested):
let files = seqOfStrs |> Seq.collect (fun s ->
match s with
| Helper.ParseRegex "(\w+) xxxxx" month ->
let currentMonth = .....
if currentMonth = month.[0] then
doc.LoadHtml(s)
doc.DucumentNode.SelectNodes("....")
|> Seq.map(fun tr ->
{ ..... } ) //R. Some code return record type R. Omitted
else
printfn "Expect %s found %s." currentMonth month.[0]
Seq.empty
| _ ->
printfn "No '(Month) Payment Data On Line' prompt."
Seq.empty )
The other options like adding Seq.concat or Seq.collect id to the end of the pipeline would obviously work too.
You want to pipe the whole thing to Seq.collect.
for example,
files |> Seq.collect id
You can use an F# sequence expresssion to flatten the seq of seqs into a seq. Say you have:
> let xss = seq { for i in 1 .. 2 -> seq { for j in 1 .. 2 -> i * j } };;
val xss : seq<seq<int>>
> xss;;
val it : seq<seq<int>> = seq [seq [1; 2]; seq [2; 4]]
Then you can do:
> seq { for x in xss do yield! x };;
val it : seq<int> = seq [1; 2; 2; 4]
Behind the scenes, the sequence expression is doing the same thing as Seq.collect, just in a more syntax-sugary way.
let aBunch = 1000
let offset = 0
let getIt offset =
MyIEnumerable
|> Seq.skip aBunch * offset
|> Seq.take aBunch
|> Seq.iter ( .. some processing ...)
Calling getIt() with different offsets eventually gives me an 'Invalid operation' exception with additional info that 'the input sequence had insufficient elements'
I try to understand why, as both the Seq.Skip and Seq.take do not generate an exception according to the online documentation FSharp Collections
Version: (Visual Studio 2010) Beta 1
I know this is an old question, but in case someone comes across this in a search the way I did:
You can use Seq.truncate if you want at most n items. It won't throw an exception if fewer than n items are available.
Both Seq.skip and Seq.take will throw this exception if called with a value larger than the sequence. You can check the source code in Seq.fs to see why:
let skip count (sequence: seq<_>) =
{ use e = sequence.GetEnumerator()
let latest = ref (Unchecked.defaultof<_>)
let ok = ref false
for i in 1 .. count do
if not (e.MoveNext()) then
raise <| System.InvalidOperationException "the input sequence had insufficient elements"
while e.MoveNext() do
yield e.Current }
let take count (sequence : seq<'T>) =
if count < 0 then invalidArg "count" "the number of elements to take may not be negative"
(* Note: don't create or dispose any IEnumerable if n = 0 *)
if count = 0 then empty else
{ use e = sequence.GetEnumerator()
for i in 0 .. count - 1 do
if not (e.MoveNext()) then
raise <| System.InvalidOperationException "the input sequence had insufficient elements"
yield e.Current }
For an exceptionless skip you can add your own version to the Seq module like this:
module Seq =
let skipSafe (num: int) (source: seq<'a>) : seq<'a> =
seq {
use e = source.GetEnumerator()
let idx = ref 0
let loop = ref true
while !idx < num && !loop do
if not(e.MoveNext()) then
loop := false
idx := !idx + 1
while e.MoveNext() do
yield e.Current
}
Combined with Seq.truncate (which is an exceptionless Seq.take equivalent - it will take as much items are available without throwing an exception).
[1..10]
|> Seq.skipSafe 20
|> Seq.truncate 5
(* returns empty seq *)
Here's a slightly shorter "skipSafe" implementation using built in functions:
module Seq =
let skipSafe num =
Seq.zip (Seq.initInfinite id)
>> Seq.skipWhile (fun (i, _) -> i < num)
>> Seq.map snd
Or if you wish to just inline it into your current pipeline directly, replace
|> Seq.skip num
with
|> Seq.zip (Seq.initInfinite id)
|> Seq.skipWhile (fun (i, _) -> i < num)
|> Seq.map snd
module Seq =
let trySkip count source =
source |> Seq.indexed |> Seq.filter(fst >> (<=) count) |> Seq.map snd