假设我有以下程序:
foo x y = let l1 = foo 0 x
l2 = foo 0 y
in l1 + l2
这只是一个简单的例子,但我认为足以用于演示目的。对于 foo 的每次新(递归)调用,我怎样才能更改 l1 和 l2 ?我知道它们在表达式中被评估为懒惰(在本例中是在运算符中的表达式)而不是在声明它们时,但是我需要一种如所述的方式,因为可能存在程序进入无限循环的情况。如果在第二个评估的参数 l2 上发生此无限递归,则没有问题,因为 l1 始终会获得在 * l2 之前评估,但如果是相反的方式,即无限评估 l1 表达式, * l2 ***没有机会评估。因此,如果我可以在每个新的 foo 和 l2 表达式评估>函数调用,问题就解决了。寻找一个好的/一般的解决方案。
编辑:忘记提及 x 或 y 或两者都可能是无限结构(列表) ,那就是问题所在。
答案 0 :(得分:1)
为了得到一个好的答案,我们首先需要一个具体的问题。考虑自然数为零Z
或另一个自然数S n
的后继n
。
data Nat = Z | S Nat
zero = Z
one = S Z
two = S $ S Z
在Haskell中,我们还可以编写无限的重复数据结构,如
infinity = S infinity
只要一个Nat
号码不是infinity
,我们就可以确定它是偶数还是奇数。
even :: Nat -> Bool
even Z = True
even (S n) = odd n
odd :: Nat -> Bool
odd Z = False
odd (S n) = even n
对于有限Nat
,even
为True
或False
,even infinity
为undefined
。这没关系,但如果我们想检查两个数字中的任何一个是even
怎么办?我们可以编写一个天真的实现:
eitherEven :: Nat -> Nat -> Bool
eitherEven x y = even x || even y
只要第一个参数是有限的,这就很好。在下文中,even
是任意偶数,odd
是任何奇数。
eitherEven even even == True
eitherEven even odd == True
eitherEven even infinity == True
eitherEven odd even == True
eitherEven odd odd == False
eitherEven odd infinity == undefined
但是当第一个参数是无限的时,即使第二个参数是True
Even
eitherEven infinity even == undefined -- this should be True
eitherEven infinity odd == undefined
eitherEven infinity infinity == undefined
该问题的一个简单解决方案是在测试第一个参数和测试第二个参数之间交替。当我们以递归方式调用函数时,我们将参数交换为替换正在测试的两个参数中的哪一个。
eitherEven :: Nat -> Nat -> Bool
eitherEven Z _ = True
eitherEven (S Z) y = even y
eitherEven (S (S x)) y = eitherEven y x
即使第一个参数不是有限的,它也具有所需的输出。
> eitherEven infinity two
True
对于不对称处理参数的更复杂问题,您可以传递Bool
标志并在每一步上翻转它。通常,您可以使用任何状态机来跟踪您的工作地点。
这个解决方案不是很令人满意,因为当我们想要测试三个数字中的任何一个是否均匀时,它不能立即解决该怎么做。为此,我们需要编写一个新函数。
anyEven3 :: Nat -> Nat -> Nat -> Bool
anyEven3 Z _ _ = True
anyEven3 (S Z) y z = eitherEven y z
anyEven3 (S (S x)) y z = anyEven3 y z x
我们将x
放在最后,因为在再次尝试y
之前,我们想要同时尝试z
和x
。我们正在排队。如果我们能够证明队列中的第一件事产生了结果True
,那么我们就完成了。如果我们可以证明队列中的第一件事没有产生结果,我们将其从队列中删除并使用适用于较小输入集的版本。如果我们无法确定队列中第一件事的结果,我们就把它放在最后。在eitherEven
中可以看到相同的模式,它带有两个参数,甚至在even
中只有一个参数。
anyEven :: [Nat] -> Bool
anyEven = go []
where
go [] [] = False
go r [] = go [] (reverse r)
go r ( Z :_ ) = True
go r ( S Z :ns) = go r ns
go r ((S (S x)):ns) = go (x:r) ns