【问题标题】:unification with recursive datatypes与递归数据类型的统一
【发布时间】:2016-04-25 01:58:27
【问题描述】:

按照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 的二叉树,通过递减的ns 递归调用自身直到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

【问题讨论】:

    标签: recursion types sml unification


    【解决方案1】:

    这里有两个问题。

    第一个问题是——例如——{ 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”。)

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


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

    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) }
    

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

    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))
    

    或混合搭配以上。 (record-vs.-tuple 问题的修复独立于 currying-vs.-tuple 问题的修复。)


    顺便说一句,我认为在LeafNode 情况下都包含一个值是错误的。根据您当前的定义,不可能有一棵包含正好 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
    

    【讨论】:

    • 好吧,我明白了……或多或少。我尝试实施您建议的第二个结构,但我仍然遇到统一错误......我在那里错过了什么?我更新了问题。
    • @lotolmencre:很抱歉。你还有另一个问题,我没有注意到。我现在更新了答案来解释这两个问题。 (这次我已经测试过了。)
    猜你喜欢
    • 2014-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-15
    • 2016-09-20
    • 2017-09-21
    • 1970-01-01
    相关资源
    最近更新 更多