只是我,还是F#不适合循环列表?
我通过反射器查看了FSharpList<T>
类,注意到'结构等于'或长度方法都没有检查周期。我只能猜测是否有2个这样的原始函数没有检查,大多数列表函数都不会这样做。
如果不支持循环列表,为什么会这样?
由于
PS:我是否正在查看正确的列表类?
答案 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]