我正在学习Haskell并且来自Python,所以列表理解很熟悉。把这个列表理解(请):
[x^2 | x <- [1..10], x^2 < 50]
[1,4,9,16,25,36,49]
表达式x^2
是否会在x
的每个值上进行两次评估?有没有办法写出这种理解,使表达式x^2
只被评估一次?做这样的事情是否有意义:
filter (< 50) [x^2 | x <- [1..10]]
[1,4,9,16,25,36,49]
这是做事的“Haskell方式”吗?它也更有效率吗?
答案 0 :(得分:7)
您可以在列表推导中使用let
:
[ z | x <- [1..10], let z = x^2, z < 50]
然后x^2
只评估一次。
答案 1 :(得分:4)
我这样做,这与你的第二个例子相似:
filter (<50) (map (^2) [1..10])
我对列表理解有偏见。他们基本上只做三件事(映射,过滤和交叉产品),并且你希望拥有比这三者更大的操作词汇。研究Data.List
模块。
关于性能,我们可以使用criterion库轻松地对其进行基准测试而无需太多精力。 (我已经a repo here了 - 您可以使用the Stack tool进行构建。)
import Criterion.Main
main = defaultMain
[ bgroup "one" [ bench "10" $ nf one 10
, bench "100" $ nf one 100
, bench "1000" $ nf one 1000
, bench "10000" $ nf one 10000
]
, bgroup "two" [ bench "10" $ nf two 10
, bench "100" $ nf two 100
, bench "1000" $ nf two 1000
, bench "10000" $ nf two 10000
]
, bgroup "three" [ bench "10" $ nf three 10
, bench "100" $ nf three 100
, bench "1000" $ nf three 1000
, bench "10000" $ nf three 10000
]
]
one :: Int -> Int
one n = sum [x^2 | x <- [1..n], x^2 < n*5]
two :: Int -> Int
two n = sum (filter (<(5*n)) [x^2 | x <- [1..n]])
three :: Int -> Int
three n = sum (filter (<(5*n)) (map (^2) [1..n]))
我得到了这些结果,对我来说这表明它并没有产生很大的影响(如果有的话):
% stack install --ghc-options='-O2'
Copied executables to /Users/luis.casillas/.local/bin:
- comprehension
% comprehension
benchmarking one/10
time 18.40 ns (18.35 ns .. 18.45 ns)
1.000 R² (1.000 R² .. 1.000 R²)
mean 18.38 ns (18.33 ns .. 18.42 ns)
std dev 143.7 ps (116.9 ps .. 173.6 ps)
benchmarking one/100
time 89.11 ns (88.49 ns .. 89.72 ns)
1.000 R² (1.000 R² .. 1.000 R²)
mean 88.78 ns (88.42 ns .. 89.44 ns)
std dev 1.582 ns (1.231 ns .. 2.103 ns)
variance introduced by outliers: 23% (moderately inflated)
benchmarking one/1000
time 649.2 ns (640.7 ns .. 658.7 ns)
0.998 R² (0.998 R² .. 0.999 R²)
mean 647.6 ns (637.8 ns .. 658.0 ns)
std dev 31.40 ns (24.70 ns .. 40.84 ns)
variance introduced by outliers: 66% (severely inflated)
benchmarking one/10000
time 6.197 μs (6.079 μs .. 6.282 μs)
0.997 R² (0.996 R² .. 0.998 R²)
mean 6.180 μs (6.058 μs .. 6.295 μs)
std dev 436.0 ns (371.1 ns .. 531.8 ns)
variance introduced by outliers: 77% (severely inflated)
benchmarking two/10
time 20.23 ns (19.89 ns .. 20.56 ns)
0.999 R² (0.998 R² .. 0.999 R²)
mean 19.89 ns (19.71 ns .. 20.11 ns)
std dev 709.8 ps (582.1 ps .. 939.1 ps)
variance introduced by outliers: 58% (severely inflated)
benchmarking two/100
time 83.95 ns (83.14 ns .. 84.90 ns)
0.999 R² (0.999 R² .. 1.000 R²)
mean 83.34 ns (82.59 ns .. 83.99 ns)
std dev 2.354 ns (1.890 ns .. 3.043 ns)
variance introduced by outliers: 44% (moderately inflated)
benchmarking two/1000
time 645.3 ns (635.8 ns .. 655.4 ns)
0.998 R² (0.997 R² .. 0.999 R²)
mean 652.9 ns (643.1 ns .. 664.5 ns)
std dev 35.54 ns (29.67 ns .. 46.19 ns)
variance introduced by outliers: 71% (severely inflated)
benchmarking two/10000
time 6.268 μs (6.142 μs .. 6.385 μs)
0.998 R² (0.997 R² .. 0.999 R²)
mean 6.200 μs (6.099 μs .. 6.367 μs)
std dev 397.6 ns (261.9 ns .. 637.4 ns)
variance introduced by outliers: 73% (severely inflated)
benchmarking three/10
time 18.96 ns (18.66 ns .. 19.32 ns)
0.998 R² (0.998 R² .. 0.999 R²)
mean 19.17 ns (18.92 ns .. 19.49 ns)
std dev 990.6 ps (774.2 ps .. 1.393 ns)
variance introduced by outliers: 75% (severely inflated)
benchmarking three/100
time 89.01 ns (88.39 ns .. 89.78 ns)
0.998 R² (0.997 R² .. 0.999 R²)
mean 92.60 ns (90.78 ns .. 98.08 ns)
std dev 9.138 ns (5.755 ns .. 14.22 ns)
variance introduced by outliers: 91% (severely inflated)
benchmarking three/1000
time 638.9 ns (627.9 ns .. 648.7 ns)
0.996 R² (0.994 R² .. 0.998 R²)
mean 643.6 ns (627.9 ns .. 660.6 ns)
std dev 48.67 ns (38.78 ns .. 61.57 ns)
variance introduced by outliers: 83% (severely inflated)
benchmarking three/10000
time 6.060 μs (5.989 μs .. 6.119 μs)
0.998 R² (0.997 R² .. 0.999 R²)
mean 6.124 μs (6.036 μs .. 6.240 μs)
std dev 359.7 ns (294.9 ns .. 431.9 ns)
variance introduced by outliers: 69% (severely inflated)