我现在正在阅读红皮书“ Scala中的功能编程”,因此同时我也在学习Scala。如果我理解正确,特质并不意味着对象。如果我错了,请纠正我。
我的问题是,我不知道如何将A类型的列表包装在Some
特征内。我希望方向正确的提示。
在我做的练习中,要求我定义一个函数,该函数用于转换列表中的每个元素,然后将整个列表包含在Some
特性内。
这是我的代码:
def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = a match {
case Nil => Nil: B
case h :: t => f(h) flatMap ( hh => hh :: traverse(t)(f))
}
我觉得自己在正确的轨道上,但是scala解释器抱怨::
无法使用Option[List[B]]
。我认为这是因为函数的类型签名不返回列表,而是返回包装在Some中的List。
但是我对 flatMap 的直觉也可能是错误的吗? f(h)
返回Option[B]
。调用flatmap实际上看起来是在Option内部,所以hh是B
类型的吗?我的逻辑是,通过这种方式,我可以使用函数hh :: traverse(t)(f)
构造类型B的列表。但是我不确定是否会在这里。
如果有什么不同,我正在使用scala解释器和:paste命令。我不确定这是否会使事情变得不合时宜。
答案 0 :(得分:3)
如果我理解正确,特质并不意味着对象。
特质是特质,物是物。特性有点像Java中的“接口”,object
是单例对象。这些单例对象可以从特征扩展。
Some
特征
Some
不是特征,它是class extending Option
。
它返回包装在Some中的列表。
它返回一个包装在Option
内的列表(即可以是None
,根本没有任何列表)。为了在可选列表值上调用::
,您需要另一个map
:
def traverse[A, B](a: List[A])(f: A => Option[B])
: Option[List[B]] = a match {
case Nil => Some(Nil)
case h :: t => f(h) flatMap {
hValue => traverse(t)(f) map {
tValue => hValue :: tValue
}
}
}
您可以缩写为:
def traverse[A, B](a: List[A])(f: A => Option[B])
: Option[List[B]] = a match {
case Nil => Some(Nil)
case h :: t => f(h) flatMap {
hValue => traverse(t)(f) map (hValue :: _)
}
}
或立即使用for
理解:
def traverse[A, B](a: List[A])(f: A => Option[B])
: Option[List[B]] = a match {
case Nil => Some(Nil)
case h :: t => for {
hValue <- f(h)
tValue <- traverse(t)(f)
} yield (hValue :: tValue)
}
答案 1 :(得分:2)
更多关于单子运算的信息。 Option
是monad,但将其视为具有零个或一个值的容器可能会更容易。像List()
一样没有元素,或者List(x)
却只有一个元素。要修改隐藏在容器中的元素,您需要map
:
List(1).map(_ + 1) => List(2)
Some(1).map(_ + 1) => Some(2)
但是map
无法更改列表中的元素数量。因此,您需要的是另一个名为flatMap
的操作,它需要一个函数,该函数又要接受一个元素并返回一个List
:
List(1).flatMap { x => List() } => List()
Some(1).flatMap { x => None } => None
List(1).flatMap { x => List(x + 1) } => List(2)
Some(1).flatMap { x => Some(x + 1) } => Some(2)
这将传播“失败”,这是一个空列表,因为List().flatMap { ...whatever...}
将始终返回一个空列表。
traverse
就是这样做的,如果任何元素的函数f
返回None
(即我的解释是List()
),则传播“失败”并合并非失败结果进入容器内的新列表。由于您从traverse
获得的是一个内部包含列表的容器,因此您需要应用map
。并且由于应该传播“故障”(如果发生),因此您需要flatMap
。