Scala中的map和filter组合会将空值过滤掉

8

是否可能将以下代码表达为一次调用,跳过map和null的部分?

list.map(_.accept(this, arg).asInstanceOf[T]).filter(_ != null)

我猜_.accept(this, arg)可能会返回null。在这种情况下,稍微更符合习惯的写法是(虽然仍需两个调用):list.map(i => Option(i.accept(this, arg).asInstanceOf[T])).flatten - Tomasz Nurkiewicz
1
请有经验的人纠正我,但是collect不是mapfilter的组合吗?还是反过来?或者我错了?;) - agilesteel
1
@agilesteel,是的,collect 可能会起作用。你可以贡献一段代码片段吗? - Timo Westkämper
2个回答

11
list flatMap { i => Option(i.accept(this, arg).asInstanceOf[T]) }

或者,如果您喜欢的话,(尽管这将更多或少地转换为您的原始表达方式)
for {
  item <- list
  itemConverted = item.accept(this, arg).asInstanceOf[T]
  itemNonNull = itemConverted if itemConverted != 0
} yield itemNonNull

可以使用collect,但由于部分函数的isDefinedAt测试,它可能会对大多数参数调用两次accept

list collect {
  case i if i.accept(this, arg).asInstanceOf[T] != null => i.accept(this, arg).asInstanceOf[T]
}

为了避免这种情况,需要使用一些记忆化(或智能提取器)。


8

如果您关注性能,可以添加.view

list.view.map(_.accept(this, arg).asInstanceOf[T]).filter(_ != null)

view命令使遍历过程变得惰性,因此mapfilter会在列表上进行一次遍历而不是两次独立的遍历。

如果您担心重复使用该模式,可以定义自己的帮助函数:

def mapNN[A,B](list: List[A])(f: A => B) = {
  list.view.map(f(_)).filter(_ != null)
}

mapNN(list)(_.accept(this, arg).asInstanceOf[T])

测试中...

> mapNN(List(1,2,3))(x => if (x%2==0) x else null).toList
res7: List[Any] = List(2)

你能详细解释一下这部分内容吗,即“因此,地图和过滤将在列表上执行一次而不是两次单独的传递”?它如何只需要一次遍历来应用2个转换? - Minh Thai
2
@MinhThai 这里有一个不错的view解释:https://dev59.com/lGw15IYBdhLWcg3wJ4YR#6799739 基本上,它使转换变得懒惰。 它甚至延迟了单个传递的执行,直到您调用toList,此时它会进行单个传递,并在进行增量式地应用每个转换。 - Dan Burton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接