我们说我有一个Scala列表List("apple", "orange", "banana", "chinese gooseberry")
*。我想搜索此列表并返回列表中与我已有项目相关的上一项。
例如:getPrevious(fruit: String, fruits: List[String]): Option[String]
应该返回
Some("apple")
如果我使用fruit
arg "orange"
来调用它; Some("banana")
代表"chinese gooseberry"
; None
如果我用"apple"
(不存在以前的元素)或"potato"
(列表中没有)来调用它。很容易完成,但我怎么能以优雅的功能方式做到这一点?我能想到的最好的是:
def previous(fruit: String, fruits: List[String]): Option[String] =
fruits.sliding(2)
.filter { case List(previous, current) => current == fruit }
.toList
.headOption
.map { case List(previous, current) => previous }
它有效,但它不优雅或高效。我特别讨厌转换filter
迭代器toList
。我该如何改进呢?
(*另外,List
是用于sliding
次迭代的最佳集合吗?)
答案 0 :(得分:12)
这是使用collectFirst
的较短版本:
def previous(fruit: String, fruits: List[String]): Option[String] =
fruits.sliding(2).collectFirst{ case List(previous, `fruit`) => previous}
请注意fruit
周围的反引号以匹配参数值。使用collectFirst
也将在第一次匹配时停止,而不是使用filter
遍历整个迭代器。
答案 1 :(得分:8)
我认为这是一种直接递归和模式匹配既高效又易于阅读的情况:
@annotation.tailrec
def getPrevious(fruit: String, fruits: List[String]): Option[String] = fruits match {
case Nil => None
case x :: `fruit` :: _ => Some(x)
case _ :: xs => getPrevious(fruit, xs)
}
答案 2 :(得分:1)
首先想到的解决方案是:
import scala.util.Try
def previous(fruit: String, fruits: List[String]) = Try(fruits(fruits.indexOf(fruit) - 1)).toOption
当然应该有效率更高。