fmap
我需要for Seq
from Data.Sequence
package的并行(但懒惰)版本。但是包不导出任何Seq
数据构造函数。所以我不能直接把它包装进去newtype
并Functor
直接为newtype
.
我可以在不重写整个包的情况下做到这一点吗?
fmap
我需要for Seq
from Data.Sequence
package的并行(但懒惰)版本。但是包不导出任何Seq
数据构造函数。所以我不能直接把它包装进去newtype
并Functor
直接为newtype
.
我可以在不重写整个包的情况下做到这一点吗?
您可以做的最好的事情可能是splitAt
将序列分成块,fmap
在每个块上,然后将这些部分重新组合在一起。Seq
表示为一棵手指树,因此它的底层结构并不是特别适合并行算法——如果你按照它的自然结构来拆分它,连续的线程将变得越来越大。如果你想试一试,你可以FingerTree
从Data.Sequence
源代码中复制类型的定义,并用于unsafeCoerce
在它和 a 之间进行转换Seq
。您可能希望将前几个Deep
节点发送到一个线程,但是您必须非常仔细地考虑其余的节点。手指树可能与重量平衡相差甚远,主要是因为3^n
其渐近增长速度比2^n
; 您需要考虑到这一点以平衡线程之间的工作。
假设您使用,至少有两种合理的方式来拆分序列splitAt
:
在将计算分解为线程之前将其全部拆分。如果你这样做,你应该从左到右或从右到左拆分它,因为拆分小块比先拆分大块然后再拆分要便宜。您应该以类似的方式附加结果。
在多个线程中递归地拆分它。如果您想要很多块或更多潜在的懒惰,这可能是有道理的。在中间附近拆分列表并将每个部分发送到线程以进行进一步拆分和处理。
还有另一种可能更好的拆分方法,使用当前用于实现的机器zipWith
(请参阅我提交请求的 GitHub 票证chunksOf
),但我不知道您会在此应用程序中获得巨大的好处。
您寻求的非严格行为似乎不太可能在一般情况下起作用。您可能可以使它在许多或大多数特定情况下工作,但我不太乐观,您会找到一个完全通用的方法。
我找到了一个解决方案,但实际上效率并不高。
-- | A combination of 'parTraversable' and 'fmap', encapsulating a common pattern:
--
-- > parFmap strat f = withStrategy (parTraversable strat) . fmap f
--
parFmap :: Traversable t => Strategy b -> (a -> b) -> t a -> t b
parFmap strat f = (`using` parTraversable strat) . fmap f
-- | Parallel version of '<$>'
(<$|>) :: Traversable t => (a -> b) -> t a -> t b
(<$|>) = parFmap rpar