-11
data BTree a = Empty | Node (BTree a) a (BTree a) -- This is a node-labelled binary tree

有人可以解释一下以下 Haskell 函数吗?

  1. labels :: BTree a -> [a]

    labels Empty = []
    labels (Node left label right) = labels left ++ [label] ++ labels right
    
  2. reflect :: BTree a -> BTree a

    reflect Empty = Empty
    reflect (Node left label right) = Node (reflect left) label (reflect right)
    
4

1 回答 1

5

第一:一些格式。这就是这段代码在 Haskell 中的典型格式:

data BTree a = Empty 
             | Node (BTree a) a (BTree a)

labels :: BTree a -> [a]
labels Empty                   = [] 
labels (Node left label right) = labels left ++ [label] ++ labels right

reflect :: BTree a -> BTree a
reflect Empty                   = Empty 
reflect (Node left label right) = Node (reflect left) label (reflect right)

(提示:如果您将代码缩进 4 个空格,它将正确显示突出显示的语法)。现在让我们来看看它:

data BTree a = Empty 
             | Node (BTree a) a (BTree a)

定义新的“参数”数据类型。它被称为参数,因为a它是一个类型参数。这意味着a可以用任何其他类型替换,例如IntDoubleString或其他类型。想想 C++ 中的模板或 Java 中的泛型。Empty并且Node被称为数据类型的构造函数。ABTree a可以是Empty或(这就是|符号)包含 a Node,其中包含 aBTree aaanother BTree a。该定义是递归的,因为数据类型 ( BTree a) 显示在它自己的定义中。

labels :: BTree a -> [a]
labels Empty                   = [] 
labels (Node left label right) = labels left ++ [label] ++ labels right

labels收集树中包含的所有值。a第一行是类型声明:它采用带有节点 ( )的二叉树BTree a并将其映射到as ( [a]) 列表。单独的类型已经让您对可能发生的事情有一个很好的了解。

接下来的两行是所谓的模式匹配。这些类似于case其他语言中的语句:您区分不同的可能性,然后选择适当的情况(尽管它们更强大)。您应该注意它们如何与具有的两个构造函数完全对应BTree a。如果我们在一个Empty节点上,那么我们将只返回一个空列表 ( [])。否则,我们将进入下一行,并且 a Nodewhich 必须有 aBTree aaaBTree a我们绑定到leftlabelright。我们可能会调用leftlabel以及right任何我们想要的,但这些都是直观的。

现在leftright又是类型BTree a,所以我们可以调用labels两者并期望它们返回一个as 列表,即[a]. 所以标签也是递归的,因为它在定义中调用了自己。这是一个非常强大的技术,在 Haskell 中被大量使用。labels然后连接它得到的列表labels left,一个只包含当前标签 ( [label]),然后一个它来自labels right。所以我们可以有效地得出结论,它将左子树的标签与当前标签和右子树的标签连接起来,并将它们全部放入一个列表中。

reflect :: BTree a -> BTree a
reflect Empty                   = Empty 
reflect (Node left label right) = Node (reflect left) label (reflect right)

工作方式与 几乎相同labels,只是它返回标签树而不是列表。如此有效,这无济于事,它有点昂贵的身份功能。但它是更强大的东西的模板。例如,您可以很容易地传递另一个函数reflect并将其应用于其每个元素。

于 2013-01-08T12:26:32.967 回答