Відповіді:
Він використовується в послідовних розуміннях (як-от розуміння списків 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
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
це буде розглянуто на вимогу, а не заздалегідь застосовуватися до всієї колекції.
Скала 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 () поверне результат типу: Список (помилковий, хибний, правдивий, справжній, правдивий), який, ймовірно, не є тим, що ви маєте намір.