Как сделать эту функцию с первым, а не нулевым результатом более элегантной / лаконичной? - PullRequest
3 голосов
/ 03 января 2012

getFirstNotNullResult выполняет список функций, пока одна из них не вернет ненулевое значение. Как реализовать 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 ]

11 голосов
/ 03 января 2012

Мне нравится ответ Рекса, но ваш вопрос поднимает так много вещей, что я хотел бы расширить его, добавив:

  1. Использование классов Scala Option / Some / None , чтобы уточнить, что следует возвращать, если совпадений не найдено. Ваш пример возвратил нуль, Рекс бросил исключение. Использование Option сразу дает понять, что мы вернем совпадение или «Нет».
  2. Используйте параметры типа, чтобы вам не приходилось работать только с функциями, которые возвращают строку.

Вот код:

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 мы оцениваем минимальное количество функций перед возвратом совпадения.

Обратите внимание, что вызывающие этот метод теперь должны учитывать тот факт, что совпадение может не быть найдено: вместо возврата T мы возвращаем Option [T]. В нашем случае выше, он вернул бы Some ("yay"). Когда все функции возвращают None, возвращаемое значение будет None. Больше нет исключений NullPointerException, если вы приняли нулевое значение за фактическое совпадение!

10 голосов
/ 03 января 2012
def getFirstNN(fs: List[() => String]): String = fs.iterator.map(_()).find(_ ne null).get
5 голосов
/ 03 января 2012

Вероятно, вы захотите, чтобы тип, переданный в getFirstNotNullResult, был Stream [String] вместо List [() => String] и сконструировал его примерно так:

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

Тогда getFirstNotNullResult изменится на:

fs.filter(_ != null).headOption

Это также будет означать, что он должен действительно возвращать Option [String], так как вы не можете гарантировать, что что-то будет ненулевым.

Как и предполагалось,Причина, по которой я предлагаю Stream, заключается в том, что он оценивает только «хвост» Stream по требованию.Поэтому, если getFirstNotNullResult обнаруживает, что первый элемент не равен нулю, тогда второй параметр первого вызова Stream.cons фактически никогда не выполняется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...