F# F中的循环列表#

F# F中的循环列表#,f#,F#,只是我,还是F#不适合循环列表 我通过reflector查看了FSharpList类,发现“structuralequals”或length方法都没有检查循环。我只能猜测,如果两个这样的基本函数不检查,大多数列表函数也不会这样做 如果不支持循环列表,为什么会这样 谢谢 PS:我看到的是正确的列表类吗?F#列表类型本质上是一个链表,其中每个节点都有一个“下一个”。理论上,这将允许您创建循环。然而,F#列表是不可变的。所以你永远不可能通过变异来“创造”这个循环,你必须在构建的时候去做。(因为无法更新

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

我通过reflector查看了
FSharpList
类,发现“structuralequals”或length方法都没有检查循环。我只能猜测,如果两个这样的基本函数不检查,大多数列表函数也不会这样做

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

谢谢

PS:我看到的是正确的列表类吗?

F#列表类型本质上是一个链表,其中每个节点都有一个“下一个”。理论上,这将允许您创建循环。然而,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 ...;};}
>键入CustomListNode={Value:int;可变下一步:CustomListNode选项};;
类型CustomListNode=
{值:int;
可变下一步:CustomListNode选项;}
>让head={Value=1;Next=None};;
val head:CustomListNode={Value=1;
Next=null;}
>设head2={Value=2;Next=Some(head)};;
val head2:CustomListNode={Value=2;
Next=Some{Value=1;
Next=null;};}
>头,下一头;;
val it:CustomListNode={Value=1;
Next=Some{Value=2;
下一步=一些…;};}

所有支持尾部调用优化和一流函数(函数类型)支持的语言的答案都是一样的:模拟循环结构非常容易

let rec x = seq { yield 1; yield! x};;
使用seq的惰性是模拟该结构的最简单方法。
当然,您可以按照所述修改列表表示。

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

  • F#
    列表
    类型。正如Chris所说,您无法初始化此类型的递归值,因为该类型不是惰性的且不可变的(不变性意味着您必须立即创建它,而它不是惰性的事实意味着您不能使用F#递归值使用
    let rec
    )。正如ssp所说,您可以使用反射来破解它,但这可能是我们不想讨论的情况

  • 另一种类型是PowerPack的
    seq
    (实际上是
    IEnumerable
    )或
    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显示了一个可变列表,如果您编写修改它的操作,它们将影响整个列表(如果您将其解释为无限数据结构)

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

类型声明可能如下所示(我使用的是对象类型,因此在检查循环时可以使用引用相等):


如前所述,这里的问题是
列表
类型是不可变的,如果
列表
是循环的,则必须将其自身粘贴到其最后一个元素中,这样做不起作用。当然,您可以使用序列

如果您有一个现有的
列表
,并希望在其上创建一个无限序列,循环遍历列表的元素,下面是您可以执行的方法:

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]

不是我想要的答案。我也无法测试它,因为我的F#1.9.9.9无法运行:(我猜它们是严格不可变的,因此不需要检查循环。@leppie:不,OCaml的列表是严格不可变的,但您仍然可以使它们循环,也不需要检查循环。因此与OCaml中的循环列表相比,
xs=ys
只是挂起而已。:-)“你必须让它自己粘到最后一个元素中,这样就不行了"。也许值得一提的是,
让rec xs=1::xs
在OCaml中创建一个循环的不可变单链表就可以了。@Jon:这很有趣,让我有点困惑。我很确定我已经弄明白了这是如何工作的……它只是创建了一个带有指向自身的尾部的
cons
单元格。也许值得一提这可以在OCaml中完成。我甚至可以在这里引用这种功能在递归闭包实现中的唯一实际应用:注意,OCaml允许您创建真正的循环列表。它们将比
seq
快得多。
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
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]