在列表上进行递归时出现SML [circularity]错误

时间:2018-02-15 03:08:51

标签: sml type-inference smlnj

我试图构建一个拉动2个给定函数的函数,忽略较长列表的长度。

fun zipTail L1 L2 = 
    let 
      fun helper buf L1 L2 = buf
        | helper buf [x::rest1] [y::rest2] = helper ((x,y)::buf) rest1 rest2
    in
      reverse (helper [] L1 L2)
    end

当我这样做时,我收到了错误消息:

  

错误:条款的右侧与功能结果类型[圆形度]不一致

我很好奇,因为循环错误是什么,我应该如何解决这个问题。

2 个答案:

答案 0 :(得分:3)

这里有很多问题

1)在helper buf L1 L2 = buf中,模式buf L1 L2将匹配所有可能的输入,使下一个子句(一旦调试)变为冗余。在上下文中,我认为你的意思是helper buf [] [] = buf,但是在不同大小的列表的情况下,你会遇到非穷举匹配的问题。最简单的解决方法是将第二个子句(带有x::rest1的子句)移动到顶行,然后使用第二个模式来捕获其中至少有一个列表为空的情况。

2)[xs::rest]是一个模式,它匹配项目为非空列表的1项目列表。那不是你的注意力。您需要使用(,)而不是[,]

3)reverse应为rev

进行这些更改后,您的定义将变为:

fun zipTail L1 L2 = 
let 
    fun helper buf (x::rest1) (y::rest2) = helper ((x,y)::buf) rest1 rest2
      | helper buf rest1 rest2 = buf

in
    rev (helper [] L1 L2)
end;

符合预期。

错误信息本身有点难以理解,但您可以这样想。在

helper buf [x::rest1] [y::rest2] = helper ((x,y)::buf) rest1 rest2

左侧括号中的内容是列表清单。因此,他们的类型为'a list list,其中'ax的类型。在x::rest1中,rest1的类型必须为'a list,因为rest1也会出现在与[x::rest1]相同位置的等号的另一侧, rest1的类型必须与[x::rest1]的类型相同,即'a list list。因此,rest1必须同时为'a list'a list list,这是不可能的。

如果您尝试理解'a list list = 'a list,则需要使用'a 'a = 'a list类型StringBuilder sample = new StringBuilder(); sample.append("DDD,FFFFFF,KKKK,LLLL).append("|") .append("KKK,FFFFF,KKK,LLLL"); 。这将是一个类型,其值由相同类型的值列表组成,并且该列表中的项的值本身必须是相同类型的元素列表...它是一个永远不会结束的粘性循环。

答案 1 :(得分:1)

圆形问题显示为many other places

您想要(x::rest1)而不是[x::rest1]

问题是语法上的误解。

  • 模式[foo]将匹配其中只包含一个元素的列表foo
  • 模式x::rest1将与列表中至少包含一个元素x及其(可能为空)尾rest1匹配。 这是您想要的模式。但模式包含一个中缀运算符,因此您需要在其周围添加一个括号。
  • 组合模式[x::rest1]将与完全一个元素的列表匹配,该元素本身是至少一个元素的列表。这种模式虽然过于具体,但仍然有效,并且本身不会引发类型错误。

您遇到循环错误的原因是编译器无法推断出rest1的类型。因为它出现在::模式构造函数的右侧,它必须是'列表,并且它本身就会发生,它必须是'a < / em>的。尝试unify 'a = '列表就像找到方程 x = x + 1 的解决方案。

你可能会说“好吧,只要'a ='列表列表列表列表...... 无限,如∞=∞+ 1 ,那就是解。”但Damas-Hindley-Milner type system并未将此无限构造视为明确定义的类型。创建单例列表[[[...x...]]]需要无限量的括号,因此无论如何它都不是完全实用的。

一些简单的圆形例子:

  • fun derp [x] = derp x:这是您的情况的简化,其中derp的第一个参数中的模式表示列表,而x表示元素的类型此列表必须与列表本身的类型相同。

  • fun wat x = wat [x]:这是一个非常类似的情况,其中wat采用'a 类型的参数,并使用'类型的参数调用自身列表。当然,'a 可以是'列表,但是'列表必须是'列表列表等等

正如我所说,由于语法上的误解,你得到了循环。列表模式。但循环不仅限于列表。它们是组合类型和自我引用的产物。这是一个没有列出Function which applies its argument to itself?列表的例子:

  • fun erg x = x x:在这里,x可以被认为是以'a 开头的类型,但是看到它作为一个函数应用到自身,它还必须有输入'a - &gt; “B 。但如果'a ='a - &gt; 'b ,然后'a - &gt; b =('a - &gt;'b) - &gt; 'b ('a - &gt;'b) - &gt; b =(('a - &gt;'b) - &gt; b) - &gt; b ,依此类推。 SML编译器很快就会确定这里没有解决方案。

这并不是说具有循环类型的函数总是无用的。作为newacct points out,将纯粹的匿名函数转换为递归函数实际上需要这样,就像在Y-combinator中一样。

内置ListPair.zip 顺便说一句是usually tail-recursive