How to make the first-not-null-result function more elegant / concise?

getFirstNotNullResult executes a list of functions until one of them returns a nonzero value. How to implement getNotNullFirstResult more elegantly / concisely?

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 } } 
+2
source share
3 answers
 def getFirstNN(fs: List[() => String]): String = fs.iterator.map(_()).find(_ ne null).get 
+8
source

I like Rex's answer, but your question raises so many things, I would like to expand it to add:

  • Using the Scala Option / Some / None classes to clarify what should be returned if no match is found. Your example returned null, Rex threw an exception. When using the option, it immediately becomes clear that we will return a match or "No".
  • Use type parameters, so you do not need to work only with functions that return a string.

Here is the code:

 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 _))) } 

Note that when this code works, f2 never prints, demonstrating that by calling .view we evaluate the minimum number of functions before returning a match.

Note that the calling calls to this method should now take into account the fact that a match cannot be found: instead of returning T, we return Option [T]. In our case above, this will return Some ("yay"). When all functions return None, the return value will be None. No more NullPointerExceptions when you accept null for an actual match!

+9
source

You will probably need the type passed to getFirstNotNullResult to be Stream [String] instead of List [() => String] and build it something like:

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

Then the getFirstNotNullResult variables will be:

 fs.filter(_ != null).headOption 

This also means that it should really return Option [String], since you cannot guarantee that something will be non-null.

As suggested, the reason I am suggesting Stream is that it only evaluates the tail of the stream on request. Therefore, if getFirstNotNullResult finds that the first element is not equal to zero, the second parameter for the first call to Stream.cons is never executed.

+5
source

Source: https://habr.com/ru/post/905080/


All Articles