haskell中的递归数据类型

MK.*_*MK. 5 c haskell pointers type-safety algebraic-data-types

请考虑从http://www.haskell.org上的教程中获取的以下定义:

data Tree a                = Leaf a | Branch (Tree a) (Tree a) 
fringe                     :: Tree a -> [a]
fringe (Leaf x)            =  [x]
fringe (Branch left right) =  fringe left ++ fringe right
Run Code Online (Sandbox Code Playgroud)

我不清楚函数条纹执行时在运行时会发生什么.

在编译表达式时fringe left,(1)编译器是否已经知道left树是a Branch还是a Leaf- 即它只对静态已知的树进行操作 - 或者(2)它是否发出一些if/ switch类似条件来检查left树是否为a Leaf或者一个Branch

如果它是后面的ie(2)那么,为什么这应该比等效的C函数更加类型安全,它基本上看起来就像上面那样,只是只有一种类型浮动(指向节点的指针).

Cac*_*tus 11

这个:

fringe (Leaf x)            =  [x]
fringe (Branch left right) =  fringe left ++ fringe right
Run Code Online (Sandbox Code Playgroud)

完全等同于单个变量的函数,然后立即执行模式匹配:

fringe t = case t of
    Leaf x            -> [x]
    Branch left right -> fringe left ++ fringe right
Run Code Online (Sandbox Code Playgroud)

所以这回答你的第一个问题为(2):"它发出一些case类似的条件来检查左边的树是a Leaf还是a Branch".

至于为什么它比你在C中做的更安全,那么,你会用C做什么?

通常你最终存储产品的一个标签,它表明,如果事情是LeafBranch,以及有效载荷这是不带标签的工会a(Tree a, Tree a).然后,您编写的代码沿着以下行(这可能不是100%合法C,但应该得到重点):

enum TreeTag { Tree_Leaf; Tree_Branch; };

struct Tree {
  TreeTag tag;
  union payload {
    struct { 
      int x;    // yeah let's not touch on parametric polymorphism here...
    } Leaf;
    struct {
      Tree l;
      Tree r;
    } Branch;
  };
};


switch (t.tag) {
  case Tree_Leaf: ... use t.payload.Leaf.x here
  case Tree_Branch: ... use t.payload.Branch.left and t.payload.Branch.right here
}
Run Code Online (Sandbox Code Playgroud)

问题是没有任何东西可以静止地阻止你t.payload.Branch.leftTree_Leaf案件中意外使用等等.此外,没有任何东西可以静静地阻止你做类似的事情

t.tag = Tree_Branch;
t.payload.Leaf.x = 42;
Run Code Online (Sandbox Code Playgroud)

这会导致"类型"的值无效Tree.

  • 此外,在c中,您不会受益于任何耗尽/冗余检查,这可以确保您不会忘记任何情况 (4认同)