【问题标题】:Haskell Tail Recursive Inner FunctionsHaskell 尾递归内部函数
【发布时间】: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 的定义中使用letwhere 子句。
  • innerLength xs n+1 被解析为(innerLength xs n)+1,这是不是尾递归的。
  • @Franky 所说的,而且innerLength [] _ = 0 扔掉了累加器而不是返回它。应该是innerLength [] n = n。我也很想在递归情况下使用严格的应用程序,例如innerLength (_:xs) n = innerLength xs $! n+1
  • @Franky 感谢您指出这一点。对于使用 haskell 的我来说,这似乎一直是一个令人头疼的问题。

标签: 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”,因为它的范围仅限于函数,而替代方法只是重复稍微修改的函数名称,这不会带来任何可读性的好处。只有顶层函数通常得到显式签名,这被认为是局部变量足够接近潜在问题,它们不需要一个(除非你想澄清一个特别晦涩的类型)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-23
  • 1970-01-01
  • 1970-01-01
  • 2011-02-14
  • 2019-02-28
  • 2020-05-08
相关资源
最近更新 更多