1

有没有办法在 F# 序列表达式中进行自引用?例如:

[for i in 1..n do if _f(i)_not_in_this_list_ do yield f(i)]

这可以防止插入重复的元素。

编辑:一般情况下,我想在应用 f() 之前知道 this_list 的内容,这在计算上非常昂贵。

编辑:我在上面的例子中过于简单化了。我的具体情况是计算量大的测试 T (T: int -> bool) 具有属性 T(i) => 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]
4

3 回答 3

2

你可以有递归序列表达式,例如

let rec allFiles dir =
    seq { yield! Directory.GetFiles dir
          for d in Directory.GetDirectories dir do
              yield! allFiles d }

但循环引用是不可能的。

另一种方法是使用Seq模块中的Seq.distinct :

seq { for i in 1..n -> f i }
|> Seq.distinct

或根据@John 的评论在消费前使用Set.ofSeq将序列转换为设置。

于 2013-03-02T02:24:18.877 回答
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
   }
于 2013-03-02T12:16:17.257 回答
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 ")
于 2013-03-02T15:18:53.657 回答