做一些大学作业我发现自己做了类似的事情:
scanl' (\acc (a, b, c) -> expression) 0 $ zip3 as bs cs
使用不同数量的压缩列表,最多7个。这似乎效率很低,因为它构造了一个辅助的元组列表。
要递归地定义scanl2, scanl3
等并且避免构建额外的列表并不是很困难,但是所需的样板数量是荒谬的。
有没有办法兼顾两者 - 缺少样板和低内存使用?
答案 0 :(得分:1)
正如评论中所提到的,看起来像生成“额外列表”的Haskell程序实际上很少生成额外的列表(感谢延迟评估等),因此不太可能引入额外的内存使用量。使用Scan.hs
。如果函数的使用方式使列表可以在生成时使用,则不会生成任何实际的列表(中间或最终)。
例如,请考虑以下sum
程序,该程序在生成import Data.List (scanl')
scanl3 :: (acc -> (a, b, c) -> acc) -> acc -> [a] -> [b] -> [c] -> [acc]
scanl3 f z as bs cs = scanl' f z $ zip3 as bs cs
main = print $ sum $ scanl3 (\z (a, b, c) -> z + a*b + 2*c)
0 [1..100000000] [10,20..] [100,200..]
时使用大型列表:
-O2
如果您使用ghc -prof -rtsopts=all -O2 Scan.hs
优化来编译它以进行性能分析:
./Scan +RTS -h
hp2ps Scan.hp
evince Scan.ps
然后使用堆分析运行它:
zip3
你会发现它在常量内存中运行大约35k),既没有明确创建中间scanl3
列表也没有创建最终scanl3
列表。
如果在整个列表需要同时保存在内存中的上下文中使用zip3
,则中间列表仍然不应该导致任何增加的内存使用,尽管只有可以肯定的是,它将在特定用例中分析它和替代实现。
关于使用zip3
作为中间步骤的时间开销,再次确实需要对替代实现进行基准测试。我使用scanl3 :: (acc -> (a, b, c) -> acc) -> acc -> [a] -> [b] -> [c] -> [acc]
scanl3 f z (a:as) (b:bs) (c:cs) = z : scanl3 f (f z (a, b, c)) as bs cs
scanl3 _ z _ _ _ = [z]
将上述版本与递归版本进行了比较:
-O2
使用与zip3
编译的相同的测试用例,发现运行时几乎相同({-# LANGUAGE BangPatterns #-}
scanl3 :: (acc -> (a, b, c) -> acc) -> acc -> [a] -> [b] -> [c] -> [acc]
scanl3 f !z (a:as) (b:bs) (c:cs) = z : scanl3 f (f z (a, b, c)) as bs cs
scanl3 _ z _ _ _ = [z]
为15.2秒,递归为16.8秒)。
请注意,我没有明确地使我的递归版本严格,但在特定的使用情况下(将其与生成时相加),这没有任何区别。
编辑:实际上,我猜它确实有所作为。使用爆炸模式的严格版本:
aws lambda create-function \
--function-name MicroserviceGetAll \
--role arn:aws:iam::<act-id>:role/service-role/microRole \
--handler org.sample.serverless.aws.couchbase.BucketGetAll \
--zip-file fileb:///Users/arungupta/workspaces/serverless/aws/microservice/microservice-http-endpoint/target/microservice-http-endpoint-1.0-SNAPSHOT.jar \
--description "Microservice HTTP Endpoint - Get All" \
--runtime java8 \
--region us-west-1 \
--timeout 30 \
--memory-size 1024 \
--environment Variables={COUCHBASE_HOST=ec2-35-165-83-82.us-west-2.compute.amazonaws.com} \
--publish
看起来它的运行速度可能会持续0.5-1.0秒(超过16秒)。