我在 Haskell 中有三个函数。所有这些都旨在基于 n 次迭代的初始猜测来执行 √2。 (1)
squareRootTwo :: Double -> Integer -> Double
squareRootTwo guess n
| n == 0 = guess
| otherwise = squareRootTwo ((guess + 2/guess) / 2) (n-1)
(2)
squareRootTwoA :: Double -> Integer -> Double
squareRootTwoA guess n
| n == 0 = guess
| otherwise = squareRootTwoA ((guess + 2/guess) / 2) (n-1) where n=n
(3)
squareRootTwoB :: Double -> Integer -> Double
squareRootTwoB guess n
| n == 0 = guess
| otherwise = let n = n-1 in squareRootTwoB ((guess + 2/guess) / 2) n
第一个函数(squareRootTwo)工作正常。在 ghci 中:
ghci> squareRootTwo 1.0 5
1.414213562373095
ghci> squareRootTwoA 1.0 5
No response
ghci> squareRootTwoB 1.0 5
No response
但是第二个和第三个功能根本不起作用。他们只是冻结了。我必须按 (ctrl + c) 在 VS Code 终端中停止此操作。 现在,我明白它可能正在创建递归并陷入无限循环。但是,我不明白它是如何创建无限循环的。因为这两个程序的编写方式,对我来说,它似乎没有创建无限循环。
由于我是 Haskell 的新手,我只是不明白为什么会发生这种情况。如果有人帮助我理解为什么会发生这种情况,那就太好了。谢谢。
您的定义等同于以下内容:
squareRootTwoA :: Double -> Integer -> Double
squareRootTwoA guess n
| n == 0 = guess
| otherwise = squareRootTwoA ((guess + 2/guess) / 2) (blurb-1)
where blurb = blurb
squareRootTwoB :: Double -> Integer -> Double
squareRootTwoB guess n
| n == 0 = guess
| otherwise = let blurb = blurb-1
in squareRootTwoB ((guess + 2/guess) / 2) blurb
您会看到,递归调用中的整数参数在这两种情况下都与原始值没有任何关系
n
,它是一个全新的变量,只是碰巧具有相同的名称和shadows旧的n
。像 n = n-1
这样的定义当然是循环的,它是一个不可能满足的方程(当其他语言将其作为语句执行时,它们在数学上对你撒谎)。
实现您尝试的方法是明确为新版本指定不同的名称:
squareRootTwoA :: Double -> Integer -> Double
squareRootTwoA guess n
| n == 0 = guess
| otherwise = squareRootTwoA ((guess + 2/guess) / 2) (n'-1)
where n' = n
squareRootTwoB :: Double -> Integer -> Double
squareRootTwoB guess n
| n == 0 = guess
| otherwise = let n' = n-1
in squareRootTwoB ((guess + 2/guess) / 2) n'