我正在寻找一种优雅的方式来编写这段代码:

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 时都会计算 gscount,花费了大量的时间,而不是只计算一次,因为 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)

说“给定 ex ,设置 gscounts 的计算并使用它们(延迟计算的)结果来计算表达式 fromJust (lookup x counts) == 1 。你可以完全等效地将它写成
onlyOneEl e x =
  let gs = ...
      counts = ...
  in fromJust ...

另一方面,如果您使用 lambda 表达式将 x 移到另一侧,
onlyOneEl e = \x -> fromJust ...
  where ...

然后将 gscounts 拉入外部作用域。这段代码相当于
onlyOneEl e =
  let gs = ...
      counts = ...
  in \x -> fromJust ...

所以 gscounts 只会在每次 onlyOneEl 应用到单个参数时计算一次。

GHC 支持一种称为“完全懒惰”的转换,它进行这种修改,当它认为它是一个好主意时就会应用它。显然,GHC在本案中做出了错误的判断。

关于Haskell : force evaluation/avoid garbage collecting when composing functions,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36293798/

10-11 22:33