9 ответов:
он используется в осмысление последовательности (как список Python-понимания и генераторы, где вы можете использовать
yieldтоже).применяется в сочетании с
forи записывает новый элемент в результирующей последовательности.простой пример (из scala-lang)
/** 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в Linq.
Рубин
yieldимеет другой эффект.
я думаю, что принятый ответ велик, но, похоже, многие люди не смогли понять некоторые фундаментальные моменты.
во-первых, Скала
forпонимание эквивалентно Хаскелуdoнотация, и это не более чем синтаксический сахар для композиции нескольких монадических операций. Поскольку это утверждение, скорее всего, не поможет никому, кто нуждается в помощи, давайте попробуем еще раз... :-)Скала
forпонимание является синтаксическим сахаром для композиции несколько операций с картой,flatMapиfilter. Илиforeach. Scala фактически переводит afor-выражение в вызовах этих методов, поэтому любой класс, предоставляющий их, или их подмножество, может использоваться для понимания.во-первых, давайте поговорим о переводах. Есть очень простые правила:
этой
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 => {...})с резервным вариантом в первый метод if
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
withFilterScala 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.
да, как сказал Ушастик, это в значительной степени эквивалентно LINQ's
selectи имеет очень мало общего с Руби и Пайтонаyield. В принципе, где в C# вы должны написатьfrom ... select ???в Scala у вас вместо
for ... yield ???также важно понимать, что
for-понимание работает не только с последовательностями, но и с любым типом, который определяет определенные методы, например LINQ:
- если ваш тип определяет только
map, это позволяетfor-выражения, состоящие из один генератор.- если он определяет
flatMapа такжеmap, это позволяетfor-выражения, содержащие из нескольких генераторов.- если он определяет
foreach, это позволяетfor-петли без выхода (как с одиночными, так и с множественными генераторами).- если он определяет
filterпозволяетfor- фильтровать выражения, начинающиеся сifвforвыражение.
ключевое слово
yieldв Scala-это просто синтаксический сахар который можно легко заменить наmap, а Даниил собрал уже объяснил подробнее.С другой стороны,
yieldабсолютно вводит в заблуждение, если вы ищете генераторы (или продолжения), подобные те в Python. См. этот поток SO для получения дополнительной информации:каков предпочтительный способ реализации 'yield' в Scala?
если вы не получите лучшего ответа от пользователя Scala (который я не являюсь), вот мое понимание.
Он появляется только как часть выражения, начиная с
for, в котором указано, как создать новый список из существующего списка.что-то типа:
var doubled = for (n <- original) yield n * 2так что есть один выходной элемент для каждого входа (хотя я считаю, что есть способ удаления дубликатов).
это сильно отличается от" императивных продолжений", включенных выход на других языках, где он обеспечивает способ создания списка любой длины, из некоторого императивного кода с почти любой структурой.
(если вы знакомы с C#, это ближе к LINQ на это
selectоператор, чем он являетсяyield return).
рассмотрим следующее понятно
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield iможет быть полезно прочитать его вслух следующим образом
"на каждое целое число
i,если больше3, потом доходность (произвести)iи добавить его в списокA."С точки зрения математической набор-конструктор нотации выше для понимания аналогично к
который может быть прочитан как
"на каждое целое число
,если больше
, потом является членом комплекса
."
или
"
- это набор всех целых чисел
, таких, что каждый элемент
больше
."
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 )эти два фрагмента кода также эквивалентны.
карта так же гибка, как и выход, и наоборот.
выход похож на цикл for, который имеет буфер, который мы не можем видеть, и для каждого приращения он продолжает добавлять следующий элемент в буфер. Когда цикл for завершится, он вернет коллекцию всех полученных значений. Урожай можно использовать как простые арифметические операторы или даже в сочетании с массивами. Вот два простых примера для лучшего понимания
scala>for (i <- 1 to 5) yield i * 3res: scala.коллекция.неизменный.IndexedSeq[Int] = Вектор(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)] = List((1,a), (1,b), (1,c), (2,a), (2,b), (2,c), (3,a), (3,b), (3,c))
надеюсь, что это помогает!!
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 )выход будет печатать результат, как: список(5, 6), что хорошо
в то время как map() вернет результат, например: List(false, false, true, true, true), что, вероятно, не то, что вы намереваетесь.
Comments