在无限序列生成期间删除元素

时间:2013-02-15 13:05:48

标签: haskell data-structures f#

我找到了一个很好的哈克尔解决方案(source)来生成Hofstadter sequence

hofstadter = unfoldr (\(r:s:ss) -> Just (r, r+s:delete (r+s) ss)) [1..]

现在,我也试图在F#中编写这样的解决方案。不幸的是(我对F#并不熟悉)到目前为止我没有成功。

我的问题是,当我在F#中使用sequence时,似乎无法删除元素(就像在haskell解决方案中完成的那样)。
允许删除元素的其他数据结构(如arrayslistset)不会生成无限序列,而只会对某些元素进行操作。

所以我的问题:在F#中是否可以生成无限序列,其中元素被删除?

到目前为止我尝试过的一些东西:

无限的数字序列:

let infinite =
    Seq.unfold( fun state -> Some( state, state + 1) ) 1

Hofstadter序列 - 无效,因为没有del关键字且语法错误更多

let hofstadter =
    Seq.unfold( fun (r :: s :: ss) -> Some( r, r+s, del (r+s) ss)) infinite

我考虑过使用Seq.filter,但也找不到解决方案。

3 个答案:

答案 0 :(得分:5)

我认为你需要的不仅仅是序列上的delete函数。您的示例需要在无限集合上进行模式匹配,该序列不支持。

Haskell列表的F#对应物是来自F#PowerPack的LazyList。 LazyList也可能是无限的,它支持模式匹配,这有助于您轻松实现delete

这是一个忠实的翻译:

open Microsoft.FSharp.Collections.LazyList

let delete x xs =  
    let rec loop x xs = seq {        
        match xs with
        | Nil -> yield! xs
        | Cons(x', xs') when x = x' -> yield! xs'
        | Cons(x', xs') ->
            yield x' 
            yield! loop x xs'
        }
    ofSeq (loop x xs)

let hofstadter =
    1I |> unfold (fun state -> Some(state, state + 1I))
       |> unfold (function | (Cons(r, Cons(s, ss))) -> 
                                 Some(r, cons (r+s) (delete (r+s) ss)) 
                           | _ -> None)
       |> toSeq

这里有一些有趣的事情:

  • 使用sequence expression实现delete以确保函数是尾递归的。非尾递归版本应该很容易。
  • 使用BigInteger;如果您不需要太多元素,则使用intSeq.initInfinite会更有效。
  • 添加一个返回None的案例,以确保详尽的模式匹配。
  • 最后我将LazyList转换为序列。它提供了与.NET集合更好的互操作性。

在序列上实现delete更加丑陋。如果您感到好奇,请查看Remove a single non-unique value from a sequence in F#以供参考。

答案 1 :(得分:4)

pad的解决方案很不错,但可能由于实现LazyList的方式,堆栈溢出介于3-4K数字之间。出于好奇心的缘故,我编写了一个围绕生成函数(unit -> 'a)构建的版本,该函数被重复调用以获取下一个元素(以解决IEnumerable的笨拙)。我能够获得前10K的数字(除此之外没有尝试过)。

let hofstadter() =

  let delete x f =
    let found = ref false
    let rec loop() =
      let y = f()
      if not !found && x = y
      then found := true; loop()
      else y
    loop

  let cons x f =
    let first = ref true
    fun () -> 
      if !first
      then first := false; x
      else f()

  let next =
    let i = ref 0
    fun () -> incr i; !i

  Seq.unfold (fun next -> 
    let r = next()
    let s = next()
    Some(r, (cons (r+s) (delete (r+s) next)))) next

答案 2 :(得分:3)

实际上,你可以使用过滤器和跟随haskell解决方案的设计(但是,正如@pad所说,你没有序列上的模式匹配;所以我使用了lisp风格的破坏):

let infinite = Seq.initInfinite (fun i -> i+1)

let generator = fun ss -> let (r, st)  = (Seq.head ss, Seq.skip 1 ss)                               
                          let (s, stt) = (Seq.head st, Seq.skip 1 st)
                          let srps     = seq [ r + s ]
                          let filtered = Seq.filter (fun t -> (r + s) <> t) stt
                          Some (r, Seq.append srps filtered)

let hofstadter = Seq.unfold generator infinite                               

let t10 = Seq.take 10 hofstadter |> Seq.toList
// val t10 : int list = [1; 3; 7; 12; 18; 26; 35; 45; 56; 69]

我对效率没有任何要求!