gpt4 book ai didi

scala - 与 FlatMap/Map 转换的 for 理解相混淆

转载 作者:行者123 更新时间:2023-12-03 05:09:23 24 4
gpt4 key购买 nike

我似乎真的不太理解Map和FlatMap。我无法理解的是 for 理解如何是对 map 和 flatMap 的一系列嵌套调用。以下示例来自Functional Programming in Scala

def bothMatch(pat:String,pat2:String,s:String):Option[Boolean] = for {
f <- mkMatcher(pat)
g <- mkMatcher(pat2)
} yield f(s) && g(s)

翻译为

def bothMatch(pat:String,pat2:String,s:String):Option[Boolean] = 
mkMatcher(pat) flatMap (f =>
mkMatcher(pat2) map (g => f(s) && g(s)))

mkMatcher方法定义如下:

  def mkMatcher(pat:String):Option[String => Boolean] = 
pattern(pat) map (p => (s:String) => p.matcher(s).matches)

模式方法如下:

import java.util.regex._

def pattern(s:String):Option[Pattern] =
try {
Some(Pattern.compile(s))
}catch{
case e: PatternSyntaxException => None
}

如果有人能在这里阐明使用 map 和 flatMap 背后的基本原理,那就太好了。

最佳答案

TL;DR 直接进入最终示例

我会尝试重述一下。

定义

for理解是组合 flatMap 的语法快捷方式和map以易于阅读和推理的方式。

让我们稍微简化一下,假设每个 class提供上述两种方法的可以称为 monad我们将使用符号M[A]表示 monad具有内部类型A .

示例

一些常见的单子(monad)包括:

  • List[String]在哪里
    • M[X] = List[X]
    • A = String
  • Option[Int]在哪里
    • M[X] = Option[X]
    • A = Int
  • Future[String => Boolean]在哪里
    • M[X] = Future[X]
    • A = (String => Boolean)

map 和平面 map

在通用 monad 中定义 M[A]

 /* applies a transformation of the monad "content" mantaining the 
* monad "external shape"
* i.e. a List remains a List and an Option remains an Option
* but the inner type changes
*/
def map(f: A => B): M[B]

/* applies a transformation of the monad "content" by composing
* this monad with an operation resulting in another monad instance
* of the same type
*/
def flatMap(f: A => M[B]): M[B]

例如

  val list = List("neo", "smith", "trinity")

//converts each character of the string to its corresponding code
val f: String => List[Int] = s => s.map(_.toInt).toList

list map f
>> List(List(110, 101, 111), List(115, 109, 105, 116, 104), List(116, 114, 105, 110, 105, 116, 121))

list flatMap f
>> List(110, 101, 111, 115, 109, 105, 116, 104, 116, 114, 105, 110, 105, 116, 121)

用于表达

  1. 表达式中的每一行都使用 <-符号被转换为 flatMap调用,最后一行除外,该行被翻译为结论 map调用,其中左侧的“绑定(bind)符号”作为参数传递给参数函数(我们之前称为 f: A => M[B] ):

    // The following ...
    for {
    bound <- list
    out <- f(bound)
    } yield out

    // ... is translated by the Scala compiler as ...
    list.flatMap { bound =>
    f(bound).map { out =>
    out
    }
    }

    // ... which can be simplified as ...
    list.flatMap { bound =>
    f(bound)
    }

    // ... which is just another way of writing:
    list flatMap f
  2. 只有一个 <- 的 for 表达式转换为map使用作为参数传递的表达式进行调用:

    // The following ...
    for {
    bound <- list
    } yield f(bound)

    // ... is translated by the Scala compiler as ...
    list.map { bound =>
    f(bound)
    }

    // ... which is just another way of writing:
    list map f

现在进入正题

如您所见,map操作保留了原始的“形状”monad ,所以 yield 也会发生同样的情况表达式:a List仍然是List yield中的操作转换后的内容.

另一方面,for 中的每个绑定(bind)行只是连续的 monads 的组合,必须“压平”以保持单一的“外部形状”。

假设每个内部绑定(bind)都被转换为 map打电话,但右手是一样的 A => M[B]函数,你最终会得到 M[M[B]]对于理解中的每一行。
整体意图for语法的目的是轻松地“展平”连续的一元操作的串联(即以“一元形状”“提升”值的操作: A => M[B] ),并添加最后的 map可能执行最终转换的操作。

我希望这能解释翻译选择背后的逻辑,它以机械的方式应用,即:n flatMap由单个 map 结束的嵌套调用打电话。

一个人为的说明性示例
旨在展示 for 的表现力语法

case class Customer(value: Int)
case class Consultant(portfolio: List[Customer])
case class Branch(consultants: List[Consultant])
case class Company(branches: List[Branch])

def getCompanyValue(company: Company): Int = {

val valuesList = for {
branch <- company.branches
consultant <- branch.consultants
customer <- consultant.portfolio
} yield (customer.value)

valuesList reduce (_ + _)
}

你能猜出valuesList的类型吗?

如前所述,monad 的形状通过理解来维持,所以我们从 List 开始在company.branches ,并且必须以 List 结尾.
相反,内部类型会发生变化,并由 yield 确定。表达式:即customer.value: Int

valueList应该是List[Int]

关于scala - 与 FlatMap/Map 转换的 for 理解相混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14598990/

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