Haskell函数寻求解释

时间:2013-01-08 12:00:21

标签: haskell functional-programming binary-tree

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)
    

1 个答案:

答案 0 :(得分: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中的泛型。 EmptyNode被称为数据类型的构造函数BTree a可以是Empty或(|符号表示的)包含Node,其中包含BTree aa以及另一个BTree a 1}}。定义是递归,因为数据类型(BTree a)显示在其自己的定义中。

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

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

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

现在leftright再次属于BTree a类型,因此我们可以同时调用labels并期望它们返回a的列表,即[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并将其应用于每个元素。