You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Haskell:并行do表示法与列表推导式的转换及并行化实现

Haskell并行化:列表推导转do表示法与递归并行的疑问解答

列表推导的并行实现示例

先看列表推导式的并行写法,借助Control.Parallel.Strategies里的parList rdeepseq策略,让列表元素的计算并行执行:

import Control.Parallel.Strategies
import Control.Monad (replicateM)

-- 模拟Yahtzee风格的条目计算
entry :: Int -> [Int]
entry n = [calculateScore roll | roll <- generateAllRolls n] `using` parList rdeepseq
  where
    -- 生成n次骰子投掷的所有可能组合
    generateAllRolls k = replicateM k [1..6]
    -- 计算单组投掷的得分(示例逻辑)
    calculateScore = sum

转成do表示法的并行实现

列表是Monad实例,列表推导式可以直接转写成do表示法,并行策略的用法完全一致——因为do块最终生成的还是列表,直接在后面加using parList rdeepseq即可:

import Control.Parallel.Strategies
import Control.Monad (replicateM)

entryDo :: Int -> [Int]
entryDo n = do
    roll <- generateAllRolls n
    return $ calculateScore roll
  `using` parList rdeepseq
  where
    generateAllRolls k = replicateM k [1..6]
    calculateScore = sum

列表推导转do表示法的简便方法

其实没有复杂技巧:列表推导的[expr | x <- list]和do块do { x <- list; return expr }是完全等价的语法糖。如果是带多个生成器的列表推导(比如[a+b | a <- as, b <- bs]),转成do表示法就是:

do
    a <- as
    b <- bs
    return $ a + b

之后直接保留using parList rdeepseq的并行策略就行,不需要额外修改。

递归并行的理解验证

你关于“并行化entry n需等待entry (n-1)处于范式”的理解是正确的,但要分具体场景:

  • 如果generateAllRolls n或者calculateScore依赖entry (n-1)的结果,Haskell的惰性求值会先触发entry (n-1)的求值,直到它完全变成范式——因为并行列表推导需要遍历生成的列表元素,而这些元素的生成依赖entry (n-1)的最终结果。
  • 如果entry (n-1)本身也使用了并行策略,它的求值会被并行处理,但entry n必须等它完成后才能开始自己的并行计算。
  • 要是递归调用出现在每个列表元素的计算逻辑里(比如calculateScore里调用entry (n-1)),每个并行执行的calculateScore都会触发entry (n-1)的求值,这时候会产生大量重复计算,建议用记忆化(memoization)优化,比如借助Data.MemoCombinators库的工具缓存entry的结果。

内容的提问来源于stack exchange,提问作者Randall Fairman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 03:50:17