我正在进行"Scala中的函数式编程"练习,在第4章中,有一段关于Option类的代码,我认为我很难理解。
我被要求实现flatMap。我知道Map是指将A转换为B,然后将其封装在Option中。那么flatMap的目的到底是什么呢?如果我没有记错的话,在列表中实现的flatMap意味着采用一个函数,将a转换为B.A => List[B]
的List。原则上,我想这也适用于这里。Option只是一个包含一个元素的列表,我将给它一个函数,该函数旨在获取一个a并将其封装在Option中。
我不知道如何使用Map实现flatMap。显而易见的实现方式很简单:
def flatMap(f: A => Option[B]): Option[B] = map(f) getOrElse None
为什么这种实施有效?f
没有A=>B
的类型签名,但它用这个签名调用map就可以了。显然,调用getOrElse(None(是它工作的原因,因为如果我去掉它,实现就会开始抱怨。但我不明白为什么scala解释器没有在这里抱怨。我们知道f
返回一个Option[B]
。返回的Option是否调用getOrElse,然后打开其值,以便具有正确的类型签名?如果是这样的话,有没有其他方法可以更明显地说明为什么它会起作用?
def flatMap[B](f: A => Option[B]): Option[B] =
map((a)=> f(a).getOrElse(None)) // doesn't work
不幸的是,这本书并没有真正深入地介绍scala,所以我不确定这是语法(非常确定它的语法(还是我不理解的逻辑。
import scala.{Option => _, Some => _, Either => _, _} // hide std library `Option`, `Some` and `Either`, since we are writing our own in this chapter
sealed trait Option[+A] {
def map[B](f: A => B): Option[B] = this match {
case Some(a) => Some(f(a))
case None => None
}
def getOrElse[B>:A](default: => B): B = this match {
case None => default
case Some(b) => b
}
def flatMap[B](f: A => Option[B]): Option[B] =
map(f) getOrElse(None)
def orElse[B>:A](ob: => Option[B]): Option[B] = ???
def filter(f: A => Boolean): Option[A] = ???
}
case class Some[+A](get: A) extends Option[A]
case object None extends Option[Nothing]
object Option {
def mean(xs: Seq[Double]): Option[Double] =
if (xs.isEmpty) None
else Some(xs.sum / xs.length)
def variance(xs: Seq[Double]): Option[Double] = ???
def map2[A,B,C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] = ???
def sequence[A](a: List[Option[A]]): Option[List[A]] = ???
def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = ???
}
你对flatMap
含义的直觉是完全正确的。我相信您对实现的困惑源于B
是每个方法定义的类型参数,因此,当一个方法调用另一个方法时,它不必具有相同的值。
为了显示这一点,让我们简单地给这些方法的类型参数不同的名称:
def map[B](f: A => B): Option[B] = this match {
case Some(a) => Some(f(a))
case None => None
}
def getOrElse[C>:A](default: => C): C = this match {
case None => default
case Some(b) => b
}
def flatMap[D](f: A => Option[D]): Option[D] = {
map(f) getOrElse None
}
现在:
flatMap
有一个类型参数D
,它可以是任何东西- 当
flatMap
调用map
时,它将Option[D]
指定为map
的类型参数B
(!(的值。就map
而言,它被传递了一个函数f
到某种类型的B
,这实际上是某种类型D
的Option[D]
(但map
不在乎!( - 当
flatMap
调用getOrElse
时——类似的事情发生了,这次getOrElse
的类型参数C
被分配了值Option[B]
——同样,getOrElse
只是"做它的事情",而不关心输出恰好是Option
本身
这样,返回的值是:
None
,如果原始输入是None
(因为getOrElse
将返回其传递的default
(- 如果
f
返回None
,则返回None
(因为getOrElse
将返回其获得的Some
中的值,该值本身就是None
( - 如果
f
的输入和结果均为Some
,则为Some(v)
这正是flatMap
应该做的。