F#中的循环列表

时间:2010-02-18 07:36:52

标签: f#

只是我,还是F#不适合循环列表?

我通过反射器查看了FSharpList<T>类,注意到'结构等于'或长度方法都没有检查周期。我只能猜测是否有2个这样的原始函数没有检查,大多数列表函数都不会这样做。

如果不支持循环列表,为什么会这样?

由于

PS:我是否正在查看正确的列表类?

4 个答案:

答案 0 :(得分:15)

F#中有许多不同的列表/集合类型。

  • F#list类型。正如Chris所说,你不能初始化这种类型的递归值,因为类型不是懒惰而且不可变(Immutability意味着你必须立即创建它并且它不是懒惰的事实意味着你不能使用F#递归使用let rec)的值。正如ssp所说,你可以使用Reflection来破解它,但这可能是我们不想讨论的情况。

  • 另一种类型是seq(实际上是IEnumerable)或来自PowerPack的LazyList类型。这些是惰性的,因此您可以使用let rec创建循环值。但是,(据我所知),使用它们的函数都没有考虑循环列表 - 如果你创建一个循环列表,它只是意味着你正在创建一个无限列表,所以(例如)map的结果将是一个潜在的无限列表。

以下是LazyList类型的示例:

 #r "FSharp.PowerPack.dll"

 // Valid use of value recursion
 let rec ones = LazyList.consDelayed 1 (fun () -> ones)
 Seq.take 5 l // Gives [1; 1; 1; 1; 1]

问题是您可以自己定义哪些数据类型。 Chris显示了一个可变列表,如果您编写修改它的操作,它们将影响整个列表(如果您将其解释为无限数据结构)。

您还可以定义一个惰性(可能是循环的)数据类型并实现处理周期的操作,因此当您创建循环列表并将其投影到另一个列表中时,它将创建循环列表(而不是潜在无限)数据结构)。

类型声明可能如下所示(我正在使用对象类型,因此我们可以在检查周期时使用引用相等性):

type CyclicListValue<'a> = 
  Nil | Cons of 'a * Lazy<CyclicList<'a>>

and CyclicList<'a>(value:CyclicListValue<'a>) = 
  member x.Value = value

以下map函数处理循环 - 如果给它一个循环列表,它将返回一个具有相同循环结构的新创建的列表:

let map f (cl:CyclicList<_>) =  
  // 'start' is the first element of the list (used for cycle checking)
  // 'l' is the list we're processing
  // 'lazyRes' is a function that returns the first cell of the resulting list
  //  (which is not available on the first call, but can be accessed
  //  later, because the list is constructed lazily)
  let rec mapAux start (l:CyclicList<_>) lazyRes =
    match l.Value with
    | Nil -> new CyclicList<_>(Nil)
    | Cons(v, rest) when rest.Value = start -> lazyRes()
    | Cons(v, rest) -> 
      let value = Cons(f v, lazy mapAux start rest.Value lazyRes)
      new CyclicList<_>(value)
  let rec res = mapAux cl cl (fun () -> res)
  res

答案 1 :(得分:5)

F#列表类型本质上是一个链表,每个节点都有一个'next'。理论上这可以让你创建周期。但是,F#列表是不可变的。所以你永远不能通过变异“制造”这个循环,你必须在建设时这样做。 (因为你无法更新最后一个节点以循环到前面。)

可以写这个来做,但是编译器专门阻止它:

  let rec x = 1 :: 2 :: 3 :: x;;

  let rec x = 1 :: 2 :: 3 :: x;;
  ------------------------^^

stdin(1,25): error FS0260: Recursive values cannot appear directly as a construction of the type 'List`1' within a recursive binding. This feature has been removed from the F# language. Consider using a record instead.

如果您想创建一个循环,可以执行以下操作:

> type CustomListNode = { Value : int; mutable Next : CustomListNode option };;

type CustomListNode =
  {Value: int;
   mutable Next: CustomListNode option;}

> let head = { Value = 1; Next = None };;

val head : CustomListNode = {Value = 1;
                             Next = null;}

> let head2 = { Value = 2; Next = Some(head) } ;;

val head2 : CustomListNode = {Value = 2;
                              Next = Some {Value = 1;
                                           Next = null;};}

> head.Next <- Some(head2);;
val it : unit = ()
> head;;
val it : CustomListNode = {Value = 1;
                           Next = Some {Value = 2;
                                        Next = Some ...;};}

答案 2 :(得分:5)

对于具有尾调用优化支持和一流函数(函数类型)支持的所有语言,答案是相同的:它可以很容易地模拟循环结构。

let rec x = seq { yield 1; yield! x};;

通过使用seq的懒惰来模拟该结构是最简单的方法。 当然,您可以按照here所述的方式破解列表表示。

答案 3 :(得分:1)

如前所述,你的问题是list类型是不可变的,而list是循环的,你必须让它自己坚持到最后一个元素,这样不起作用。当然,你可以使用序列。

如果你有一个现有的list,并希望在它上面创建一个循环遍历列表元素的无限序列,那么你可以这样做:

let round_robin lst =
    let rec inner_rr l =        
        seq {
            match l with
            | [] ->
                 yield! inner_rr lst            
            | h::t ->                
                 yield h                
                 yield! inner_rr t            
        }    
     if lst.IsEmpty then Seq.empty else inner_rr []


let listcycler_sequence = round_robin [1;2;3;4;5;6]