【问题标题】:Optimizing repeated function calls in a Haskell composition优化 Haskell 组合中的重复函数调用
【发布时间】:2017-11-11 06:09:51
【问题描述】:

在编写一个探索生日悖论的程序时,我有以下可用的 Haskell 代码

sort :: Ord a => [a] -> [a]
-- body

hasDuplicates :: Eq a => [a] -> Bool
-- body

boolToInt :: Bool -> Int
-- body

main = do
  -- stuff
  repeats <- liftM sum . replicateM numTrials . liftM boolToInt .
    liftM hasDuplicates . liftM sort . replicateM checkNum $
    randomRIO (1::Int, 365)
  -- stuff

在最后一行,有很多liftM一个接一个地组成。这个组合可以优化吗?

我想到了mapping liftM[boolToInt, hasDuplicates, sort],然后是composeing,但这个列表是异构的,所以无效。 iterate 因类似原因无法工作。

【问题讨论】:

  • 好吧,至少,liftM f . liftM g = liftM (f . g)
  • 这可能是一个答案。 @Izaak 打败了你!

标签: haskell


【解决方案1】:

是的,您可以编写其中的一些。看到这一点的最简单方法是认识到 liftM 实际上只是 fmap 对于 Monad 实例的实现。(1) 所以通常的函子定律适用:

fmap id = id
fmap f . fmap g = fmap (f . g)

这样

liftM boolToInt .
liftM hasDuplicates .
liftM sort

可以写

fmap (boolToInt . hasDuplicates . sort)

我们怎样才能做得更好?让我们在上下文中看一下。

liftM sum . replicateM numTrials .
fmap (boolToInt . hasDuplicates . sort) .
replicateM checkNum

这里似乎没有很多重复,但是如果每个试验中有很多试验或检查,则可能会显着降低效率,因为您是在对它们求和之前在内存中建立这些列表。您可以手动解决此问题,但这不会很愉快(2)。修复它的好方法是使用流包。要考虑的另一件事是,由于我们正在处理的唯一影响是随机性,如果出现重复,我们可以停止试验。稍后我会尝试进行演示。


  1. liftM全部目的是能够为类型Monad 编写m 实例,然后编写instance Functor m where fmap = liftM。您通常不应该将liftM 用于其他任何事情。

  2. 例如,

    fmap sum . replicateM n
    

    可以写

    sumReplications = go 0 where
      go !acc 0 _ = pure acc
      go acc n m = m >>= \res -> go (acc + res) (n - 1) m
    

【讨论】:

    【解决方案2】:

    您可以liftM 整个组合,而不是分别liftM每个功能。

    liftM (sum . replicate numTrials . boolToInt . hasDuplicates . sort)

    【讨论】:

    • 除了replicateM不只是liftM . replicate
    猜你喜欢
    • 2011-08-30
    • 1970-01-01
    • 2013-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多