我在F#中实现了一个标准的可变就地置换算法(如果有一些类似的内置方法,我会很感激这些信息):
let permutations f (alphabet:'a array) =
let swap i j =
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 n i
permutations' (n+1)
swap n i
permutations' 0
虽然这个函数非常通用,但我想知道在F#中是否有某种方法可以实现一个包装器函数,它可以将发现的项目作为序列生成。类似于以下(不正确的)F#方法:
let permutations_seq (alphabet:'a array) =
seq {
permutations (fun arr -> yield arr.Clone()) alphabet
}
在permutations
中,我不想直接yield
,因为我想维护一般功能,我不希望客户端总是为阵列克隆付出代价。
你会怎么做?
答案 0 :(得分:3)
如果你想从lambda函数“产生”结果,那么lambda函数本身需要返回一个序列(因此lambda函数的调用者也需要返回一个序列)。因此,如果不修改permutations
函数,就无法获得所需的内容(因为您无法将在一个(嵌套)作用域中运行的代码的值生成到某些其他(外部)作用域中定义的列表)。
但是,您可以将permutations
更改为如下所示:
let permutations f (alphabet:'a array) =
let swap i j =
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 n i
yield! permutations' (n+1)
swap n i }
permutations' 0
我将permutations'
包裹在seq { .. }
块中,并在yield!
之前添加f alphabet
(以便将函数f
生成的所有元素作为结果传递)我还在递归调用中添加了yield!
。
然后你可以写:
permutations (fun arr -> seq { yield arr |> Array.map id }) [|1;2;3|]
代码使用的是Array.map id
而不是Clone
,因此您可以获得数组的类型安全副本,而不是.NET克隆机制返回的obj
。
但是,我想你实际上并不需要从lambda中产生多个项目,所以你可以将yield! f alphabet
改为yield f alphabet
(只返回一个元素而不是多个元素)和写:
permutations (fun arr -> arr |> Array.map id) [|1;2;3|]
通过这种方式,您 - 至少 - 可以很好地抽象出克隆行为(您可以选择克隆或不克隆数组)。
答案 1 :(得分:1)
您必须生成未克隆的数组。有明显的
奇怪的行为,如果你在序列上调用toList
然后你得到一个数组的最后一个值的数组。所以
你需要做的第一件事就是Seq.map
克隆功能。我也认为没有必要
如果你已经准备好了你的函数递归
使用mutables。
let permutations (alphabet:'a array) =
let swap i j =
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 n i
yield! permutations' (n+1)
swap n i
}
permutations' 0
let a = [|"a"; "b"; "c"; "d"|]
let p =
(permutations a)
|> Seq.map (fun arr -> arr.Clone() )
|> Seq.toList
输出
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"|]]
答案 2 :(得分:1)
如果您真的想使用回叫(reactive
)方法,请使用
反应性扩展
https://github.com/fsharp/FSharp.Reactive/blob/master/src/Observable.fs
并写
let permutations (alphabet:'a array) =
Observable.create (fun subscriber ->
let swap i j =
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 n i
permutations' (n+1)
swap n i
}
permutations' 0
)
然后你可以做
permutations [|"a"; "b"; "c"|]
|> Observable.map ( fun arr -> arr.Clone() )
|> Observable.ToEnumerable
|> Seq.ToList
然而请注意与我发布的其他答案相关的对称性 在收益率上,所以在这种情况下你不会获得高于收益率的收益。