Ответы:
Он используется в понимании последовательностей (например, в списках и генераторах Python, где вы yield
тоже можете использовать ).
Он применяется в сочетании с for
и записывает новый элемент в результирующую последовательность.
Простой пример (из Скала-Ланга )
/** Turn command line arguments to uppercase */
object Main {
def main(args: Array[String]) {
val res = for (a <- args) yield a.toUpperCase
println("Arguments: " + res.toString)
}
}
Соответствующее выражение в F # будет
[ for a in args -> a.toUpperCase ]
или
from a in args select a.toUpperCase
в Линк.
Ruby's yield
имеет другой эффект.
Я думаю, что принятый ответ великолепен, но, похоже, многим не удалось понять некоторые фундаментальные моменты.
Во-первых, for
понимание Scala эквивалентно do
нотации Хаскелла , и это не более чем синтаксический сахар для составления множества монадических операций. Поскольку это утверждение, скорее всего, не поможет никому, кто нуждается в помощи, давайте попробуем еще раз… :-)
Понимание Scala for
является синтаксическим сахаром для составления множества операций с картой, flatMap
и filter
. Или foreach
. Scala фактически переводит for
-выражение в вызовы этих методов, поэтому любой класс, предоставляющий их, или их подмножество, можно использовать для понимания.
Сначала поговорим о переводах. Есть очень простые правила:
это
for(x <- c1; y <- c2; z <-c3) {...}
переводится на
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
это
for(x <- c1; y <- c2; z <- c3) yield {...}
переводится на
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
это
for(x <- c; if cond) yield {...}
переведено на Scala 2.7 в
c.filter(x => cond).map(x => {...})
или на Scala 2.8, в
c.withFilter(x => cond).map(x => {...})
с запасным вариантом, если метод withFilter
недоступен, но filter
есть. Пожалуйста, смотрите раздел ниже для получения дополнительной информации по этому вопросу.
это
for(x <- c; y = ...) yield {...}
переводится на
c.map(x => (x, ...)).map((x,y) => {...})
Когда вы смотрите на очень простые for
постижениях, что map
/ foreach
альтернативы выглядят, действительно, лучше. Однако, как только вы начнете их составлять, вы можете легко потеряться в скобках и уровнях вложенности. Когда это происходит, for
понимание обычно намного яснее.
Я покажу один простой пример и намеренно опущу любое объяснение. Вы можете решить, какой синтаксис было легче понять.
l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length))
или
for {
sl <- l
el <- sl
if el > 0
} yield el.toString.length
withFilter
В Scala 2.8 появился метод с именем withFilter
, основное отличие которого заключается в том, что вместо возврата новой отфильтрованной коллекции он фильтрует по требованию. filter
Метод имеет свое поведение , определенное на основе строгости коллекции. Чтобы лучше это понять, давайте взглянем на Scala 2.7 с List
(строгим) и Stream
(не строгим):
scala> var found = false
found: Boolean = false
scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
7
9
scala> found = false
found: Boolean = false
scala> Stream.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
Разница возникает потому, что filter
сразу применяется с List
, возвращая список шансов - так как found
есть false
. Только тогда foreach
выполняется, но к этому времени изменение не found
имеет смысла, как filter
уже выполнено.
В случае Stream
, условие не применяется немедленно. Вместо этого, поскольку каждый элемент запрашивается foreach
, filter
проверяется условие, которое позволяет foreach
на него влиять found
. Просто чтобы прояснить, вот эквивалентный код для понимания:
for (x <- List.range(1, 10); if x % 2 == 1 && !found)
if (x == 5) found = true else println(x)
for (x <- Stream.range(1, 10); if x % 2 == 1 && !found)
if (x == 5) found = true else println(x)
Это вызвало много проблем, потому что люди ожидали, что они if
будут рассматриваться по требованию, а не применяться ко всей коллекции заранее.
Представлена Scala 2.8 withFilter
, которая всегда не является строгой, независимо от строгости коллекции. Следующий пример показывает List
оба метода в Scala 2.8:
scala> var found = false
found: Boolean = false
scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
7
9
scala> found = false
found: Boolean = false
scala> List.range(1,10).withFilter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
Это дает результат, которого ожидает большинство людей, без изменения filter
поведения. В качестве примечания, Range
между Scala 2.7 и Scala 2.8 был изменен с нестрогого на строгий.
withFilter
Предполагается, что он также не является строгим, даже для строгих коллекций, что заслуживает некоторого объяснения Я рассмотрю это ...
for(x <- c; y <- x; z <-y) {...}
переведено на c.foreach(x => x.foreach(y => y.foreach(z => {...})))
2. for(x <- c; y <- x; z <- y) yield {...}
переведено наc.flatMap(x => x.flatMap(y => y.map(z => {...})))
for(x <- c; y = ...) yield {...}
действительно переведено на c.map(x => (x, ...)).map((x,y) => {...})
? Я думаю, что это переведено c.map(x => (x, ...)).map(x => { ...use x._1 and x._2 here...})
или я что-то упустил?
Да, как сказал Уорвикер, он в значительной степени эквивалентен LINQ select
и имеет мало общего с Ruby и Python yield
. В основном, где в C # вы бы написали
from ... select ???
в Scala у вас есть вместо
for ... yield ???
Также важно понимать, что for
-comprehensions работают не только с последовательностями, но и с любым типом, который определяет определенные методы, например LINQ:
map
, он разрешает for
выражения, состоящие из одного генератора.flatMap
так же, как map
, он допускает for
выражения, состоящие из нескольких генераторов.foreach
, он допускает for
-loops без выхода (как с одним, так и с несколькими генераторами).filter
, он разрешает for
выражения -filter, начинающиеся с выражения if
в for
выражении.Если вы не получите лучший ответ от пользователя Scala (которым я не являюсь), вот мое понимание.
Он появляется только как часть выражения, начинающегося с for
, в котором указано, как создать новый список из существующего списка.
Что-то вроде:
var doubled = for (n <- original) yield n * 2
Таким образом, есть один элемент вывода для каждого ввода (хотя я считаю, что есть способ отбрасывания дубликатов).
Это сильно отличается от «императивных продолжений», доступных в yield в других языках, где он позволяет генерировать список любой длины из некоторого императивного кода практически с любой структурой.
(Если вы знакомы с C #, он ближе к оператору LINQ, select
чем к нему yield return
).
Ключевым словом yield
в Scala является просто синтаксический сахар, который можно легко заменить на a map
, как подробно объяснял Даниэль Собрал .
С другой стороны, yield
абсолютно вводит в заблуждение, если вы ищете генераторы (или продолжения), подобные тем, что есть в Python . Посмотрите эту ветку SO для получения дополнительной информации: Каков предпочтительный способ реализации yield в Scala?
Рассмотрим следующее для понимания
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i
Может быть полезно прочитать это вслух следующим образом
« Для каждого целого числа i
, если оно больше, чем 3
, тогда выведите (произведите) i
и добавьте его в список A
».
С точки зрения математической нотации построителя множеств вышеупомянутое для понимания аналогично
который может быть прочитан как
« Для каждого целого числа , если оно больше, чем оно , оно является членом множества ».
или в качестве альтернативы
« это набор всех целых чисел , так что каждое из них больше, чем .»
Выход аналогичен циклу for, в котором есть буфер, который мы не видим, и для каждого приращения он продолжает добавлять следующий элемент в буфер. Когда цикл for завершает работу, он возвращает коллекцию всех полученных значений. Выход можно использовать как простые арифметические операторы или даже в сочетании с массивами. Вот два простых примера для вашего лучшего понимания
scala>for (i <- 1 to 5) yield i * 3
res: scala.collection.immutable.IndexedSeq [Int] = Vector (3, 6, 9, 12, 15)
scala> val nums = Seq(1,2,3)
nums: Seq[Int] = List(1, 2, 3)
scala> val letters = Seq('a', 'b', 'c')
letters: Seq[Char] = List(a, b, c)
scala> val res = for {
| n <- nums
| c <- letters
| } yield (n, c)
res: Seq [(Int, Char)] = Список ((1, a), (1, b), (1, c), (2, a), (2, b), (2, c), ( 3, а), (3, б), (3, в))
Надеюсь это поможет!!
val aList = List( 1,2,3,4,5 )
val res3 = for ( al <- aList if al > 3 ) yield al + 1
val res4 = aList.filter(_ > 3).map(_ + 1)
println( res3 )
println( res4 )
Эти две части кода эквивалентны.
val res3 = for (al <- aList) yield al + 1 > 3
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
Эти две части кода также эквивалентны.
Карта так же гибка, как доходность и наоборот.
yield является более гибким, чем map (), см. пример ниже
val aList = List( 1,2,3,4,5 )
val res3 = for ( al <- aList if al > 3 ) yield al + 1
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
yield выдаст результат наподобие: List (5, 6), что хорошо
в то время как map () вернет результат в виде: List (false, false, true, true, true), что, вероятно, не то, что вы намереваетесь.