有没有办法在F#序列表达式中进行自引用?例如:
[for i in 1..n do if _f(i)_not_in_this_list_ do yield f(i)]
可防止插入重复元素。
编辑:在一般情况下,我想在应用f()之前知道this_list的内容,这在计算上非常昂贵。
编辑:我在上面的示例中过于简化了。我的具体情况是具有属性T(i)=>的计算上昂贵的测试T(T:int - > bool)。 T(n * i)所以代码片段是:
[for i in 1..n do if _i_not_in_this_list_ && T(i) then for j in i..i..n do yield j]
目标是减少T()应用程序的数量并使用简洁的表示法。我通过使用可变辅助数组完成了前者:
let mutable notYet = Array.create n true
[for i in 1..n do if notYet.[i] && T(i) then for j in i..i..n do yield j; notYet.[j] <- false]
答案 0 :(得分:2)
您可以使用递归sequence expression,例如
let rec allFiles dir =
seq { yield! Directory.GetFiles dir
for d in Directory.GetDirectories dir do
yield! allFiles d }
但不能循环引用。
另一种方法是使用Seq.distinct模块中的Seq:
seq { for i in 1..n -> f i }
|> Seq.distinct
或根据@ John的评论将序列转换为使用Set.ofSeq进行设置。
答案 1 :(得分:2)
您也可以决定以明确的方式维护有关以前生成的元素的信息;例如:
let genSeq n =
let elems = System.Collections.Generic.HashSet()
seq {
for i in 1..n do
if not (elems.Contains(i)) then
elems.Add(i) |> ignore
yield i
}
答案 2 :(得分:2)
这里有几点考虑因素
首先,在实际计算f(i)
之前,您无法检查列表中是否有f(i)
。所以我猜你的意思是你的check
功能很贵,而不是f(i)
本身。如果我错了,请纠正我。
其次,如果check
确实计算成本非常高,您可能会寻找更有效的算法。不能保证你会为每个序列找到一个,但它们经常存在。那么您的代码将只是一个Seq.unfold
。
第三。当没有这样的优化时,您可以采取另一种方法。在[for...yield]
内,您只构建一个当前元素,而无法访问之前的元素。无需返回元素,而是手动构建整个列表似乎是要走的路:
// a simple algorithm checking if some F(x) exists in a sequence somehow
let check (x:string) xs = Seq.forall (fun el -> not (x.Contains el)) xs
// a converter i -> something else
let f (i: int) = i.ToString()
let generate f xs =
let rec loop ys = function
| [] -> List.rev ys
| x::t ->
let y = f x
loop (if check y ys then y::ys else ys) t
loop [] xs
// usage
[0..3..1000] |> generate f |> List.iter (printf "%O ")