如何在Haskell中强制评估?

时间:2013-01-04 18:51:00

标签: haskell lazy-evaluation

我对Haskell相对较新,我正在尝试学习如何使用符号按顺序执行不同的操作。 特别是,我正在编写一个程序来对算法(函数)进行基准测试

foo :: [String] -> [String]

为此,我想写一个像

这样的函数
import System.CPUTime

benchmark :: [String] -> IO Integer
benchmark inputList = do
                         start <- getCPUTime
                         let r = foo inputList
                         end <- getCPUTime
                         return (end - start) -- Possible conversion needed.

最后一行可能需要转换(例如毫秒),但这不是此问题的主题。

这是衡量在某个参数inputList上计算函数foo所需时间的正确方法吗?

换句话说,在执行操作foo inputList之前,表达式end <- getCPUTime会完全缩小吗?或者r只会绑定到thunk foo inputList

更一般地说,如何在执行某些操作之前确保完全评估表达式?


这个问题几个月前被问到程序员(参见here)并在那里得到了一个接受的答案,但它已被关闭为主题,因为它属于堆栈溢出。问题无法移动到堆栈溢出,因为它超过60天。因此,与主持人达成协议,我在此处重新提出问题,并自行发布已接受的问题,因为我认为它包含一些有用的信息。

2 个答案:

答案 0 :(得分:17)

最初由用户ysdx on programmers提供的答案:

  

确实,您的版本不会对您的算法进行基准测试。由于未使用r,因此根本不予评估。

     

您应该可以使用DeepSeq

benchmark :: [String] -> IO Integer
benchmark inputList = do
                     start <- getCPUTime
                     let r = foo inputList
                     end <- r `deepseq` getCPUTime
                     return (end - start)
     

a `deepseq` b)是一些&#34;魔法&#34;表达式,在返回a之前强制对b进行完整/递归评估。

答案 1 :(得分:8)

我会使用语言扩展-XBangPatterns,我觉得在这种情况下非常有表现力。所以你必须说“let !r = foo inputList”,如:

{-# LANGUAGE BangPatterns #-}
import System.CPUTime

benchmark :: [String] -> IO Integer
benchmark inputList = do
                         start <- getCPUTime
                         let !r = foo inputList
                         end <- getCPUTime
                         return (end - start)