Cha*_*ieP 14 haskell types linked-list list abstract-data-type
据我所知,Haskell中的列表类型是使用链表在内部实现的.但是,该语言的用户无法查看实现的详细信息,也无法修改组成链接列表的"链接"以允许其指向不同的内存地址.我想,这是内部完成的.
那么,如何在Haskell中限定列表类型?它是"数据类型"还是"抽象数据类型"?那个实现的链表类型是什么?
另外,由于Prelude提供的列表类型不是链表类型,如何实现基本链表功能?
举例来说,这段代码旨在在列表的索引n处添加元素a:
add [] acc _ _ = reverse acc
add (x:xs) acc 0 a = add xs (x:a:acc) (-1) a
add (x:xs) acc n a = add xs (x:acc) (n-1) a
Run Code Online (Sandbox Code Playgroud)
使用"真实"链表,添加元素只需修改指向内存地址的指针.这在Haskell中是不可能的(或者是它?),因此问题是:我的实现是将一个元素添加到列表中最好的一个,或者我错过了一些东西(reverse我觉得,使用该函数特别难看,但是可以不做吗?)
如果我说的话有误,请不要犹豫,纠正我,谢谢你的时间.
Chu*_*uck 10
你混淆了数据结构的可变性.这是一个正确的列表 - 只是你不允许修改的列表.Haskell纯粹是功能性的,意味着值是不变的 - 您不能更改列表中的项目,而不能将数字2转换为3.而是执行计算以根据需要更改创建新值.
您可以通过以下方式最简单地定义该函数:
add ls idx el = take idx ls ++ el : drop idx ls
Run Code Online (Sandbox Code Playgroud)
该列表el : drop idx ls重用原始列表的尾部,因此您只需要生成一个新的列表idx(这是take函数的作用).如果你想使用显式递归来实现它,你可以像这样定义它:
add ls 0 el = el : ls
add (x:xs) idx el
| idx < 0 = error "Negative index for add"
| otherwise = x : add xs (idx - 1) el
add [] _ el = [el]
Run Code Online (Sandbox Code Playgroud)
这以相同的方式重用列表的尾部(这el : ls是第一种情况).
由于您似乎无法查看这是一个链接列表,因此我们要清楚链接列表是什么:它是由单元格组成的数据结构,其中每个单元格都有一个值和对下一个项目的引用.在C中,它可能被定义为:
struct ListCell {
void *value; /* This is the head */
struct ListCell *next; /* This is the tail */
}
Run Code Online (Sandbox Code Playgroud)
在Lisp中,它定义为(head . tail),head值在哪里,tail是对下一个项的引用.
在Haskell中,它定义为data [] a = [] | a : [a],a值在哪里,[a]是对下一个项的引用.
如您所见,这些数据结构都是等效的.唯一的区别是在C和Lisp中,它们不是纯粹的功能,头部和尾部值是你可以改变的东西.在Haskell中,你无法改变它们.
Haskell是一种纯函数式编程语言.这意味着根本无法进行任何更改.
列表是非抽象类型,它只是一个链表.
您可以这样定义它们:
data [a] = a : [a] | []
Run Code Online (Sandbox Code Playgroud)
这正是链接列表的定义方式 - 头部元素和(其指向)其余部分.
请注意,这在内部没有区别 - 如果您想要更高效的类型,请使用Sequence或Array.(但由于不允许进行任何更改,因此您无需实际复制列表以区分副本,这可能是性能增益而不是命令式语言)
在Haskell中,"数据类型"和"抽象类型"是艺术术语:
"数据类型"(非抽象)具有可见值构造函数,您可以在case表达式或函数定义中进行模式匹配.
"抽象类型"没有可见值构造函数,因此您无法对类型的值进行模式匹配.
给定一个类型a,[a](list of a)是一种数据类型,因为你可以在可见的构造函数cons(书面:)和nil(书面[])上进行模式匹配.抽象类型的一个例子是IO a,你无法通过模式匹配来解构.