gpt4 book ai didi

swift - 如何使嵌套的 flatMap 和 map 更易于理解

转载 作者:搜寻专家 更新时间:2023-10-31 23:01:24 24 4
gpt4 key购买 nike

假设,我们有一个结构M:

public struct M<T> {
let value: T
public init(_ value: T) { self.value = value }

public func map<U>(f: T -> U) -> M<U> { return M<U>(f(value)) }
public func flatMap<U>(f: T -> M<U>) -> M<U> { return f(value) }
}

和一些计算值 (T) 并将其作为包含 M 的包装值返回的函数:

func task1() -> M<Int> {
return M(1)
}

func task2(value: Int = 2) -> M<Int> {
return M(value)
}

func task3(value: Int = 3) -> M<Int> {
return M(value)
}

func task4(arg1: Int, arg2: Int, arg3: Int) -> M<Int> {
return M(arg1 + arg2 + arg2)
}

现在,假设我们要计算 task1、task2 和 task3 的值,然后将所有三个计算值作为参数传递给 task4。看来,这需要对 flatMapmap 使用嵌套调用:

let f1 = task1()
let f2 = task2()
let f3 = task3()

f1.flatMap { arg1 in
return f2.flatMap { arg2 in
return f3.flatMap { arg3 in
return task4(arg1, arg2:arg2, arg3:arg3).map { value in
print("Result: \(value)")
}
}
}
}

但这看起来不太好理解。有办法改善吗?例如,使用自定义运算符?

最佳答案

好吧,作为引用,最好在此处记录 Haskell 在这种情况下所做的事情:

example1 = do
arg1 <- task1
arg2 <- task2
arg3 <- task3
value <- task4 arg1 arg2 arg3
putStrLn ("Result: " ++ show value)

这会脱糖到 >>=运算符,它是一个翻转的中缀 flatMap:

-- (>>=) :: Monad m => m a -> (a -> m b) -> m b
--
-- It's a right-associative operator

example2 = task1 >>= \arg1 ->
task2 >>= \arg2 ->
task3 >>= \arg3 ->
task4 arg1 arg2 arg3 >>= \value ->
putStrLn ("Result: " ++ show value)

是的,您在这里所做的是重新发现 Haskell 的 do 的动机。 -notation——它正是用于编写嵌套 flatMap 的特殊平面语法!

但这里还有另一个可能与此示例相关的技巧。请注意,在您的计算中,task1 , task2task3没有任何相互依赖性。这可以作为设计用于将它们合并到一个任务中的“平面”实用程序结构的基础。在 Haskell 中,您可以使用 Applicative 轻松完成此操作类和模式匹配:

import Control.Applicative (liftA3, (<$>), (<*>))

-- `liftA3` is the generic "three-argument map" function,
-- from `Control.Applicative`.
example3 = do
-- `liftA3 (,,)` is a task that puts the results of its subtasks
-- into a triple. We then flatMap over this task and pattern match
-- on its result.
(arg1, arg2, arg3) <- liftA3 (,,) task1 task2 task3
value <- task4 arg1 arg2 arg3
putStrLn ("Result: " ++ show value)

-- Same thing, but with `<$>` and `<*>` instead of `liftA3`
example4 = do
(arg1, arg2, arg3) <- (,,) <$> task1 <*> task2 <*> task3
value <- task4 arg1 arg2 arg3
putStrLn ("Result: " ++ show value)

如果task1 , task2task3返回相同的类型,那么另一种展平它的方法是使用 Traversable类(达到与上述相同的 Applicative 技术):

import Data.Traversable (sequenceA)

example5 = do
-- In this use, sequenceA turns a list of tasks into a
-- task that produces a list of the originals results.
[arg1, arg2, arg3] <- sequenceA [task1, task2, task3]
value <- task4 arg1 arg2 arg3
putStrLn ("Result: " ++ show value)

因此,一个想法是构建一个提供类似功能的实用程序库。一些示例操作:

  1. 将异构类型的任务组合成一个复合体。签名看起来像 (M<A1>, ..., M<An>) -> M<(A1, ..., An)>
  2. 多任务映射:将 n 个函数映射到生成适当类型的 n 个任务上。
  3. 将一系列任务转变为产生一系列结果的任务。

请注意,#1 和#2 的功率相等。还要注意,如果我们谈论的是异步任务,这些操作比平面图有一个优势,那就是它们更容易并行化。

关于swift - 如何使嵌套的 flatMap 和 map 更易于理解,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34313256/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com