Exit from inside a lambda expression in F #

I implemented a standard mutable in-place permutation algorithm in F # (if there is some kind of built-in way to do something like this, I would be grateful for the information):

let permutations f (alphabet:'a array) = let swap ij = let aux = alphabet.[i] alphabet.[i] <- alphabet.[j] alphabet.[j] <- aux let rec permutations' n = if n = alphabet.Length then f alphabet else for i in n..(alphabet.Length-1) do swap ni permutations' (n+1) swap ni permutations' 0 

Although the function is pretty universal, I was wondering if there was any way in F # to implement a wrapper function that would give me the elements found as a sequence. Something similar to the following (wrong) F # method:

 let permutations_seq (alphabet:'a array) = seq { permutations (fun arr -> yield arr.Clone()) alphabet } 

In permutations I don't want yield directly, since I would like to support the general function, and I don't want the client to always have to pay the price for cloning the array.

How would you do that?

+6
source share
3 answers

If you want to β€œoutput” the results from a lambda function, then the lambda function itself must return a sequence (and therefore, the caller of the lambda function must also return a sequence). Thus, you cannot get what you want without changing the permutations function (because you cannot transfer values ​​from code running in one (nested) region to a list defined in some other (outer) region).

However, you can change permutations to look something like this:

 let permutations f (alphabet:'a array) = let swap ij = let aux = alphabet.[i] alphabet.[i] <- alphabet.[j] alphabet.[j] <- aux let rec permutations' n = seq { if n = alphabet.Length then yield! f alphabet else for i in n..(alphabet.Length-1) do swap ni yield! permutations' (n+1) swap ni } permutations' 0 

I wrapped permutations' in a seq { .. } block seq { .. } and added yield! to f alphabet (so that all elements created by f are passed as results), and I also added yield! to recursive call.

Then you can write:

 permutations (fun arr -> seq { yield arr |> Array.map id }) [|1;2;3|] 

The code uses Array.map id instead of Clone , so you get a copy of the array type, not obj , returned by the .NET cloning mechanism.

However, I suppose you really don't need to output multiple elements from lambda, so you can change yield! f alphabet yield! f alphabet to just yield f alphabet (to return only one element, not multiple elements) and write:

 permutations (fun arr -> arr |> Array.map id) [|1;2;3|] 

This way you will at least get a good way to abstract cloning behavior (and you can clone or not clone an array easily).

+3
source

You must provide an unblocked array. There is an obvious strange behavior if you call toList in a sequence, you will get an array of the last value of the array. So the first thing you need to do is Seq.map it with the clone function. Also, I do not think that there is a need to make your function recursive if you are ready to work with variables.

 let permutations (alphabet:'a array) = let swap ij = let aux = alphabet.[i] alphabet.[i] <- alphabet.[j] alphabet.[j] <- aux let rec permutations' n = seq { if n = alphabet.Length then yield alphabet else for i in n..(alphabet.Length-1) do swap ni yield! permutations' (n+1) swap ni } permutations' 0 let a = [|"a"; "b"; "c"; "d"|] let p = (permutations a) |> Seq.map (fun arr -> arr.Clone() ) |> Seq.toList 

exits

  val p : obj list = [[|"a"; "b"; "c"; "d"|]; [|"a"; "b"; "d"; "c"|]; [|"a"; "c"; "b"; "d"|]; [|"a"; "c"; "d"; "b"|]; [|"a"; "d"; "c"; "b"|]; [|"a"; "d"; "b"; "c"|]; [|"b"; "a"; "c"; "d"|]; [|"b"; "a"; "d"; "c"|]; [|"b"; "c"; "a"; "d"|]; [|"b"; "c"; "d"; "a"|]; [|"b"; "d"; "c"; "a"|]; [|"b"; "d"; "a"; "c"|]; [|"c"; "b"; "a"; "d"|]; [|"c"; "b"; "d"; "a"|]; [|"c"; "a"; "b"; "d"|]; [|"c"; "a"; "d"; "b"|]; [|"c"; "d"; "a"; "b"|]; [|"c"; "d"; "b"; "a"|]; [|"d"; "b"; "c"; "a"|]; [|"d"; "b"; "a"; "c"|]; [|"d"; "c"; "b"; "a"|]; [|"d"; "c"; "a"; "b"|]; [|"d"; "a"; "c"; "b"|]; [|"d"; "a"; "b"; "c"|]] 
+1
source

If you really want to use a reactive callback, use reactive extensions

https://github.com/fsharp/FSharp.Reactive/blob/master/src/Observable.fs

and write

 let permutations (alphabet:'a array) = Observable.create (fun subscriber -> let swap ij = let aux = alphabet.[i] alphabet.[i] <- alphabet.[j] alphabet.[j] <- aux let rec permutations' n = seq { if n = alphabet.Length then subscriber.OnNext(alphabet) else for i in n..(alphabet.Length-1) do swap ni permutations' (n+1) swap ni } permutations' 0 ) 

Then you can do

 permutations [|"a"; "b"; "c"|] |> Observable.map ( fun arr -> arr.Clone() ) |> Observable.ToEnumerable |> Seq.ToList 

However, pay attention to the symmetry in relation to the other answer that I posted based on the crop, so in this case you will not get much more crop.

+1
source

Source: https://habr.com/ru/post/947380/


All Articles