Я понимаю доходность Ruby и Python. Что делает выход Scala?
Что такое Scala выход?
Ответ 1
Он используется в последовательности понимания (например, 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.
Ruby yield
имеет другой эффект.
Ответ 2
Я думаю, что принятый ответ велик, но, похоже, многие люди не смогли понять некоторые фундаментальные моменты.
Во- первых, Scala for
постижений эквивалентны Haskell do
запись, и это не более чем синтаксический сахар для состава нескольких монадических операций. Поскольку это утверждение, скорее всего, не поможет никому, кто нуждается в помощи, повторите попытку... :-)
Scala for
понимания - синтаксический сахар для составления нескольких операций с картой, flatMap
и filter
. Или foreach
. Scala фактически переводит for
-expression для вызовов этих методов, поэтому любой класс, предоставляющий их, или их подмножество, может использоваться для понимания.
Во-первых, позвольте поговорить о переводах. Существуют очень простые правила:
-
Эта
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.
Ответ 3
Да, как сказал Earwicker, он почти эквивалентен LINQ select
и имеет очень мало общего с Ruby и Python yield
. В основном, где в С# вы пишете
from ... select ???
в Scala у вас вместо этого
for ... yield ???
Также важно понимать, что for
-понимание не просто работает с последовательностями, но и с любым типом, который определяет определенные методы, подобно LINQ:
- Если ваш тип определяет только
map
, он позволяетfor
-выражения, состоящие из одиночный генератор. - Если он определяет
flatMap
, а такжеmap
, он позволяетfor
-выражения, состоящие нескольких генераторов. - Если он определяет
foreach
, он позволяетfor
-loops без yield (как с одним, так и с несколькими генераторами). - Если он определяет
filter
, он позволяет выражать выраженияfor
-filter, начиная сif
в выраженииfor
.
Ответ 4
Если вы не получите лучшего ответа от пользователя Scala (которого я не знаю), вот мое понимание.
Он появляется только как часть выражения, начинающегося с for
, в котором указывается, как сгенерировать новый список из существующего списка.
Что-то вроде:
var doubled = for (n <- original) yield n * 2
Итак, есть один выходной элемент для каждого входа (хотя я считаю, что есть способ сбросить дубликаты).
Это сильно отличается от "императивных продолжений", разрешенных выходом на других языках, где он предоставляет способ генерации списка любой длины из некоторого императивного кода с почти любой структурой.
(Если вы знакомы с С#, он ближе к оператор LINQ select
, чем к yield return
).
Ответ 5
Ключевое слово yield
в Scala просто синтаксический сахар, который легко может быть заменен на map
, так как Даниэль Собрал уже объяснил подробно.
С другой стороны, yield
абсолютно вводит в заблуждение, если вы ищете генераторы (или продолжения), подобные тем, которые существуют в Python. См. Этот поток SO для получения дополнительной информации. Каков предпочтительный способ реализации "yield" в Scala?
Ответ 6
Рассмотрим следующее для понимания
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i
Может быть полезно прочитать это вслух следующим образом
" Для каждого целого i
, если оно больше 3
, тогда выведите (произведите) i
и добавьте его в список A
".
В терминах обозначения математического набора-построек вышеприведенное для понимания аналогично
который можно читать как
" Для каждого целого , если он больше, чем , то он является членом множества ".
или, альтернативно, как
" - это набор всех целых чисел , так что каждый больше, чем ".
Ответ 7
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 )
Эти две части кода также эквивалентны.
Карта является такой же гибкой, как и выход, и наоборот.
Ответ 8
Доход аналогичен циклу, который имеет буфер, который мы не видим, и для каждого приращения он добавляет следующий элемент в буфер. Когда цикл 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)] = List ((1, a), (1, b), (1, c), (2, a), (2, b), (2, c), ( 3, а), (3, б), (3, с))
Надеюсь это поможет!!
Ответ 9
выход более гибкий, чем 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), который, вероятно, не является тем, что вы намереваетесь.