如何使这个first-not-null-result函数更优雅/简洁?

时间:2012-01-03 16:46:10

标签: scala

getFirstNotNullResult执行函数列表,直到其中一个函数返回非null值。 如何更优雅/简洁地实现getNotNullFirstResult?

object A {
  def main(args: Array[String]) {
    println(test());
  }

  def test(): String = {
    getFirstNotNullResult(f1 _ :: f2 _ :: f3 _ :: Nil);
  }

  def getFirstNotNullResult(fs: List[() => String]): String = {
    fs match {
      case head::tail => 
        val v = head(); 
        if (v != null) return v;
        return getFirstNotNullResult(tail);

      case Nil => null
    }
  }

  // these would be some complex and slow functions; we only want to execute them if necessary; that is, if f1() returns not null, we don't want to execute f2 nor f3.
  def f1(): String = { null }
  def f2(): String = { "hello" }
  def f3(): String = { null }
}

3 个答案:

答案 0 :(得分:11)

我喜欢雷克斯的答案,但你的问题带来了很多东西,我想扩展它,添加:

  1. 使用Scala的Option/Some/None类来阐明未找到匹配项时应返回的内容。你的例子返回null,Rex引发了异常。使用Option会立即清楚我们将返回一个匹配或“无”。
  2. 使用类型参数,因此您不必仅对返回String的函数进行操作。
  3. 以下是代码:

    object A extends App {
    def getFirstNNWithOption[T](fs: List[() => Option[T]]): Option[T] = fs
        .view //allows us to evaluate your functions lazily: only evaluate as many as it takes to find a match 
        .flatMap(_()) //invoke the function, discarding results that return None
        .headOption // take the first element from the view - returns None if empty
    
    def f1 = { println("f1"); None }
    def f2 = Some("yay!")
    def f3 = { println("f2"); None }
    
    println(getFirstNNWithOption(List(f1 _, f2 _, f3 _)))
     }
    

    请注意,当此代码运行时,f2永远不会打印,这表明,由于.view调用,我们会在返回匹配项之前评估最小函数数。

    请注意,此方法的调用者现在必须考虑可能找不到匹配的事实:我们返回Option [T]而不是返回T.在上面的例子中,它将返回Some(“yay”)。当所有函数都返回None时,返回值将为None。当你将null误认为实际匹配时,不再有NullPointerExceptions!

答案 1 :(得分:9)

def getFirstNN(fs: List[() => String]): String = fs.iterator.map(_()).find(_ ne null).get

答案 2 :(得分:5)

您可能希望传入getFirstNotNullResult的类型为Stream [String]而不是List [()=>字符串]并构造如下:

Stream.cons(f1, Stream.cons(f2, Stream.cons(f3, Stream.empty)))

然后getFirstNotNullResult变为:

fs.filter(_ != null).headOption

这也意味着它应该真正返回Option [String],因为你不能保证某些东西是非空的。

正如所建议的那样,我建议Stream的原因是它只评估"尾部"按需流。因此,如果getFirstNotNullResult发现第一个元素不为null,那么第一个Stream.cons调用的第二个参数永远不会被实际执行。