33

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

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

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


这个问题是几个月前在程序员身上问的(见这里),那里有一个被接受的答案,但由于它属于堆栈溢出,它已被关闭为题外话。该问题无法移至堆栈溢出,因为它已超过 60 天。因此,与版主一致,我在这里重新发布问题并自己发布已接受的问题,因为我认为它包含一些有用的信息。

4

2 回答 2

21

最初由用户ysdx 在程序员上给出的答案:

实际上,您的版本不会对您的算法进行基准测试。由于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进行完整/递归评估。ab

于 2013-01-04T18:53:07.593 回答
14

我会使用语言扩展-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)
于 2013-01-04T19:37:53.627 回答