Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.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_Functional Programming_Algebraic Data Types - Fatal编程技术网

Haskell 如何在没有嵌套列表的情况下编写与树同构的类型?

Haskell 如何在没有嵌套列表的情况下编写与树同构的类型?,haskell,functional-programming,algebraic-data-types,Haskell,Functional Programming,Algebraic Data Types,在Haskell中,据说任何ADT都可以表示为乘积之和。我试图在Data.Tree上找到一个与Tree同构的平面类型 Tree a = Node a [Tree a] -- has a nested type (List!) 我想为没有嵌套类型的树编写功能相同的定义: Tree = ??? -- no nested types allowed 为此,我尝试为类型代数编写递归关系: L a = 1 + a * L a T a = a * L (T a) 内联线L,我有: T a = a *

在Haskell中,据说任何ADT都可以表示为乘积之和。我试图在
Data.Tree
上找到一个与
Tree
同构的平面类型

Tree a = Node a [Tree a] -- has a nested type (List!)
我想为没有嵌套类型的树编写功能相同的定义:

Tree = ??? -- no nested types allowed
为此,我尝试为类型代数编写递归关系:

L a = 1 + a * L a
T a = a * L (T a)
内联线L,我有:

T a = a * (1 + T a * L (T a))
T a = a * (1 * T a * (1 + T a * L (T a)))
T a = a * (1 + T a * (1 + T a * (1 + T a * L (T a))))
那是行不通的,所以我停下来做乘法,留下:

T a = a + a * T a + a * T a * T a ...
这与:

T a = a * (T a) ^ 0 + a * (T a) ^ 1 + a * (T a) ^ 2 ...
这是一个乘积之和,但它是无限的。我不能用哈斯克尔的话写。滥用代数:

(T a) - (T a) ^ 2 = a * T a
- (T a) ^ 2 - a * T a + (T a) = 0
解决
ta
,我发现:

T a = 1 - a
这显然毫无意义。那么,回到原来的问题:如何从
Data.Tree
展平
Tree
,这样我就可以编写一个与之同构的类型,而不需要嵌套类型

这个问题不是重复的。最后一个是关于用Scott编码表示嵌套类型,正确答案是“忽略嵌套”。这一部分继续询问如何展平嵌套类型(因为它是Scott编码的特定用途所必需的,但通常不是强制性的)

T a = a * (1 + T a * L (T a))
你可以继续

    = a + a * T a * L (T a)   -- distribute
    = a + T a * (a * L (T a)) -- commute and reassociate
    = a + T a * T a           -- using your original definition of T backwards
那么你到了

data Tree a = Leaf a | InsertLeftmostSubtree (Tree a) (Tree a)

然而,我不确定这在多大程度上是一个普通程序的例子。

在阅读任何答案之前,我认为这是一个有趣的谜题,并得出了与公认答案相当的答案:

data Tree' a = Node a [Tree' a] deriving (Show, Eq, Ord)
data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving (Show, Eq, Ord)
除此之外,我还以似乎唯一可能的方式编写了转换函数:

convert :: Tree' a -> Tree a
convert (Node x [])     = (Leaf x)
convert (Node x (t:ts)) = Branch (convert t) $ convert (Node x ts)

convert' :: Tree a -> Tree' a
convert' (Leaf x)      = Node x []
convert' (Branch t ts) = Node x $ convert' t : subtrees
  where (Node x subtrees) = convert' ts
这些函数的实现并不是正确性的证明,但它们的类型检查、没有非穷举模式匹配,并且似乎适用于简单输入(即
convert.convert'==id
),这有助于表明数据类型彼此同构,这是令人鼓舞的

至于如何构建这样一个东西的一般结构,我的理解与公认答案中的类型代数不同,因此我的思维过程可能有助于推导出一个通用方法。主要的是注意到,
[x]
可以按照通常的方式转换为
数据列表a=Nil | Cons a(列表a)
。因此,我们需要将该转换应用于
[Tree'a]
字段,同时还保留
[Tree'a]
上方的额外
a
。因此,我的
叶a
自然地与
Nil
等价,但有一个额外的
a
;然后
分支
构造函数类似于
Cons b(列表b)

我认为您可以对任何包含列表的数据构造函数执行类似的操作:给定
构造函数ab[c]
,将其转换为新类型中的两个构造函数:

data Converted a b c = Nil a b | Cons c (Converted a b c)
如果旧构造函数中有两个列表,则可以为每个列表指定一个构造函数,只需将单个项添加到其中一个列表中:

data Old a b c = Old a [b] [c]

data New a b c = Done a
               | AddB b (New a b c)
               | AddC c (New a b c)

虽然我想我知道你想要什么(也许里德已经解决了),你能在一个平面数据结构下添加你所理解的吗?(因为你似乎不介意递归,这让我感到惊讶)具体地说,我希望它可以表示为一个乘积的和,是的,可能是,在绑定变量上递归。例如:
data Rec a=aa(Rec a)| ba | ca(Rec a)a | da
,完全有效。但是在另一个递归过程(比如这里的
[treea]
)上进行递归是不行的。我之所以要这样做,是因为我正在使用Scott编码在Lambda演算上对Haskell数据类型进行编码,而Scott编码不考虑嵌套类型,因此我必须在不嵌套的情况下进行编码。@Viclib
Tree
对于Scott编码来说似乎没有问题。不应该工作吗?安德拉斯,当你试图用规范化术语来折叠它们时就不行了,就像你在另一个线程中证明的那样。为此,我必须用递归调用扩展编码,这样cons就变成了
(head-tail-cons-nil)→ cons cons nil头尾)
而不是
(头尾cons nil→ cons(头尾)
。通过使用它,我可以通过将任何数据类型表示为
Scott:[[Field]]]
,从而为任何数据类型导出一个规范化的
折叠,其中
(Field::Bool)==true,如果字段是递归的
。例如,通过这种方式,列表被表示为
[[false-true][]]
。我不确定这是唯一的办法。有什么想法吗?很好,谢谢。虽然它显然解决了特定的问题,但它让我感到好奇,因此,如果有人愿意,我很高兴听到为什么这是可行的,如果这总是可以做到:)非常感谢你解释你的思维过程,这是非常有洞察力的,对我所做的事情帮助很大。