Haskell:在编写函数时强制评估/避免垃圾收集

时间:2016-03-29 19:48:22

标签: haskell

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

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似乎计算gscount每次调用multipleProducts,花费大量时间,而不是只计算一次,因为e的值总是与multipleProducts相同。

避免重新计算的最优雅方法是什么? 有没有什么比使用一个函数预先计算counts并将其存储在局部变量中,然后将其传递给onlyOneEl而没有哪里更好?

因为我后来根据不同的集合重用onlyOneEl,我想避免使用多个counts变量。

我理解here为什么每个函数没有对它进行一次评估,但是,我不使用x作为我的最后一个参数,因此不能完全按照这种方式进行。

提前致谢!

2 个答案:

答案 0 :(得分:3)

你可以用更多的目标来重写它。没有进入数学,只需生成数据和过滤,您就可以用更少的计算来实现相同的目标。

生成产品时,也要将乘数加到元组中,即

combi n = [((x,y),x*y) | x<-[2..n], y<-[x..n]]

现在您可以根据产品进行排序和分组

multi = filter ((>1) . length) . groupBy ((==) `on` snd) . sortBy (comparing snd) . combi

并提取元组的第一个元素,它将是(x,y)对,不止一次地提供相同的产品。

map (map fst) (multi 100)

如果你不关心分组,你可以压扁结果,即

concatMap (map fst) (multi 100)

答案 1 :(得分:2)

定义

e

说“给定xgs,设置countsfromJust (lookup x counts) == 1的计算,并使用他们(懒惰计算)的结果来计算表达式{{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在这种情况下做出了错误的判断。