我正在寻找一种优雅的方式来编写这段代码:
import Data.List
import Data.Maybe
combi = [(x,y) | x <- [2..100], y <- [x..100]]
gsp = group (sort [x*y | (x,y) <- combi])
counts = zip (map head gsp) (map length gsp)
multipleProducts x = (fromJust (lookup x counts)) > 1
possibleComb1 = [(x,y) | (x,y) <- combi, multipleProducts (x*y)]
由于我多次重用相同的模式,但基于与
[x*y | (x,y) <- combi]
不同的输入集,因此我得出了这段代码。import Data.List
import Data.Maybe
combi = [(x,y) | x <- [2..100], y <- [x..100]]
onlyOneEl e x = (fromJust (lookup x counts)) == 1
where gs = group (sort e)
counts = zip (map head gs) (map length gs)
multipleProducts = not.(onlyOneEl [x*y | (x,y) <- combi])
possibleComb1 = [(x,y) | (x,y) <- combi, multipleProducts (x*y)]
然而,Haskell 似乎每次我调用 multipleProducts 时都会计算
gs
和 count
,花费了大量的时间,而不是只计算一次,因为 e 的值对于 multipleProducts 总是相同的。避免重新计算的最优雅的方法是什么?
有什么比使用一个函数预先计算
counts
并将其存储在一个局部变量中,然后将它传递给 onlyOneEl
而没有 where 更好的方法吗?因为我后来基于不同的集合重用
onlyOneEl
,我想避免有多个 counts
变量。我理解 here 为什么它没有对每个函数进行一次评估,但是,我不使用 x 作为我的最后一个参数,因此不能完全按照这种方式进行。
提前致谢 !
最佳答案
定义
onlyOneEl e x = fromJust (lookup x counts) == 1
where gs = group (sort e)
counts = zip (map head gs) (map length gs)
说“给定
e
和 x
,设置 gs
和 counts
的计算并使用它们(延迟计算的)结果来计算表达式 fromJust (lookup x counts) == 1
。你可以完全等效地将它写成onlyOneEl e x =
let gs = ...
counts = ...
in fromJust ...
另一方面,如果您使用 lambda 表达式将
x
移到另一侧,onlyOneEl e = \x -> fromJust ...
where ...
然后将
gs
和 counts
拉入外部作用域。这段代码相当于onlyOneEl e =
let gs = ...
counts = ...
in \x -> fromJust ...
所以
gs
和 counts
只会在每次 onlyOneEl
应用到单个参数时计算一次。GHC 支持一种称为“完全懒惰”的转换,它进行这种修改,当它认为它是一个好主意时就会应用它。显然,GHC在本案中做出了错误的判断。
关于Haskell : force evaluation/avoid garbage collecting when composing functions,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36293798/