真实世界Haskell第3章练习:带1值构造函数的二叉树-后续

真实世界Haskell第3章练习:带1值构造函数的二叉树-后续,haskell,tree,maybe,Haskell,Tree,Maybe,这个问题不是重复的 ,但在我看来,这只是部分解决了这个问题,我也对它没有解决的问题感兴趣 前言 现实世界Haskell在第3章第58页中提出了二叉树数据类型的以下定义 data Tree a = Node a (Tree a) (Tree a) | Empty deriving (Show) 它提供了两个构造函数(用于空的和非空的Trees) 另一方面,在第60页,一个练习要求读者使用单个构造函数定义树数据类型 经过几次尝试,我提出了与上面链

这个问题不是重复的

,但在我看来,这只是部分解决了这个问题,我也对它没有解决的问题感兴趣

前言

现实世界Haskell在第3章第58页中提出了二叉树数据类型的以下定义

data Tree a = Node a (Tree a) (Tree a)
            | Empty
              deriving (Show)
它提供了两个构造函数(用于空的和非空的
Tree
s)

另一方面,在第60页,一个练习要求读者使用单个构造函数定义
数据类型

经过几次尝试,我提出了与上面链接的解决方案相同的解决方案:

data Tree a = Node a (Maybe (Tree a)) (Maybe (Tree a)) deriving(Show)
链接问题中未回答的问题

此定义的缺点是,它不允许实例化空的
,尽管它允许通过以下语法实例化具有空子级的

Node 3 Nothing (Just (Node 2 Nothing Nothing))
我认为没有比上面更好的解决方案了,如果没有一个“独立的”空树是可以接受的,并且要求只使用一个构造函数

对上述声明发表一些评论会很好;然而,我的主要问题是如何用一个构造函数定义
,以便实例化一个空的

现在我已经写了这个问题,我认为一个可能的答案是以下,我一点也不确定:

如果一个子节点是空的或不是空的,则不管它是通过
Nothing
还是
Just(Node…
)构造的,对整个树(或根节点)来说几乎是一样的,它实际上可以被定义为
Nothing
Just(Node…
;也就是说,只有一个构造函数,
Nothing
是实例化空树的方法。(换句话说,我刚开始认为这个问题本质上是“格式不正确的”。没什么,我会发布它,因为我想我可以从你的评论/回答中学到一些东西。)

以上这些有意义吗

可能的答案

原始问题中的评论提出了以下解决方案

data Tree a = Tree (Maybe (a,Tree a,Tree a))

这(我的理解)允许通过
Node Nothing
实例化emtpy树,或者通过
Node(Just(value,child1,child2))
实例化非空树。这里有一个提示:您可以使用n元组类型将任何n元构造函数转换为1元构造函数

例如,您的树类型同构于以下类型:

data Tree a = Node (a, Tree a, Tree a)
            | Empty

我认为你现在应该能够将这种类型转化为只涉及一个构造函数的类型。

关于@chi的回答以及他的评论说明了一切,它可以通过以下两种方式完成:

data Tree a = T (Either () (a, Tree a, Tree a)) deriving Show
树的一个例子是:

node1 = T $ Right ("data node 1", node2, node3)
node2 = T $ Left ()
node3 = T $ Right ("data node 3", node2, node2)

  $> node1
T (Right ("data node 1",T (Left ()),T (Right ("data node 3",T (Left ()),T (Left ())))))

但是每个人都已经说过,这可以用Maybe代替,因为
other()
可以被视为
Maybe a

我想你指的是我刚才在回答中提到的解决方案。即使它是有效的,我发现它有点“笨重”。@EnricoMariaDeAngelis啊,对。我错过了。它有什么笨重的地方?我认为没有其他简单的方法了。从类型理论的角度来看,标准树定义为
树a=1+(a*树a*树a)
,其中
+
表示总和类型,
*
表示产品类型,
1
表示单位类型。在Haskell中,这可以转化为
treeA=other()(a,treeA,treeA)
,而
other()
最好写成
Maybe
。我的类型理论水平相当低(如果不是零的话),因此我可能在将来的某个时候会回到你的评论。然而,如果您能澄清最后一部分,那就太好了:
other()
最好写成
Maybe
;我的理解是,
Maybe
other()
都是类型执行器,它们使用一个具体的类型
a
来产生一个类型
a
的值,或者某个值,其中某个值是
对于
Maybe
对于
或者
来说是
对的
,对于
可能
左(
左()
对于
或者()
,有些东西是
无(
)。所以在我看来,它们看起来是两种不同的东西(即使它们可以编码相同的信息)。@EnricoMariaDeAngelis你写的是正确的。它们是两种不同的类型,但编码的信息相同。在类型理论中,我们说它们是同构的,因为有两个函数
f::Maybe a->earth()a
g::earth()a->Maybe a
,它们是互逆的。在很多情况下,由于这个原因,我们并没有真正区分两者。实际上,在Haskell中,使用
或()a
是不寻常的,因为
也许a
可以做同样的工作,而且概念上更容易理解。从某种意义上说,这只是一个品味问题。
数据树a=Tree(可能是(a,Tree a,Tree a))
在原始定义中似乎没有指定
任何内容
,而
仅指定
节点
。这到底有什么好处?为什么你要发布一个只会重复别人所说内容的答案?@chepner总结并浓缩在一个答案中