Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
树与否(Haskell类型理解)_Haskell_Types_Binary Tree_Graph Theory - Fatal编程技术网

树与否(Haskell类型理解)

树与否(Haskell类型理解),haskell,types,binary-tree,graph-theory,Haskell,Types,Binary Tree,Graph Theory,在“Haskell的温和介绍”中,我们对树的类型进行了这样的声明: data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving (Eq,Ord,Show,Read) 让我们制作一些这种类型的值: a1 = Leaf 1 a2 = Leaf 2 a3 = Leaf 3 a4 = a1 `Branch` a2 a5 = a2 `Branch` a3 a6 = a4 `Branch` a5 在ghci中: *Main> :t a

在“Haskell的温和介绍”中,我们对树的类型进行了这样的声明:

data Tree a = Leaf a | Branch (Tree a) (Tree a)
     deriving (Eq,Ord,Show,Read)
让我们制作一些这种类型的值:

a1 = Leaf 1
a2 = Leaf 2
a3 = Leaf 3
a4 = a1 `Branch` a2
a5 = a2 `Branch` a3
a6 = a4 `Branch` a5
在ghci中:

*Main> :t a6
a6 :: Tree Integer
但是
a6
根本不是树,请参见:

      a6
     / \
   a4   a5
  / \  /  \
a1   a2    a3
这张图中有一个循环! 怎么了?树的类型定义正确吗?
或者,在理解这个例子的过程中,我没有发现一些错误…

简单的回答是,从概念上来说,
a2
“是一个
树a
,而不是对
树a
的引用。从这个意义上说,
a6
看起来确实像

          a6
       /      \
     a4        a5
    /   \    /    \
  a1   a2   a2    a3
也就是说,树中有两个
a2
的“副本”

实际上,由于所有值都是不可变的,因此实现可以使用相同的内存来表示
a4
的右子级和
a5
的左子级,但这两个节点在
树a
类型表示的抽象级别上保持不同


为了真正有一个循环,需要有一种能够从
a2
到达
a4
a5
的概念,并且这种类型不提供任何这样的子到父链接的表示,这使得我们无法判断
a4
的左子节点和
a5
的右子节点是同一个节点,还是两个看起来完全相同的不同节点。对于这种数据类型,根本不存在区别。

我们应该区分表达式的值和它的内存表示形式

例如,这两个表达式具有相同的值:

事实上,在Haskell中,无法区分上述表达式的计算结果。它们在语义上是等价的

Haskell实现(例如GHC)可以自由地在内存中以任何不破坏语义的方式表示这些值。例如:

  • 它可能会在内存中存储两次字符串
    “Hello”
    ,然后使用一对指针
    (p1,p2)
  • 它可能将字符串
    “Hello”
    存储在内存中一次,然后使用一对指针
    (p,p)
  • 请注意,理论上,这两种表示都可以用于上述表达式
    e1、e2
    中的任何一种。实际上,GHC将前者用于
    e2
    ,后者用于
    e1
    ,但这并不重要

    在你们的树中,同样的问题也出现了。您的
    a6
    的值是一个树。GHC可能将该树表示为非树DAG(即,如果转换为无向图,则有一个循环的DAG),但这并不重要,它只是一个实现细节。重要的方面是,这种表示尊重树的语义

    人们可能会想,如果值是树,为什么DAG表示是合理的。这是因为在Haskell中,我们无法比较GHC使用的底层“引用”
    p
    。如果我们有一个比较这些引用的函数
    comparePtr::a->a->Bool
    ,我们可以通过使用
    comparePtr(fst e)(snd e)
    来区分
    e1
    e2
    之间的
    e
    。这将严重破坏执行的可靠性。不过,在哈斯凯尔,我们没有这种情况


    (从技术上讲,有一个
    不安全的
    函数可以做到这一点,但是
    不安全的
    函数永远不应该用在“正常”代码中。我们通常假装这些函数不存在。)

    它不是树或非树,因为一张纸上的图片暗示了这一点。为什么你认为你可以在图片中只使用一次a2?如果您更改程序,使所有节点都包含数字42,您会绘制42一次还是4次?内置数据类型和用户定义数据类型之间没有实质性差异,因此42与Leaf 42处于同等地位。为了证明这不是一棵树,您需要生成一个Haskell程序来检测这个“循环”。而你就是不能。Haskell的语义不允许这样的程序存在。非常感谢,伙计们!我现在明白我的误解是什么了!DAG中的A是“非循环的”——你不是说它是一个有循环的DAG,而是一个节点有两个父节点的DAG。它的内存表示可能不是一棵树,但它的语义值当然仍然是一棵树,正如您所说。无论如何,他们都没有一个循环。@amalloy说得对。我的意思是,当DAG转换成无向图时,它有一个循环。我使用了“循环”一词,因为OP在问题中是为了同一个概念,所以为了让OP更容易理解,我将添加更多细节。
    e1 = ("Hello", "Hello")
    e2 = let s = "Hello" in (s, s)