我正在从 Java 背景学习 Haskell。 当我编写 Java 程序时,我觉得我对对象在内存中的布局及其后果有深入的了解。 例如,我确切地知道
java.lang.String
和 java.util.LinkedList
是如何工作的,因此我知道应该如何使用它们。 对于 Haskell,我有点迷失了。 例如,(:)
是如何工作的? 我应该关心吗? 有指定的地方吗?
简短的回答是否定的。在 Haskell 中编程时,您应该将数据结构视为纯数学对象,而不用担心它们在内存中的表示方式。原因是,在没有副作用的情况下,除了创建数据的函数以及可用于提取构造数据的更简单部分的函数之外,实际上没有任何数据。 要查看有关数据构造函数(如
(:)
)或任何其他术语的信息,请在 GHCi 中使用
:type
(或简称 :t
)命令::Prelude> :type (:)
(:) :: a -> [a] -> [a]
这告诉您
(:)
构造函数(发音为“cons”)接受任何类型的值和相同类型的列表,并返回相同类型的列表。您还可以使用
:info
命令获取更多信息。这将向您展示数据定义的样子:Prelude> :info (:)
data [] a = ... | a : [a] -- Defined in GHC.Types
infixr 5 :
这告诉您
(:)
是将元素添加到现有列表的构造函数。
我还强烈推荐Hoogle,不仅可以通过名称查找事物,还可以进行相反的搜索;您知道您正在寻找的函数的签名,并且想要查找是否有人已经为您编写了该函数。 Hoogle 很好,因为它提供了描述和示例用法。 归纳数据的形状
data MyList a = Nil | Cons a (MyList a)
从定义中可以看出,获得新的
MyList
的唯一方法是通过
Cons
构造函数。如果你多次使用这个构造函数,你最终会得到大致如下形状的东西:
(Cons a5 (Cons a4 (Cons a3 (Cons a2 (Cons a1 Nil)))))
它只是一棵没有分支的树,这就是列表的定义!获得 a1
的唯一方法是依次弹出每个
Cons
;因此,访问最后一个元素的时间为
O(n),而访问头部的时间为常数。一旦您可以根据数据结构的定义对数据结构进行此类推理,您就一切就绪了。
简短的回答是“不”,您不需要了解数据布局——但了解复杂性会很有用。