首先,它很棒。但是,我遇到了一种情况,我的基准测试结果很奇怪。我是 Haskell 的新手,这是我第一次接触可变数组和 Monad。下面的代码是基于这个例子。
我写了一个通用的单子for
函数,它接受数字和一个阶跃函数而不是一个范围(就像forM_
那样)。我将使用我的通用for
函数(循环 A)与嵌入等效递归函数(循环 B)进行了比较。拥有循环 A 明显快于拥有循环 B。更奇怪的是,同时拥有循环 A 和 B 比单独拥有循环 B 更快(但比单独拥有循环 A 稍慢)。
对于这些差异,我能想到一些可能的解释。请注意,这些只是猜测:
- 关于 Haskell 如何从 monadic 函数中提取结果,我还没有学到一些东西。
- 与循环 A 相比,循环 B 以较低缓存效率的方式对阵列进行故障处理。为什么?
- 我犯了一个愚蠢的错误;Loop A 和 Loop B 实际上是不同的。
- 请注意,在具有循环 A 和循环 B 之一或两者的所有 3 种情况下,程序产生相同的输出。
这是代码。我ghc -O2 for.hs
使用 GHC 版本 6.10.4 对其进行了测试。
import Control.Monad
import Control.Monad.ST
import Data.Array.IArray
import Data.Array.MArray
import Data.Array.ST
import Data.Array.Unboxed
for :: (Num a, Ord a, Monad m) => a -> a -> (a -> a) -> (a -> m b) -> m ()
for start end step f = loop start where
loop i
| i <= end = do
f i
loop (step i)
| otherwise = return ()
primesToNA :: Int -> UArray Int Bool
primesToNA n = runSTUArray $ do
a <- newArray (2,n) True :: ST s (STUArray s Int Bool)
let sr = floor . (sqrt::Double->Double) . fromIntegral $ n+1
-- Loop A
for 4 n (+ 2) $ \j -> writeArray a j False
-- Loop B
let f i
| i <= n = do
writeArray a i False
f (i+2)
| otherwise = return ()
in f 4
forM_ [3,5..sr] $ \i -> do
si <- readArray a i
when si $
forM_ [i*i,i*i+i+i..n] $ \j -> writeArray a j False
return a
primesTo :: Int -> [Int]
primesTo n = [i | (i,p) <- assocs . primesToNA $ n, p]
main = print $ primesTo 30000000