与对平面图/地图转换的理解混淆 [英] Confused with the for-comprehension to flatMap/Map transformation
问题描述
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)
表达式
-
表达式中的每一行使用
< -
符号被转换为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
-
只有一个
< -
的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]
whereM[_]: List[_]
A: String
Option[Int]
whereM[_]: Option[_]
A: Int
Future[String => Boolean]
whereM[_]: 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
Each line in the expression using the
<-
symbol is translated to aflatMap
call, except for the last line which is translated to a concludingmap
call, where the "bound symbol" on the left-hand side is passed as the parameter to the argument function (what we previously calledf: 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
A for-expression with only one
<-
is converted to amap
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屋!