为什么F#更喜欢列表而不是数组?

时间:2011-03-21 15:19:37

标签: data-structures f#

我正在尝试学习F#并且在看到一些奇怪的东西(至少对我来说)时正在观看视频。有问题的视频是here,相关部分从2:30开始,感兴趣的人。但基本上,这个人说F#使得使用数组变得很尴尬,设计人员故意这样做,因为列表更容易“添加和追加”。

立即浮现在脑海中的问题:在不可改变的语言中添加一些应该不赞成的东西并不容易吗?具体来说,我正在考虑C#的列表,您可以在其中执行List.Add(obj);之类的操作并改变列表。使用数组,您必须创建一个全新的数组,但这也是在不可变语言中需要发生的事情。

那么为什么F#的设计师更喜欢列表呢?列表和数组之间的不可变环境的根本区别是什么?我错过了什么? F#中的列表是真正链接的列表吗?

5 个答案:

答案 0 :(得分:48)

我不同意“F#使得使用数组变得尴尬”。实际上,与大多数语言相比,F#使得使用数组非常好。

例如,F#具有文字数组构造:let arr = [|1;2;3;4;|]。在阵列上甚至可能更酷,模式匹配:

match arr with
| [|1;2;_;_|] -> printfn "Starts with 1;2"
| [|_;_;3;4|] -> printfn "Ends with 3;4"
| _ -> printfn "Array not recognized"

至于为什么不可变的单链表在F#这样的函数式编程中是首选的,有很多话要说,但简短的回答是它允许O(1)提前效率,并允许实现共享节点,所以它很容易记忆。例如,

let x = [2;3]
let y = 1::x

这里y是通过在x之前加1来创建的,但x既不修改也不复制,因此y非常便宜。我们可以看到这有多可能,因为x指向最初构造的列表的头部2,并且只能向前移动,并且因为它指向的列表的元素不能被突变,所以它不会与y共享节点的问题。

答案 1 :(得分:30)

在功能语言中,列表通常是单链表。即没有必要复制完整列表。相反,前置(通常称为cons)是一个O(1)操作,你仍然可以使用旧列表,因为列表是不可变的。

答案 2 :(得分:13)

首先,数组是相当低级的数据结构,只有在创建数组时才知道数组的长度,它们才真正有用。情况并非如此,这就是C#程序员使用System.Collections.Generic.List<T>和F#程序员使用F#list<T>的原因。

F#更喜欢自己的功能列表而不是使用.NET List<T>的原因是函数式语言更喜欢不可变类型。您可以通过编写list.Add(x)来创建包含添加到前端的项目的新列表,而不是通过调用let newList = x::list来修改对象。

我也同意Stephen的观点,即在F#中使用数组并不尴尬。如果您知道正在使用的元素数量,或者您正在转换某些现有数据源,那么使用数组非常简单:

/ You can create arrays using `init`
let a = Array.init 10 (fun i -> (* calculate i-th element here *) )

// You can transform arrays using `map` and `filter`
a |> Array.map (fun n -> n + 10)
  |> Array.filter (fun n -> n > 12)

// You can use array comprehensions:
let a2 = [| for n in a do
              if n > 12 then yield n + 10 |]

这与处理列表基本相同 - 您可以使用列表推导[ ... ]和列表处理函数,例如List.map等。差异确实出现在初始化列表/数组时。

答案 3 :(得分:9)

  

F#使得使用数组

变得尴尬

F#提供了许多功能,使得使用数组比使用其他语言更容易,包括数组文字,数组模式和高阶函数。

  

立即浮现在脑海中的问题:在不可改变的语言中添加一些不应该被忽视的东西并不容易吗?

我相信你误解了这句话的含义。当人们谈论在纯功能数据结构的上下文中进行前置和附加时,他们指的是创建一个新集合,该集合是使用现有集合派生(并与其大部分内部共享)。

  

那么为什么F#的设计师更喜欢列表?

F#从OCaml继承了一些与列表相关的功能,这些功能从标准ML和ML继承它们,因为单链接的不可变列表在其应用程序域(元编程)的上下文中非常有用,但我不会说F#的设计者更喜欢列表。

  

列表和数组之间不可变环境的根本区别是什么?

在F#中,列表提供O(1)前置和附加以及O(n)随机访问,而阵列提供O(n)前置和附加以及O(1)随机访问。数组可以变异,但列表不能。

  

我错过了什么?

纯功能数据结构的基础知识。阅读冈崎。

  

F#中的列表真的是链接列表吗?

是。具体来说,单链接的不可变列表。实际上,在某些ML中,list类型可以定义为:

type 'a list =
  | ([])
  | (::) of 'a * 'a list

这就是::运算符是构造函数而不是函数的原因,因此您不能像(::)那样编写(+)

答案 4 :(得分:5)

F#列表更像是以下数据结构 - 单个链表:

public class List<T> {

    public List(T item, List<T> prev) { /*...*/ }

    public T Item { get; }
    public List<T> Prev { get; }
}

因此,当创建新列表时,它实际上是创建一个单一节点,其中引用了上一个列表的第一个元素,而不是复制整个数组。