【问题标题】:SML [circularity] error when doing recursion on lists对列表进行递归时出现 SML [循环性] 错误
【发布时间】:2018-02-15 03:08:51
【问题描述】:

我正在尝试构建一个压缩 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

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

错误:子句右侧与函数结果类型不符 [循环性]

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

【问题讨论】:

    标签: sml type-inference smlnj


    【解决方案1】:

    这里有很多问题

    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。这将是一种类型,其值由相同类型的值列表组成,并且该列表中的项目的值本身必须是相同类型元素的列表......这是一个永无止境的粘性循环.

    【讨论】:

    • 谢谢!真的很有帮助
    【解决方案2】:

    循环问题出现manyotherplaces

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

    问题在于语法错误。

    • 模式[foo] 将匹配其中只有一个元素foo 的列表。
    • 模式x::rest1 将匹配一个包含至少一个元素x 及其(可能为空)尾部rest1 的列表。 这是你想要的模式。但是模式包含一个中缀运算符,所以你需要在它周围添加一个括号。
    • 组合模式[x::rest1] 将匹配具有恰好一个元素的列表,该列表本身就是具有至少一个元素的列表。这种模式是有效的,尽管过于具体,并且本身不会引发类型错误。

    出现循环错误的原因是编译器无法推断 rest1 的类型。因为它出现在:: 模式构造函数的右侧,所以它必须是'a list,并且由于它是单独出现的,它必须是'a。尝试unify 'a = 'a list 就像找到方程 x = x + 1 的解。

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

    一些更简单的循环示例:

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

    • fun wat x = wat [x]:这是一个非常相似的情况,wat 采用 'a 类型的参数并使用 'a 列表类型的参数调用自身时间>。自然地,'a 可以是 'a list,但是 'a list 也必须是 'a list list em> 等

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

    • fun erg x = x x:这里,x 可以被认为具有 'a 类型,但将其作为函数应用于自身,它也必须具有类型 'a - >'b。但是如果'a = 'a -> 'b,那么'a -> b = ('a -> 'b) -> 'b,和 ('a -> 'b) -> b = (('a -> 'b) -> b) -> b,以此类推。 SML 编译器很快就会确定这里没有解决方案。

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

    内置ListPair.zip 顺便说一句,是usuallytail-recursive

    【讨论】:

    • 这个特定问题似乎与您链接到的问题有点不同(尽管它是相同的潜在问题)。我认为我之前没有看到过 Stack Overflow 上的这个错误以这种方式触发(通过错误地分组 x::xs)。
    • @JohnColeman:你是对的。我已经看过很多次了wrt。我认为我之前在 StackOverflow 上也看到过这个教学环境中的列表。我已经更改了措辞,以免表明这是一个重复的问题。这绝对值得一个答案。
    猜你喜欢
    • 2011-12-22
    • 2017-01-30
    • 1970-01-01
    • 2021-04-04
    • 1970-01-01
    • 1970-01-01
    • 2014-05-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多