与对平面图/地图转换的理解混淆 [英] Confused with the for-comprehension to flatMap/Map transformation

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

问题描述

我真的好像不了解Map和FlatMap。我不明白的是,一个理解是一个嵌套的map和flatMap的调用序列。以下示例来自 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 =>没有
}

如果有人可以看出使用背后的理由地图和平面图。

解决方案

TL; DR直接转到最终的例子



我会尝试重述



定义



理解的是一个语法快捷方式,用于将 flatMap map 以一种容易阅读和理解的方式。



让我们简化一些事情,并假设每个提供上述方法可以称为 monad ,我们将使用符号 M [A] 表示具有内部类型 A monad



示例



一些常见的monads




  • 列表[String] 其中


    • M [_]:列表[_]

    • A:String


  • Int] 其中


    • M [_]:选项[_]

    • A:Int


  • Future [String =>布尔] 其中


    • M [_]:未来[_]

    • A:String =>布尔值




/ strong>



在通用monad中定义 M [A]

  / *应用一个monad内容的转换,使
* monad外部形状
*即一个列表仍然是一个List选项仍然是一个选项
*,但内部类型更改
* /
def map(f:A => B):M [B]

/ *通过编写
*这个monad与导致另一个monad实例
*的相同类型
* /
def flatMap(f: A => M [B]):M [B]

eg

  val list = List(neo,smith,trinity)

//转换字符串对应的代码
val f:String =>列表[Int] = s => s.map(_。toInt).toList

列表地图f
>>名单(名单(110,101,111),名单(115,109,105,116,104),名单(116,114,105,110,105,116,121))

列表flatMap f
>>名单(110,101,111,115,109,105,116,104,116,114,105,110,105,116,121)

表达式


  1. 表达式中的每一行使用< - 符号被转换为 flatMap 调用,除了最后一行被转换为结论映射调用,其中左侧的绑定符号作为参数传递给参数函数(我们以前称为 f: A => M [B] ):

      //以下... 
    为{
    bound< - list
    out< - f(bound)
    } yield out

    // ...由Scala翻译编译为...
    list.flatMap {bound =>
    f(bound).map {out =>
    out
    }
    }

    // ...可以简化为...
    list.flatMap {bound =>
    f(bound)
    }

    // ...这只是另一种写作方式:
    list flatMap f


  2. 只有一个< - 的for-expression被转换为 map 调用表达式作为参数传递:

      //以下。 
    为{
    bound< - list
    } yield f(bound)

    // ...由Scala编译器翻译为...
    list.map {bound =>
    f(bound)
    }

    // ...这只是另一种写作方式:
    list map f


现在到



如您所见,地图操作保留原始 monad 操作的形状 ,所以对于 yield 表达式也是一样:列表仍然是列表收益中的操作转换的内容



另一方面,每个绑定行只是连续的 monads 的组合,必须扁平化才能维持一个外部形状



假设有一段时间,每个内部绑定被翻译成地图调用,但右边是相同的 A => M [B] 函数,你会得到一个 M [M [B]] 在理解中的每一行。

整个语法的意图是简单地扁平化连续的单体操作的连接(即提升单形中的值的操作) : A => M [B] ),添加最终地图操作,可能
执行一个结束转换



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



一个有创意的说明性例子

想要表达语法

 案例类客户(值:Int)
案例类顾问(投资组合:列表[客户])
案例分类(顾问:List [顾问])
案例分类公司(branche s:List [Branch]

def getCompanyValue(company:Company):Int = {

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

valueList reduce(_ + _)

您可以猜测 valuesList的类型



如上所述,通过理解来确定 monad 的形状,所以我们从列表 company.branches 中,必须以列表结尾

内部类型改变并由 yield 表达式决定:这是 customer.value:Int



valueList 应该是一个列表[Int] code>


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)

translates to

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

The mkMatcher method is defined as follows:

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

And the pattern method is as follows:

import java.util.regex._

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

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

解决方案

TL;DR go directly to the final example

I'll try and recap

Definitions

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

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.

Examples

Some commonly seen monads

  • List[String] where
    • M[_]: List[_]
    • A: String
  • Option[Int] where
    • M[_]: Option[_]
    • A: Int
  • Future[String => Boolean] where
    • M[_]: Future[_]
    • A: String => Boolean

map and flatMap

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]

e.g.

  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)

for expression

  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
    

  2. 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
    

Now to the point

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

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

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

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.

A contrived illustrative example
Meant to show the expressiveness of the for syntax

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)

  valueList reduce (_ + _)
}

Can you guess the type of valuesList?

As already said, the shape of the monad is mantained 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 should be a List[Int]

这篇关于与对平面图/地图转换的理解混淆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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