我似乎真的不了解Map和FlatMap。我无法理解的是,for-comprehensiion是如何对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 => None
}
如果有人能在这里阐明使用map和flatMap背后的基本原理,那就太好了。
其基本原理是链接 monadic 操作,这提供了一个好处,即适当的“快速失败”错误处理。
其实很简单。mkMatcher
方法返回一个选项
(这是一个单变量)。mkMatcher
的一元运算结果要么是None
,要么是Some(x)
。
将map
或平面地图
函数应用于无
总是返回无
-作为参数传递给map
和平面地图
的函数不计算。
因此,在您的示例中,如果 mkMatcher(pat)
返回 None,则应用于它的 flatMap 将返回 None
(不会执行第二个一元运算 mkMatcher(pat2),
并且最终映射
将再次返回 None
。换句话说,如果 for understanding 中的任何操作返回 None,则表示您有一个快速失败行为,其余操作未执行。
这是一元错误处理风格。命令式样式使用异常,基本上是跳转(到catch子句)
最后注意:模式
函数是使用Option
将命令式样式错误处理(try
...catch
)“转换”为一元样式错误处理的典型方式
我不是scala超级头脑,所以请随意纠正我,但这是我对自己解释< code > flat map/map/for-comprehension 传奇的方式!
为了理解<code>的理解</code>及其到<code>scala地图/平面地图的转换</code>我们必须采取一些小步骤,理解组成部分-<code>地图</code>和<code>平面地图</code>。但是,你问自己,scala的平面图不是<code>吗?<code>只是<code>地图</code>中的<code<展平</code>而已!如果是这样的话,为什么那么多开发人员发现很难掌握它或<code>来理解/flatMap/map。如果你只看scala的<code>映射</code>和<code>平面映射</code>签名,你会发现它们返回相同的返回类型<code>M[B],它们使用相同的输入参数<code>A</code>(至少是它们所用函数的第一部分),如果是这样,有什么区别?
我们的计划
斯卡拉的地图
scala map signature:
map[B](f: (A) => B): M[B]
但是,当我们看这个签名时,有一个很大的部分丢失了,那就是——这个< code>A来自哪里?我们的容器属于< code>A类型,因此在容器的上下文中查看该函数非常重要。我们的容器可以是类型为< code>A的项目的< code >列表,我们的< code>map函数接受一个函数,该函数将类型为< code>A的每个项目转换为类型为< code>B的项目,然后返回类型为< code>B(或< code>M[B])的容器
让我们在考虑容器的情况下编写 map 的签名:
M[A]: // We are in M[A] context.
map[B](f: (A) => B): M[B] // map takes a function which knows to transform A to B and then it bundles them in M[B]
请注意一个关于map的非常非常重要的事实——它会自动捆绑在输出容器M[B]
中,您无法控制它。让我们再次强调它:
map
为我们选择输出容器,它将与我们处理的源容器是同一个容器,因此对于< code>M[A]容器,我们只为< code>B
M[B]
获得同一个< code>M容器,其他什么也没有! < li>
map
为我们实现了这种容器化,我们只需给出从< code>A到< code>B的映射,它会将它放入< code>M[B]的框中,它会将它放入框中!
您会看到您没有指定如何容器化
刚刚指定的项如何转换内部项。由于我们对M[A]
和M
[B]
都有相同的容器M,这意味着M[B]
是相同的容器,这意味着如果你有List[A]
,那么你将有一个List[B]
,更重要的是map
是为你做的!
既然我们已经处理了地图
,让我们继续讨论平面地图
。
Scala的平面图
让我们看看它的签名:
flatMap[B](f: (A) => M[B]): M[B] // we need to show it how to containerize the A into M[B]
你可以看到在
flatMap中从map到flatMap
的巨大差异,我们为其提供了一个功能,该功能不仅可以从A转换为B
,还可以将其容器化为M[B]
。
为什么我们关心谁做集装箱运输?
那么,为什么我们如此关注map/flatMap的输入函数,是将容器化为< code>M[B]
还是map本身为我们进行html" target="_blank">容器化呢?
您可以在理解的上下文中看到正在发生的事情是
for
中提供的项目的多次转换,因此我们赋予装配线中的下一个工人确定包装的能力。想象一下,我们有一条装配线,每个工人都对产品做了一些事情,只有最后一个工人将其包装在容器中!欢迎来到平面图
这是它的目的,在map
中,每个工人在完成对项目的工作时也将其打包,以便您在容器上获得容器。
理解力最强的人
现在让我们来看看你的理解,考虑到我们上面所说的:
def bothMatch(pat:String,pat2:String,s:String):Option[Boolean] = for {
f <- mkMatcher(pat)
g <- mkMatcher(pat2)
} yield f(s) && g(s)
我们得到了什么:
>
mkMatcher
返回一个容器
。该容器包含一个函数:字符串=
mkMatcher(pat)flat map(f//pull out f function)将物品交给下一个装配线工人(您可以看到它可以访问< code>f
,并且不要将其打包回去我的意思是让地图确定包装让下一个装配线工人确定容器。mkMatcher(pat2)映射(g =
太长别读直接到最后一个例子
我试着复述一下。
用于理解的是一种语法快捷方式,可以以易于阅读和推理的方式组合
flatMap
和。
让我们稍微简化一下,假设提供上述两种方法的每个
类
都可以称为monad
,我们将使用符号M[a]
来表示具有内部类型a
的monad
。
举例
一些常见的monads包括:
列出[String]
所在
M[X]=列表[X]
A=字符串
M[X]=选项[X]
A=Int
M[X] = 未来[X]
A = (字符串 =
地图和平面地图
定义于泛型 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)
为了表达
> < li>
表达式中的每一行使用< code >
// 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表达式
// 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
现在说到点子上
如您所见,映射
操作保留了原始单子
的“形状”,因此产量表达式也是如此:一个列表
仍然是一个列表</code>,其内容由收益
中的操作转换。
另一方面,for
中的每条绑定线只是连续monads
的组合,必须将其“扁平化”以保持单个“外部形状”。
假设每个内部绑定都被转换成一个< code>map
调用,但是右边的是同一个< code>A =
我希望这能解释翻译选择背后的逻辑,它是以机械的方式应用的,即: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 (_ + _)
}
你能猜出< code>valuesList
的类型吗?
如前所述,< code>monad的形状是通过理解来保持的,因此我们从< code>company.branches中的< code>List开始,并且必须以< code>List结束。< br >内部类型会发生变化,并由< code>yield表达式确定:即< code>customer.value: Int
value eList
应该是一个List[Int]
给定和: 如何将此转换为? 请注意,我提供了上述内容,作为在Scala中使用for constancement、Try和sequences的答案,但在将其转换为
如何将此地图/平面图转换为便于理解的图形,请解释其工作原理,谢谢。
对于理解Scala中的以下代码段,什么是最好的(无需线性/回调、较少样板)形式?
本文向大家介绍java json与map互相转换的示例,包括了java json与map互相转换的示例的使用技巧和注意事项,需要的朋友参考一下 第一种 1.准备工作 所需jar包:json-20180130.jar 2.json转map 用法:JSONObject.toMap(); 3.map转json 用法:new JSONObject(map); 4.举例说明 第二种 1.准备工作
有人能帮忙吗?
我在一个类中有以下代码,它扩展了Primefaces的 在上面的过滤器是类型