Haskell 尾递归内部函数
Posted
技术标签:
【中文标题】Haskell 尾递归内部函数【英文标题】:Haskell Tail Recursive Inner Functions 【发布时间】:2015-05-09 04:51:30 【问题描述】:来自 Scala 背景,我非常习惯于使用尾递归来编写无法使用其他方法轻松表示的内容。假设我想计算列表的长度,但我不想使用折叠。我想以自然的尾递归方式来做。我是这样想的:
myLength :: [a] -> Int
myLength x = innerLength x 0
innerLength :: [a] -> Int -> Int -- Tail recursive inner call
innerLength [] _ = 0
innerLength (x:xs) n = innerLength xs n+1
这很好用。但是,innerLength 实际上是用于计算列表成员的尾递归内部调用并不是很可读,而且似乎 innerLength 的作用域是这样的,有人可以只使用 innerLength 而不是更好的 myLength。
有没有更好的方法来做到这一点?
【问题讨论】:
是的,在myLength
的定义中使用let
或where
子句。
innerLength xs n+1
被解析为(innerLength xs n)+1
,这是不是尾递归的。
@Franky 所说的,而且innerLength [] _ = 0
扔掉了累加器而不是返回它。应该是innerLength [] n = n
。我也很想在递归情况下使用严格的应用程序,例如innerLength (_:xs) n = innerLength xs $! n+1
@Franky 感谢您指出这一点。对于使用 haskell 的我来说,这似乎一直是一个令人头疼的问题。
【参考方案1】:
是的,您可以使用let
:
myLength :: [a] -> Int
myLength x =
let innerLength :: [a] -> Int -> Int -- Tail recursive inner call
innerLength [] n = n
innerLength (x:xs) n = innerLength xs (n+1)
in innerLength x 0
Live demo
或where
:
myLength :: [a] -> Int
myLength x = innerLength x 0
where innerLength :: [a] -> Int -> Int -- Tail recursive inner call
innerLength [] n = n
innerLength (x:xs) n = innerLength xs (n+1)
Live demo
两者的区别见here。
【讨论】:
好吧,innerLength [] n = n
,真的。
Haskell 风格通常将辅助函数命名为“go”或“aux”,因为它的范围仅限于函数,而替代方法只是重复稍微修改的函数名称,这不会带来任何可读性的好处。只有顶层函数通常得到显式签名,这被认为是局部变量足够接近潜在问题,它们不需要一个(除非你想澄清一个特别晦涩的类型)。以上是关于Haskell 尾递归内部函数的主要内容,如果未能解决你的问题,请参考以下文章