因此,我有两个函数列表的实现,给定一个函数f :: Int -> a
和一个数字n
,它们应该产生列表[f 0, f 1, ..., f (n-1)]
。我试图猜测在工作和跨度方面哪个更好。
tabulate1 :: (Int -> a) -> Int -> [a]
tabulate1 f n = tab (\x -> f (n - x)) n where
tab _ 0 = []
tab g n = let (x,xs) = (g n) ||| (tab g (n-1))
in (x:xs)
tabulate2 :: (Int -> a) -> Int -> [a]
tabulate2 f n = tab f 0 (n-1) where
tab f n m
| n > m = []
| n == m = [f n]
| otherwise = let i = (n + m) `div` 2
(l, r) = (tab f n i) ||| (tab f i+1 m)
in (l ++ r)
第一个避免使用具有线性工作和跨度的(++)
,而第二个则并行计算两个子列表,但使用(++)
。
那么...哪个更好?
答案 0 :(得分:1)
在Haskell中,时间和空间的复杂性通常是不平凡的,因为它是一种惰性语言。这意味着尽管一个函数可能是O(n!)
,但它的结果可能永远不需要,因此也就不会求值。或者像在这种情况下一样,如果您的函数返回一个列表,而其他函数只需要前三个元素,则仅对那些元素进行求值。
无论如何,您的函数只是map
的特例,因此可以用更易读的方式对其进行编码:
tabulate f n = map f [0..n]
地图实现了折叠,可能是您可以获得的最优化的版本