我找到了一些代码
data Tree c a = Node String [Tree c a]
| NodeWithCleanup c [Tree c a]
| Leaf a
我不明白为什么有必要添加[Tree c a]
。我不知道这个语法,你能解释一下吗?
答案 0 :(得分:5)
[]
在Haskell列表(概念上是链接列表)中,类型为[]
。列表只能包含一个类型的元素(因此列表不能同时包含Int
和String
。
如果列表因此包含a
类型的元素,那么我们将其表示为[a]
。例如,Int
的列表表示为[Int]
。
注意:这种语法实际上是语法糖。如果你写下
[a]
,在窗帘后面你实际上写了[] a
。
在你引用的代码片段中,程序员定义了一个类型Tree
,该类型有两个类型参数c
(“清理”的类型)和a
( “叶子”的类型。这意味着类型Tree c a
是一种类型,其中c
是清理类型,a
是叶类型。
如果我们想要构建此类Tree
的列表,我们会写[] (Tree c a)
或更方便的[Tree c a]
。
程序员已经定义了三个数据构造函数。数据构造函数可以看作是附加到对象的标签,它们将“参数”绑定在一起。数据构造函数具有的参数数量以及类型可能会有所不同。
在您的代码片段中有三个数据构造函数:
Node
一个带有两个参数的数据构造函数:String
和Tree c a
s列表(其子项); NodeWithCleanup
一个带有两个参数的dataconstructor:一个c
(清理)和一个Tree c a
s列表(其子项);和Leaf
具有单个参数的数据构造函数:它存储的数据(类型a
)。答案 1 :(得分:3)
与Haskell中的大多数“语法”一样,这些[]
根本不是特殊的语法†。构造函数声明只列出要包含的类型。如果你添加唱片标签可能会更清楚:(我会在这里指出“清理”部分)
data Tree a
= Node { nodeCaption :: String
, subtrees :: [Tree c a] }
| Leaf { leafContent :: a }
这基本上就像两个Python类:
class TreeNode:
def __init__(self, caption, subs):
self.nodeCaption = caption
self.subtrees = subs
class TreeLeaf:
def __init__(self, content):
self.leafContent = content
......打算像
一样构建TreeNode("foo", [TreeNode("bar1", TreeLeaf(1)), TreeNode("bar2", TreeLeaf(2))])
在Haskell实现中,您只需编写
Node "foo" [Node "bar1" (Leaf 1), Node "bar2" (Leaf2)]
为此。
† 方括号是特殊语法,因为它们是为列表保留的,但无论你是否用它们编写它们,都是这样做的函数的类型签名或数据声明。
答案 2 :(得分:2)
在定义值构造函数K
时,符号K T1 T2 .. Tn
表示K
是一个构造函数,它采用n
个值,第一个是类型T1
等等。
在Node String [Tree c a]
中,我们可以看到Node
有两个参数。第一个是字符串(String
)。第二个是树的列表([Tree c a]
)。因此,节点包括字符串和子树列表。
相反,NodeWithCleanup c [Tree c a]
表示带清理节点包含类型c
的值和子树列表。
Leaf a
表示叶子包含a
类型的单个值。
答案 3 :(得分:0)
这不是语法问题;这是一个语义问题。 Leaf
值只包含a
类型的值。另外两个构造函数包含Tree
值的列表,使其成为递归数据结构。 Tree c a
可以是叶节点(例如Leaf 3
),也可以是具有任意子树数的内部节点(例如Node "foo" [Leaf 1, Leaf 3, (Node "bar" []]
)。