15

我有一个清单l:List[T1],目前我正在做以下事情:

myfun : T1 -> Option[T2]
val x: Option[T2] = l.map{ myfun(l) }.flatten.find(_=>true)

myfun函数返回 None 或 Some,flatten 丢弃所有的 None 并 find 返回列表的第一个元素(如果有)。

这对我来说似乎有点hacky。我认为可能存在一些便于理解或类似的东西,这会减少浪费或更聪明。例如:如果在 list期间myfun返回任何 答案,我不需要任何后续答案。Somemapl

4

5 回答 5

13

怎么样:

l.toStream flatMap (myfun andThen (_.toList)) headOption

Stream 是惰性的,因此它不会提前映射所有内容,但也不会重新映射。不要将事物展平,而是转换OptionList可以flatMap使用的东西。

于 2010-09-04T22:52:51.447 回答
4

除了用于toStream使搜索变得懒惰之外,我们还可以使用Stream::collectFirst

List(1, 2, 3, 4, 5, 6, 7, 8).toStream.map(myfun).collectFirst { case Some(d) => d }
// Option[String] = Some(hello)
// given def function(i: Int): Option[String] = if (i == 5) Some("hello") else None

这:

  • 将 转换List为 aStream以便尽早停止搜索。

  • myFun使用as Option[T]s转换元素。

  • 收集不是的第一个映射元素None并提取它。

从s 开始Scala 2.13,弃用Streams 以支持LazyLists,这将变为:

List(1, 2, 3, 4, 5, 6, 7, 8).to(LazyList).map(function).collectFirst { case Some(d) => d }
于 2018-10-02T00:10:58.450 回答
3

嗯,这差不多,但不完全

val x = (l flatMap myfun).headOption

但是您从 myfun 返回 aOption而不是 a List,所以这可能行不通。如果是这样(我手头没有 REPL),那么请尝试:

val x = (l flatMap(myfun(_).toList)).headOption
于 2010-09-04T22:14:18.577 回答
2

好吧,理解等价物很容易

(for(x<-l, y<-myfun(x)) yield y).headOption

如果您真的进行翻译,则翻译结果与 oxbow_lakes 给出的相同。假设 List.flatmap 有合理的惰性,这既是一个干净又高效的解决方案。

于 2010-09-04T22:15:20.337 回答
0

截至 2017 年,之前的答案似乎已经过时。我运行了一些基准测试(1000 万个 Ints 列表,第一个匹配大致在中间,Scala 2.12.3、Java 1.8.0、1.8 GHz Intel Core i5)。除非另有说明,否则listmap以下几种:

list: scala.collection.immutable.List
map: A => Option[B]

只需调用map列表:~1000 ms

list.map(map).find(_.isDefined).flatten

列表中的第一个调用toStream:~1200 ms

list.toStream.map(map).find(_.isDefined).flatten

列表调用toStream.flatMap:~450 ms

list.toStream.flatMap(map(_).toList).headOption

在列表中调用flatMap:~100 ms

list.flatMap(map(_).toList).headOption

列表中的第一个调用iterator:~35 ms

list.iterator.map(map).find(_.isDefined).flatten

递归函数find():~25 ms

def find[A,B](list: scala.collection.immutable.List[A], map: A => Option[B]) : Option[B] = {
  list match {
    case Nil => None
    case head::tail => map(head) match {
      case None => find(tail, map)
      case result @ Some(_) => result
    }
  }
}

迭代函数find():~25 ms

def find[A,B](list: scala.collection.immutable.List[A], map: A => Option[B]) : Option[B] = {
  for (elem <- list) {
    val result = map(elem)
    if (result.isDefined) return result
  }
  return None
}

您可以通过使用 Java 而不是 Scala 集合和功能较少的样式来进一步加快速度。

循环索引java.util.ArrayList:~15 ms

def find[A,B](list: java.util.ArrayList[A], map: A => Option[B]) : Option[B] = {
  var i = 0
  while (i < list.size()) {
    val result = map(list.get(i))
    if (result.isDefined) return result
    i += 1
  }
  return None
}

java.util.ArrayList使用函数返回null而不是循环遍历索引None:~10 ms

def find[A,B](list: java.util.ArrayList[A], map: A => B) : Option[B] = {
  var i = 0
  while (i < list.size()) {
    val result = map(list.get(i))
    if (result != null) return Some(result)
    i += 1
  }
  return None
}

(当然,通常将参数类型声明为java.util.List,而不是java.util.ArrayList。我在这里选择后者,因为它是我用于基准测试的类。其他实现java.util.List将显示不同的性能 - 大多数会更糟。)

于 2017-12-22T08:20:20.927 回答