F#序列表达式中的自引用

时间:2013-03-02 01:48:26

标签: f# sequence

有没有办法在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]

3 个答案:

答案 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 ")