【问题标题】:Haskell: How to make foldr/build fusion happen in (zip [0..])?Haskell:如何在(zip [0..])中进行折叠/构建融合?
【发布时间】:2017-01-29 02:26:28
【问题描述】:

在 Haskell 中,我们可以使用这个有用的习语从列表中获取索引元素的列表:

indexify :: (Num i) => [a] -> [(i,a)]
indexify = zip [0..]

但是,根据zipGHC.List as of base-4.9.1.0中的实现,这不会完全执行列表融合,即这实际上不会生成列表[0..],但indexify的参数列表将是构建。

当然,有一个定义允许适当的列表融合:

indexify' :: (Num i) => [a] -> [(i,a)]
indexify' xs = build $ \c n ->
                foldr (\x r !i -> (i,x) `c` r (i+1)) (const n) xs 0

我们需要import GHC.Prim (build) 来执行此操作吗?还是有其他简化为indexify' 的实现?

【问题讨论】:

  • indexify = let f !i x = (i + 1, (i, x)) in snd . mapAccumL f 0 会起作用吗?我相信mapAccumL 会被融合。
  • @Alec 我正准备将您的评论变成答案并接受它,但它不起作用。 mapAccumL 定义为traverse = mapM,在“消费”方向融合(即使用foldr),但在“生产”方向不融合(即不使用build )。
  • 啊。好点子。早该想到的。还是比zip好一点。 :)
  • 另见ghc.haskell.org/trac/ghc/ticket/9495了解一些背景信息。
  • 我认为this 应该或多或少地回答你的问题。 TL;DR:似乎没有更简单的实现,您可以从GHC.Exts 导入build

标签: list haskell ghc


【解决方案1】:

这已经存在于ilist 包中,如indexed。相关源码sn-ps是

import GHC.Exts  -- exports `build`

indexed :: [a] -> [(Int, a)]
indexed xs = go 0# xs
  where
    go i (a:as) = (I# i, a) : go (i +# 1#) as
    go _ _ = []
{-# NOINLINE [1] indexed #-}

indexedFB :: ((Int, a) -> t -> t) -> a -> (Int# -> t) -> Int# -> t
indexedFB c = \x cont i -> (I# i, x) `c` cont (i +# 1#)
{-# INLINE [0] indexedFB #-}

{-# RULES
"indexed"       [~1] forall xs.    indexed xs = build (\c n -> foldr (indexedFB c) (\_ -> n) xs 0#)
"indexedList"   [1]  forall xs.    foldr (indexedFB (:)) (\_ -> []) xs 0# = indexed xs
  #-}

您可能会注意到,重写规则使用的定义几乎与您的定义相同,因此这可能是最好的方法。另外GHC.Exts也导出build,所以你不需要导入GHC.Prim

【讨论】:

    【解决方案2】:

    "Shortcut Fusion for Accumulating Parameters & Zip-like Functions

    它显示zip 融合。简而言之,对于类似zip 的函数,将使用双重函数unfoldr/destroy

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-11
      • 1970-01-01
      • 1970-01-01
      • 2015-08-30
      相关资源
      最近更新 更多