混淆了 for-comprehension 到 flatMap/Map 的转换 [英] Confused with the for-comprehension to flatMap/Map transformation

查看:18
本文介绍了混淆了 for-comprehension 到 flatMap/Map 的转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我似乎真的不太了解 Map 和 FlatMap.我未能理解的是 for-comprehension 如何是对 map 和 flatMap 的嵌套调用序列.以下示例来自 Scala 中的函数式编程

I really don't seem to be understanding Map and FlatMap. What I am failing to understand is how a for-comprehension is a sequence of nested calls to map and flatMap. The following example is from 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 方法定义如下:

The mkMatcher method is defined as follows:

  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 背后的基本原理,那就太好了.

It will be great if someone could shed some light on the rationale behind using map and flatMap here.

推荐答案

TL;DR 直接看最后的例子

我会试着回顾一下.

定义

for 推导式是以一种易于阅读和推理的方式组合 flatMapmap 的语法快捷方式.

The for comprehension is a syntax shortcut to combine flatMap and map in a way that's easy to read and reason about.

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

Let's simplify things a bit and assume that every class that provides both aforementioned methods can be called a monad and we'll use the symbol M[A] to mean a monad with an inner type A.

示例

一些常见的 monad 包括:

Some commonly seen monads include:

  • List[String] 其中
    • M[X] = 列表[X]
    • A = String
    • M[X] = Option[X]
    • A = Int
    • M[X] = Future[X]
    • A = (String => Boolean)

    地图和平面地图

    定义在一个通用的 monad M[A]

    Defined in a generic 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 调用,其中左侧的绑定符号"作为参数传递给参数函数(我们之前称为 f: A => M[B]):

    1. Each line in the expression using the <- symbol is translated to a flatMap call, except for the last line which is translated to a concluding map call, where the "bound symbol" on the left-hand side is passed as the parameter to the argument function (what we previously called 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
    

  • 只有一个 <- 的 for 表达式被转换为 map 调用,并将表达式作为参数传递:

  • A for-expression with only one <- is converted to a map call with the expression passed as argument:

    // 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 表达式也是如此:一个List仍然是一个List,内容由yield中的操作转换.

    As you can see, the map operation preserves the "shape" of the original monad, so the same happens for the yield expression: a List remains a List with the content transformed by the operation in the yield.

    另一方面,for 中的每个绑定行只是连续的monads 的组合,必须展平"以保持单个外部形状".

    On the other hand each binding line in the for is just a composition of successive monads, which must be "flattened" to maintain a single "external shape".

    假设每个内部绑定都被转换为一个 map 调用,但右侧是相同的 A =>;M[B] 函数,你最终会得到一个 M[M[B]] 用于理解中的每一行.
    整个 for 语法的意图是轻松地扁平化"连续的一元操作的串联(即提升"一个一元形状"中的值的操作:A => M[B]),加上最后的 map 操作,可能 执行结束转换.

    Suppose for a moment that each internal binding was translated to a map call, but the right-hand was the same A => M[B] function, you would end up with a M[M[B]] for each line in the comprehension.
    The intent of the whole for syntax is to easily "flatten" the concatenation of successive monadic operations (i.e. operations that "lift" a value in a "monadic shape": A => M[B]), with the addition of a final map operation that possibly performs a concluding transformation.

    我希望这解释了翻译选择背后的逻辑,它以一种机械的方式应用,即:n flatMap 嵌套调用由单个 map 调用.

    I hope this explains the logic behind the choice of translation, which is applied in a mechanical way, that is: n flatMap nested calls concluded by a single map call.

    人为的说明性示例
    旨在展示 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的形状是通过推导保持的,所以我们从company.branches中的一个List开始,并且必须以 List 结尾.
    内部类型改为更改并由 yield 表达式确定:即 customer.value: Int

    As already said, the shape of the monad is maintained through the comprehension, so we start with a List in company.branches, and must end with a List.
    The inner type instead changes and is determined by the yield expression: which is customer.value: Int

    valueList 应该是一个 List[Int]

    这篇关于混淆了 for-comprehension 到 flatMap/Map 的转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆