recursion - 与递归数据类型的统一

标签 recursion types sml unification

遵循 suggestion要对像树这样的嵌套结构使用递归数据类型,我试图使上述递归数据类型在测试程序中工作,但遇到了(对我来说又一个非常神秘的)错误。

我的程序是这样的:

datatype 'a tree =
  Leaf of { value : 'a }
| Node of { value : 'a, left: 'a tree, right: 'a tree }


fun recursivetreebuilder a n =
  if n = 0
  then
      Leaf a
  else
      Node (a, recursivetreebuilder(a, n-1), recursivetreebuilder(a, n-1))

因此,该函数应该构建深度为 n 的二叉树,通过递减的 n 递归调用自身,直到 n 为0.

但我收到此错误:

Can't unify {left: 'a tree, right: 'a tree, value: 'a} with {value: 'b} *
(Int32.int/int -> 'c) * (Int32.int/int -> 'c) (Field 1 missing) Found near if
<( n, 0) then Leaf(a) else Node( a, recursivetreebuilder( ...), ......)

使用递归数据类型的目的是解决使用嵌套列表时的另一个统一问题。也许我应该能够看到问题在哪里给出了我的其他问题的解释,但我还没有。

编译器指的是什么“字段 1”?当递归数据类型旨在使其能够统一同一数据类型的不同“子类型”时,为什么它不能统一?

编辑

尝试了几种建议的结构,但仍然出现错误。例如对于

datatype 'a tree =
     Leaf of 'a
     |  Node of 'a tree * 'a tree


fun recursivetreebuilder a n =
  if n < 0
  then
      Leaf (a)
  else
      Node (recursivetreebuilder(a, n-1), recursivetreebuilder(a, n-1))

我明白了

val printList = fn : Int.int list -> unit
Error- in 'recon_bintree.sml', line 12.
Can't unify 'a with 'a * Int32.int/int (Type variable to be unified occurs in type) Found near if
<( n, 0) then Leaf(a) else
Node( recursivetreebuilder( a, ...), recursivetreebuilder( ...))
Error- in 'recon_bintree.sml', line 12.
Can't unify 'a with 'a * Int32.int/int (Type variable to be unified occurs in type) Found near if
<( n, 0) then Leaf(a) else
Node( recursivetreebuilder( a, ...), recursivetreebuilder( ...))
Error- in 'recon_bintree.sml', line 12.
Can't unify 'a tree with Int32.int/int -> 'b (Incompatible types) Found near if
<( n, 0) then Leaf(a) else
Node( recursivetreebuilder( a, ...), recursivetreebuilder( ...))
Error- in 'recon_bintree.sml', line 12.
Can't unify 'a tree with Int32.int/int -> 'b (Incompatible types) Found near if
<( n, 0) then Leaf(a) else
Node( recursivetreebuilder( a, ...), recursivetreebuilder( ...))
Exception- Fail "Static errors (pass2)" raised

最佳答案

这里有两个问题。

第一个问题是 - 例如 - { value : 'a, left: 'a tree, right: 'a tree } 是记录类型,而 (a, recursivetreebuilder (a, n-1), recursivetreebuilder(a, n-1)) 是一个元组而不是一条记录。所以他们不匹配;这就像将 real 传递给需要 int 的函数。

(迂腐的一边:从技术上讲,元组实际上记录,但是非常具体;(a, b, c){ 1 = a的语法糖, 2 = b, 3 = c }。对于大多数实际目的,您可以将元组和记录视为组合类型的两种相似但完全独立的方式。但现在您知道为什么会出现错误消息引用“字段 1”。)

第二个问题是您声明函数使用柯里化(Currying) (fun recursivetreebuilder a n = ...),但随后您尝试使用元组调用它 (recursivetreebuilder( a,n-1))。


一种方法是坚持您的数据类型定义,并使用柯里化(Currying)保留函数,并更改所有内容以匹配这些决策:

datatype 'a tree =
  Leaf of { value : 'a }
| Node of { value : 'a, left: 'a tree, right: 'a tree }

fun recursivetreebuilder a n =
  if n = 0
  then
      Leaf { value = a}
  else
      Node { value = a,
             left = recursivetreebuilder a (n-1),
             right = recursivetreebuilder a (n-1) }

或者更改数据类型定义以消除记录类型,并更改函数以消除柯里化(Currying):

datatype 'a tree =
  Leaf of 'a
| Node of 'a * 'a tree * 'a tree

fun recursivetreebuilder (a, n) =
  if n = 0
  then
      Leaf a
  else
      Node (a, recursivetreebuilder(a, n-1), recursivetreebuilder(a, n-1))

或混合搭配以上内容。 (记录与元组问题的修复独立于柯里化(Currying)与元组问题的修复。)


顺便说一句,我认为在 Leaf 情况和 Node 情况下都包含一个值是错误的。根据您当前的定义,树不可能恰好包含 0 个或恰好 2 个元素。

相反,我认为你应该有空叶子:

datatype 'a tree =
  Leaf
| Node of 'a * 'a tree * 'a tree

或者有子节点但没有自己的值的节点:

datatype 'a tree =
  Leaf of 'a
| Node of 'a tree * 'a tree

或者消除叶子和节点之间的区别,并使子节点可选:

datatype 'a tree =
   Node of 'a * 'a tree option * 'a tree option

关于recursion - 与递归数据类型的统一,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36830988/

相关文章:

haskell - 将 ML 风格的模块添加到 Haskell 的主要理论困难是什么?

c++ - C++ 中的设置数组

c++ - Cmake 无法从单独的文件 C++ 链接一个类

scala - 具有抽象类型成员的具体类

binary - 如何在标准 ML 中检查整数的以 2 为底的表示形式?

algorithm - 快速船体最坏情况解释

java - 递归创建目录

java - 用Java实现自定义快速排序算法

c# - 如何 : "letrec" in C# (lambda expression call within its definition)

sml - 如何写入sml文件