例如,您是否可以在Haskell中定义列表而无需定义递归结构?或者用某些功能替换所有列表?
data List a = Empty | (a, List a) -- <- recursive definition
修改
我以列表为例,但我一般都在询问所有数据结构。 也许我们只需要一个递归数据结构来处理需要递归的所有情况?就像Y组合器是唯一需要的递归函数。 @TikhonJelvis的回答让我想到了这一点。 现在我很确定这篇文章更适合cs.stackexchange。
关于当前选定的答案
我真的在寻找看起来更像是@DavidYoung&amp; @TikhonJelvis,但他们只给出了部分答案,我很欣赏他们。 因此,如果有任何答案使用功能概念,请分享。
答案 0 :(得分:6)
这是一个奇怪的问题。我认为答案是并非真的,但数据类型的定义不必直接递归。
最终,列表是递归数据结构。如果没有某种递归某处,就无法定义它们。这是他们本质的核心。
但是,我们不必对List
的实际定义进行递归。相反,我们可以将递归分解为单个数据类型Fix
,然后使用它定义所有其他递归类型。从某种意义上说,Fix
只是抓住了数据结构递归意义的本质。 (它是fix
函数的类型级版本,它对函数执行相同的操作。)
data Fix f = Roll (f (Fix f))
我的想法是Fix f
对应于f
反复应用于自身。为了使它适用于Haskell的代数数据类型,我们必须在每个级别引入Roll
构造函数,但这不会改变类型所代表的内容。
基本上,f
反复应用于自身,这就是递归的本质。
现在我们可以定义一个非递归模拟List
,它采用一个额外的类型参数f
来代替我们之前的递归:
data ListF a f = Empty | Cons a f
这是一种简单的数据类型,不递归。
如果我们将两者结合起来,我们会得到旧的List
类型,除非在每个递归步骤中都有一些额外的Roll
构造函数。
type List a = Fix (ListF a)
此类型的值如下所示:
Roll (Cons 1 (Roll (Cons 2 (Roll Empty))))
它包含与(Cons 1 (Cons 2 Empty))
相同的信息,甚至仅包含[1, 2]
,但还有一些额外的构造函数。
因此,如果您获得Fix
,则可以在不使用递归的情况下定义List
。但这并不是特别特别,因为从某种意义上说,Fix
是递归。
答案 1 :(得分:6)
我不确定所有递归结构是否可以被非递归版本替换,但肯定可以包括列表。一种可行的方法是使用所谓的 Boehm-Berarducci encoding 。这是一种将结构表示为函数的方法,特别是该结构的折叠(在列表的情况下为foldr
):
{-# LANGUAGE RankNTypes #-}
type List a = forall x . (a -> x -> x) -> x -> x
-- ^^^^^^^^^^^^^ ^
-- Cons branch Nil branch
(来自上面的格式略有不同的链接)
此类型也类似于列表中的案例分析。第一个参数表示cons情形,第二个参数表示nil情形。
通常,sum类型的分支成为函数的不同参数,产品类型的字段变为具有每个字段的参数的函数类型。请注意,在上面的编码中,nil分支(通常)是非函数,因为nil构造函数不带参数,而cons分支有两个参数,因为cons构造函数有两个参数。定义的递归部分被替换为&#34;排名N类型(此处称为x
)。
答案 2 :(得分:2)
我认为这个问题分解为考虑Haskell提供的三个不同的特征子集:
仅查看(1),本机类型定义工具实际上并不提供定义除递归之外的任何无限大类型。
然而,在(2)中,Haskell 2010 provides the Data.Array
module提供了与(1)一起使用的数组类型,可用于构建许多不同结构的非递归定义。
即使语言没有提供数组,(3)也意味着我们可以将它们作为FFI扩展插入语言。 Haskell实现也被允许提供可用于此而不是FFI的额外功能,并且GHC的许多库利用这些功能(例如,vector
)。
所以我说最好的答案是Haskell只允许你定义非递归集合类型,只是它为你提供了基本的内置函数,你可以用它作为更复杂的构建块。