首页 > 解决方案 > 异步mapFold

问题描述

对于以下示例,Array.mapFold生成结果([|1; 4; 12|], 7)

let mapping s x = (s * x, s + x)

[| 1..3 |]
|> Array.mapFold mapping 1

现在假设我们的映射是异步的。

let asyncMapping s x = async { return (s * x, s + x) }

我能够创建Array.mapFoldAsync以下工作。

[| 1..3 |]
|> Array.mapFoldAsync asyncMapping 1
|> Async.RunSynchronously

有没有一种简洁的方法可以在不创建的情况下实现这一目标Array.mapFoldAsync

我要求作为学习其他技术的一种方式 - 我使用 Array.fold 的尝试非常糟糕。

标签: asynchronousf#fold

解决方案


mapFold我认为与函数结合通常不会有多大好处Async,因为预期的结果是 tuple ('values * 'accumulator),但使用Async函数最多会给你一个Async<'values * 'accumulator>. 考虑以下尝试与 一起Array.mapFold工作Async

let mapping s x = async { 
    let! s' = s
    let! x' = x
    return (s' * x', s' + x') 
}

[| 1..3 |]
|> Array.map async.Return
|> Array.mapFold mapping (async.Return 1)

即使这样也行不通,因为类型不匹配: The type ''a * Async<'b>' does not match the type 'Async<'c * 'd>'

您可能还注意到,虽然有Array.Parallel.map,但没有Array.Parallel.foldor Array.Parallel.mapFold。如果您尝试编写自己的mapFoldAsync,您可能会明白原因。映射部分非常简单,只需部分应用Array.map和组合Async.Parallel

let mapAsync f = Array.map f >> Async.Parallel

您也可以实现异步fold,但由于每次评估都取决于先前的结果,因此您不能利用Async.Parallel这次:

let foldAsync f state array =
    match array |> Array.length with
    | 0 -> async.Return state
    | length ->
        async {
            let mutable acc = state
            for i = 0 to length - 1 do
                let! value = f acc array.[i]
                acc <- value
            return acc
        }

现在,当我们尝试将这些组合起来构建 amapFoldAsync时,很明显我们不能再利用映射上的并行执行,因为值和累加器都可以基于先前评估的结果。这意味着我们mapFoldAsync将是一个修改后的“foldAsync”,而不是它的组合mapAsync

let mapFoldAsync (f: 's -> 'a -> Async<'b * 's>) (state: 's) (array: 'a []) =
    match array |> Array.length with
    | 0 -> async.Return ([||], state)
    | length ->
        async {
            let mutable acc = state
            let results = Array.init length <| fun _ -> Unchecked.defaultof<'b>
            for i = 0 to length - 1 do
                let! (x,y) = f acc array.[i]
                results.[i] <- x
                acc <- y
            return (results, acc)
        }

虽然这将为您提供一种mapFold使用异步映射函数的方法,但唯一真正的好处是如果映射函数执行一些具有高延迟的操作,例如服务调用。您将无法利用并行执行来加速。如果可能,我建议根据您的实际情况考虑替代解决方案。


推荐阅读