【问题标题】:Haskell - How to bypass lazy evaluation in updating arguments in a tail-recursive functionHaskell - 如何在尾递归函数中更新参数时绕过惰性求值
【发布时间】:2012-08-15 04:55:19
【问题描述】:

这是一个 Haskell 函数,它接受一个数字 n 并返回第 n 个斐波那契数。 (我使用了索引方案,第 0 个数字是 0,第一个数字是 1,第 2 个数字是 1,第 3 个数字是 2,依此类推。)

fib :: (Integral a) => a -> a
fib 0 = 0
fib n = fibhelper n 0 1

fibhelper :: (Integral a) => a -> a -> a -> a
fibhelper 1 x y = y
fibhelper n x y = fibhelper (n-1) y (x+y)

现在,假设为了提高效率,我想绕过 Haskell 的惰性求值并强制对更新的参数求值(例如,使用 $! 运算符?)什么是最优雅/惯用的方式这样做?

【问题讨论】:

标签: haskell pattern-matching lazy-evaluation fibonacci tail-recursion


【解决方案1】:

您可以使用 bang 模式来做到这一点。

{-# LANGUAGE BangPatterns #-}

fib :: (Integral a) => a -> a
fib 0 = 0
fib n = fibhelper n 0 1

-- Everything with a ! before it will be evaluated before the function body
fibhelper :: (Integral a) => a -> a -> a -> a
fibhelper 1 _ (!y) = y
fibhelper (!n) (!x) (!y) = fibhelper (n-1) y (x+y)
-- The above line is equivalent to:
--fibhelper n x y = n `seq` x `seq` y `seq` fibhelper (n-1) y (x+y)

还请注意,您对Integral 类型类的使用有点过分热心。您真的希望斐波那契数列的索引与值的类型相同吗?我建议您改用签名:

fib :: (Integral a, Integral b) => a -> b

此外,如果您正在寻找性能,应完全避免使用Integral

【讨论】:

  • 你甚至不需要Integral 来获得结果,Num 就可以了。不过,我很担心你的爆炸模式 - fibhelper 1 undefined 0 不是 0,因此函数不严格吗?
  • 嗯,是的,我没有在_ 之前放置爆炸模式,并且模式仍然自上而下匹配。因此,如果运行时发现第一个参数是 1,则将应用 fibhelper 的第一个定义,其中 _ 参数不是强制的。
  • 看来你不需要强制x,因为它收到了之前强制的y
猜你喜欢
  • 1970-01-01
  • 2016-04-13
  • 1970-01-01
  • 2015-05-09
  • 2011-12-05
  • 1970-01-01
  • 1970-01-01
  • 2017-01-20
  • 1970-01-01
相关资源
最近更新 更多