Відповіді:
Він використовується в послідовних розуміннях (як-от розуміння списків 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
в Лінку.
Рубі yieldмає інший ефект.
Я вважаю, що прийнята відповідь чудова, але, здається, багато людей не змогли зрозуміти деякі основні моменти.
По-перше, forрозуміння Scala еквівалентно doпозначенням Haskell , і це не що інше, як синтаксичний цукор для композиції з декількох монадичних операцій. Оскільки це твердження, швидше за все, не допоможе тому, хто потребує допомоги, спробуємо ще раз… :-)
Поняття Scala - forце синтаксичний цукор для складання декількох операцій з картою flatMapта filter. Або foreach. Скала фактично переводить 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
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це буде розглянуто на вимогу, а не заздалегідь застосовуватися до всієї колекції.
Скала 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і має дуже мало спільного з Рубі та Пітоном yield. В основному, де в C # ви б писали
from ... select ???
у Scala у вас замість цього
for ... yield ???
Також важливо розуміти, що for-порозуміння працюють не просто з послідовностями, а з будь-яким типом, який визначає певні методи, як LINQ:
map, він дозволяє for-вирази, що складаються з одного генератора.flatMapтакож map, він дозволяє for-вирази, що складаються з декількох генераторів.foreach, він дозволяє for-виходити без виходу (як з одиночними, так і з декількома генераторами).filter, що дозволяє for-Filter вираження , починаючи з if
в forвираженні.Якщо ви не отримаєте кращої відповіді від користувача Scala (чого я не є), ось моє розуміння.
Він з’являється лише як частина виразу, що починається з for, який визначає, як створити новий список із наявного списку.
Щось на зразок:
var doubled = for (n <- original) yield n * 2
Таким чином, є один вихідний елемент для кожного введення (хоча я вважаю, що існує спосіб скидання дублікатів).
Це сильно відрізняється від "імперативних продовжень", що забезпечуються урожаєм в інших мовах, де він забезпечує спосіб для створення списку будь-якої довжини, з якогось імперативного коду практично з будь-якою структурою.
(Якщо ви знайомі з C #, це ближче до оператора LINQ, select ніж це yield return).
Ключове слово yieldв Scala - це просто синтаксичний цукор, який можна легко замінити на map, як це вже детально пояснив Даніель Собрал .
З іншого боку, yieldабсолютно вводить в оману, якщо ви шукаєте генератори (або продовження), подібні до тих, що в Python . Дивіться цю тему SO для отримання додаткової інформації: Який найкращий спосіб впровадити «урожай» у Scala?
Розглянемо наступне для розуміння
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i
Це може бути корисно прочитати вголос наступним чином
« Для кожного цілого i, якщо воно більше 3, то вихід (продукти) iі додати його в список A.»
Що стосується математичних позначень побудови множини , вищезгадане для розуміння є аналогом
яку можна читати як
Msgstr " Для кожного цілого числа , якщо воно більше ніж
, то воно є членом набору
."
або альтернативно як
" - це набір усіх цілих чисел
, такий, що кожне
більше, ніж
."
Вихід подібний до циклу, який містить буфер, який ми не можемо бачити, і для кожного збільшення він продовжує додавати наступний елемент до буфера. Коли цикл for закінчиться, він поверне колекцію всіх вихідних значень. Вихід можна використовувати як прості арифметичні оператори або навіть у поєднанні з масивами. Ось два простих приклади для кращого розуміння
scala>for (i <- 1 to 5) yield i * 3
res: scala.collection.immutable.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)] = Список ((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 )
Ці дві частини коду також рівноцінні.
Карта така ж гнучка, як урожайність і навпаки.
урожайність більш гнучка, ніж карта (), див. приклад нижче
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 () поверне результат типу: Список (помилковий, хибний, правдивий, справжній, правдивий), який, ймовірно, не є тим, що ви маєте намір.