另一个答案是正确的,正确的解决方法是使用~-pattern(也称为无可辩驳的模式或惰性模式)或新类型,但让我们看看为什么。 ..
以下是您的定义再次供参考:
(>/>) :: Try -> Try -> Try
T [] >/> i = i
T ts >/> T qs = T (ts ++ qs)
infi2 r = T [r] >/> infi2 r -- I've omitted unnecessary parentheses
现在,如果您致电 infi2 1,则会发生以下缩减:
infi2 1
= { expanding the definition of infi2 }
T [1] >/> infi2 1
现在这是重点。我们要减少最外层的函数,
这是>/>。但我们必须决定哪些情况适用。这简单
看到第一个没有,因为T [1] 不匹配T []。然而,第二种情况需要>/> 的第二个参数的形状为T qs,而我们有infi2 1。即使Try 只有一个构造函数,GHC/Haskell 也不会做出这样的信念飞跃。相反,它将进一步评估infi2 1,直到它了解其最外层的构造函数。所以下一个减少步骤是
T [1] >/> infi2 1
= { expanding the definition of infi2 }
T [1] >/> (T [1] >/> infi2 1)
现在我们又陷入了完全相同的境地。我们仍然不能减少最外层的>/>,因为我们不知道正确参数的构造函数;所以我们必须进一步减少。但是,我们需要再次减少
右参数进一步了解内部>/>的右参数的构造函数:
T [1] >/> (T [1] >/> infi2 1)
= { expanding the definition of infi2 }
T [1] >/> (T [1] >/> infi2 1)
= { expanding the definition of infi2 }
T [1] >/> (T [1] >/> (T [1] >/> infi2 1))
= ...
这将无限期地继续,直到内存填满。我们永远做不到
真正的进步。
再次回顾最初的定义,(通过一些练习)实际上可以在不进行整个扩展的情况下看到这一点:
(>/>) :: Try -> Try -> Try
T [] >/> i = i
T ts >/> T qs = T (ts ++ qs)
infi2 r = T [r] >/> infi2 r
在>/> 定义的第二种情况下,我们仅在知道两个参数都是Ts 之后生成T。所以在infi2 r中,我们只能在infi2 r返回后减少外层>/>,但那是递归调用……
现在关于解决此问题的解决方案:
使用新类型
有
newtype Try = T [Int]
deriving (Show)
而不是data,T 上的模式匹配变成了无操作。新类型保证与底层类型(此处为 [Int])具有相同的运行时表示,并且应用构造函数 T 或模式匹配对转换类型有影响,但在运行时没有影响。
因此,一旦我们有
T [1] >/> infi2 1
为了对其中一个案例做出决定,我们现在只看到第一个列表是非空的,所以第一个案例不能适用。第二种情况是左侧
T ts >/> T qs = ...
在T 上的模式匹配是一个 noop 的假设下,这是微不足道的,可以立即减少。
使用~-模式
同样,如果我们继续使用data,但是写
T ts >/> ~(T qs) = ...
我们改变了 GHC/Haskell 的行为,以真正实现我在上面谈到的“信仰飞跃”。无可辩驳的模式匹配会自动成功,因此它不会引起进一步的评估。对于像Try 这样的单构造函数数据类型,这样做基本上是安全的。但是,如果您在多构造函数数据类型上执行这种惰性模式匹配,结果发现您匹配的值不是出现在您的模式中的构造函数,匹配仍然会成功,并且您会得到一个一旦您尝试使用模式内部的值,就会出现运行时异常。
显式提取
第三种选择是编写提取函数
unT :: Try -> [Int]
unT (T ts) = ts
然后说
(>/>) :: Try -> Try -> Try
T [] >/> i = i
T ts >/> qs = T (ts ++ unT qs)
这表明我们对第二个参数没有任何期望
在模式匹配时。这个版本非常符合~-pattern-version 将编译成的版本。
总结一下,现在让我们看看减少:
infi2 1
= { expanding the definition of infi2 }
T [1] >/> infi2 1
= { expanding the definition of >/> }
T ([1] ++ unT (infi2 1))
假设我们要打印结果和一个完整的缩减,让我们从这里继续一点:
T ([1] ++ unT (infi2 1))
= { expanding the definition of ++ }
T (1 : unT (infi2 1))
= { expanding the definition of infi2 }
T (1 : unT (T [1] >/> infi2 1))
= { expanding the definition of >/> }
T (1 : unT (T ([1] ++ unT (infi2 1))))
= { expanding the definition of the outer unT }
T (1 : ([1] ++ unT (infi2 1)))
此时,应该很明显,我们确实是增量获得了无限列表。