Haskell递归与无限循环问题:求√2迭代函数异常分析
解析Haskell中计算√2的两个函数无限循环的原因
你写的三个函数都是基于牛顿迭代法计算√2的实现,其中squareRootTwo能正常终止,而squareRootTwoA和squareRootTwoB陷入无限循环,核心原因都是名字遮蔽(Name Shadowing)导致的递归变量错误,具体分析如下:
1. squareRootTwoA的问题
函数定义里的where n=n是典型的自引用名字遮蔽:
squareRootTwoA guess n | n == 0 = guess | otherwise = squareRootTwoA ((guess + 2/guess) / 2) (n-1) where n=n
这里的where子句重新定义了局部变量n,让它等于自身——这是一个无限递归的定义,没有任何实际值。此时函数里所有用到n的地方(包括条件判断n == 0和递归参数n-1),都会优先使用这个局部的n,而非函数参数传入的n。
因为局部n永远无法被求值为一个具体的整数,条件n == 0永远不成立,递归会一直进行下去,最终陷入无限循环。
2. squareRootTwoB的问题
let n = n-1同样犯了名字遮蔽的错误:
squareRootTwoB guess n | n == 0 = guess | otherwise = let n = n-1 in squareRootTwoB ((guess + 2/guess) / 2) n
let子句里定义的局部n,右边的n引用的是这个刚定义的局部变量,而非函数参数的n。这就导致n = n-1变成了自引用的无限递归定义,永远无法得到一个递减的整数值。
递归调用时传递的n始终是这个未正确求值的局部变量,条件n == 0永远无法满足,自然会无限循环。
对比正确的squareRootTwo
正确的函数没有做任何局部变量遮蔽,递归时直接使用参数n进行递减:
squareRootTwo guess n | n == 0 = guess | otherwise = squareRootTwo ((guess + 2/guess) / 2) (n-1)
每次递归都会把参数n减1,直到n变为0,触发终止条件,递归正常结束。
内容的提问来源于stack exchange,提问作者Anik kanti sikder
相关产品推荐
相关产品推荐

