В дополнение к ответу Даниэля, имейте в виду, что Stream
полезен для кратковременных оценок. Например, предположим, у меня есть огромный набор функций, которые принимают String
и возвращают Option[String]
, и я хочу продолжать выполнять их, пока одна из них не заработает:
val stringOps = List(
(s:String) => if (s.length>10) Some(s.length.toString) else None ,
(s:String) => if (s.length==0) Some("empty") else None ,
(s:String) => if (s.indexOf(" ")>=0) Some(s.trim) else None
);
Ну, я определенно не хочу выполнять весь список, и на List
нет никакого удобного метода, который говорит: "обрабатывать их как функции и выполнять до тех пор, пока одна из них не будет выполнена возвращает что-то отличное от None
". Что делать? Возможно это:
def transform(input: String, ops: List[String=>Option[String]]) = {
ops.toStream.map( _(input) ).find(_ isDefined).getOrElse(None)
}
Это берет список и обрабатывает его как Stream
(который на самом деле ничего не оценивает), затем определяет новый Stream
, который является результатом применения функций (но он еще ничего не оценивает) ), затем ищет первый, который определен - и здесь, волшебным образом, он оглядывается назад и понимает, что должен применить карту и получить правильные данные из исходного списка - и затем разворачивает его из Option[Option[String]]
в Option[String]
с использованием getOrElse
.
Вот пример:
scala> transform("This is a really long string",stringOps)
res0: Option[String] = Some(28)
scala> transform("",stringOps)
res1: Option[String] = Some(empty)
scala> transform(" hi ",stringOps)
res2: Option[String] = Some(hi)
scala> transform("no-match",stringOps)
res3: Option[String] = None
Но это работает? Если мы добавим println
в наши функции, чтобы мы могли определить, вызваны ли они, мы получим
val stringOps = List(
(s:String) => {println("1"); if (s.length>10) Some(s.length.toString) else None },
(s:String) => {println("2"); if (s.length==0) Some("empty") else None },
(s:String) => {println("3"); if (s.indexOf(" ")>=0) Some(s.trim) else None }
);
// (transform is the same)
scala> transform("This is a really long string",stringOps)
1
res0: Option[String] = Some(28)
scala> transform("no-match",stringOps)
1
2
3
res1: Option[String] = None
(Это с Scala 2.8; реализация 2.7, к сожалению, иногда будет сбрасываться на единицу. И обратите внимание, что do накапливает длинный список None
по мере накопления ошибок, но, вероятно, это Недорого по сравнению с вашими настоящими вычислениями.)