Ben*_*itz 6 monads scala optional scala-collections scala-option
在内部构建集合时Option,每次尝试使集合的下一个成员都可能失败,从而使集合整体失败.在第一次未能成为会员时,我想立即放弃并返回None整个收藏.在Scala中执行此操作的惯用方法是什么?
这是我提出的一种方法:
def findPartByName(name: String): Option[Part] = . . .
def allParts(names: Seq[String]): Option[Seq[Part]] =
names.foldLeft(Some(Seq.empty): Option[Seq[Part]]) {
(result, name) => result match {
case Some(parts) =>
findPartByName(name) flatMap { part => Some(parts :+ part) }
case None => None
}
}
Run Code Online (Sandbox Code Playgroud)
换句话说,如果任何调用findPartByName返回None,则allParts返回None.否则,allParts返回Some包含集合的集合Parts,所有集合都保证有效.空集合是可以的.
以上优点是它findPartByName在第一次失败后停止呼叫.但foldLeft无论如何,仍然会为每个名称迭代一次.
这是一个版本,只要findPartByName返回一个拯救None:
def allParts2(names: Seq[String]): Option[Seq[Part]] = Some(
for (name <- names) yield findPartByName(name) match {
case Some(part) => part
case None => return None
}
)
Run Code Online (Sandbox Code Playgroud)
我目前发现第二个版本更具可读性,但是(a)看起来最具可读性的东西可能随着我获得Scala的更多经验而改变,(b)我得到的印象是早期return在Scala中不受欢迎,并且(c)两者都没有似乎对我来说特别明显的事情.
"全有或否"和"放弃第一次失败"的组合看起来像是一个基本的编程概念,我认为必须有一个共同的Scala或功能成语来表达它.
在return你的代码实际上是一对夫妇的深层次匿名函数.因此,必须通过抛出外部函数中捕获的异常来实现它.这不高效或漂亮,因此皱着眉头.
使用while循环和循环来编写它是最简单和最有效的Iterator.
def allParts3(names: Seq[String]): Option[Seq[Part]] = {
val iterator = names.iterator
var accum = List.empty[Part]
while (iterator.hasNext) {
findPartByName(iterator.next) match {
case Some(part) => accum +:= part
case None => return None
}
}
Some(accum.reverse)
}
Run Code Online (Sandbox Code Playgroud)
因为我们不知道是什么类型,Seq names所以我们必须创建一个迭代器来有效地循环它,而不是使用tail或索引.while循环可以用尾递归内部函数替换,但是使用迭代器while循环更清晰.