将列表分为头部和尾部的目的是什么?

wro*_*yte 5 f# design-patterns functional-programming language-design

我是函数式编程的新手,刚刚遇到了在头部和尾部划分列表的模式。

\n

我有Javascript和Python的背景,据我所知,这些语言不采用这种划分,因此它似乎是与函数式编程范式相关的设计,因为像Haskell这样的语言采用了这种划分。例如,在 F# 中(根据Understandingpatternmatchingwithconsoperator),列表是类型的可区分联合

\n
type list<\'T> =         // \'\n    | Nil\n    | Cons of \'T * list<\'T>\n
Run Code Online (Sandbox Code Playgroud)\n

其中我们有一个 和 的[]并集\xe2\x80\x98a*list<\xe2\x80\x98a>。因此,列表是按设计“划分”的。

\n

头部和尾部的划分列表与函数范式有何关系?它有助于解决哪些问题?我希望能找到一些例子来说明这种划分是有用的和/或需要的。

\n

Mar*_*ann 6

先问它有助于解决什么问题?,询问它来自哪里可能会有所帮助?

但要回答这个问题,它有助于解决什么问题?,答案是:将零个或多个项目作为事物集合来处理的问题。

如果您现在说:但是我们已经为此拥有了集合(或者,在 .NET 中)!IEnumerable<T>,那只是因为您已经处于存在此类抽象的上下文中。

然而,想象一个这些都不存在的世界。

列表,从“缺点列表”的意义上来说,是从首要原则解决“众多”问题的一种方法。

如果您所拥有的只是类似于 lambda 演算的东西,那么您需要弄清楚如何以一致的方式处理许多对象。cons 列表是一种可以从 F 代数定义的数据类型。因此,它是一种基本数据类型 - 有点像逻辑门(如与非门)是计算机电路的基础。

特别是在 Haskell 中,它作为基本集合类型也非常有用,适合 80% 以上的正常使用。在 Haskell 中,您通常可以使用内置的链表 ( []) 数据类型来解决您的问题。

然而,它在 Haskell 中如此有用,还源于 Haskell 是惰性求值的事实。因此,在 Haskell 中,所有链表都是惰性的,这意味着如果您评估头部,则尾部可能保持“未评估”状态。

用 F# 术语来说,这实际上更接近seq 'a( IEnumerable<T>),它也是(可能)延迟计算的。

即便如此,F# 仍然带有一个内置的缺点列表-list类型。尽管它在 F# 中没有被延迟计算,但它仍然非常有用。

在许多方面,在 F# 中,listarray、 和seq是可以互换的,而且seq是奇怪的(因为它可以是惰性的和/或无限的)。

实际上,在 F# 中,listarray通常是可以互换的,因为它们具有相同的可供性。你能用 a 做什么list,你也可以用 an 做什么array,反之亦然。

然而,它们的用户体验和性能特征确实略有不同。F# 语言稍微偏爱列表而不是数组,因为如果您想在列表上创建或模式匹配,您可以使用语法[],而数组则要求您使用[||].

在某些算法中列表也可以更有效。许多函数算法通过递归构建列表。将元素添加到现有列表中是一种廉价的操作,因为您不必更改尾部。您只需更新列表指针以指向新的头。

另一方面,对于数组,每次想要追加一个元素时,都必须分配一个全新的数组并复制旧值。

这并不是说您不能有效地使用数组。你当然可以 - 特别是如果你可以预先计算数组的大小。

在 F# 中,有时数组很好,有时列表更好。就我个人而言,当我用 F# 编写代码时,我发现列表比数组或 s 更容易使用seq