Что такое Scala выход?

Я понимаю доходность Ruby и Python. Что делает выход Scala?

289
27 июня '09 в 12:18
источник поделиться
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.

Ruby yield имеет другой эффект.

193
27 июня '09 в 12:42
источник

Я думаю, что принятый ответ велик, но, похоже, многие люди не смогли понять некоторые фундаментальные моменты.

Во- первых, Scala for постижений эквивалентны Haskell do запись, и это не более чем синтаксический сахар для состава нескольких монадических операций. Поскольку это утверждение, скорее всего, не поможет никому, кто нуждается в помощи, повторите попытку... :-)

Scala for понимания - синтаксический сахар для составления нескольких операций с картой, flatMap и filter. Или foreach. Scala фактически переводит for -expression для вызовов этих методов, поэтому любой класс, предоставляющий их, или их подмножество, может использоваться для понимания.

Во-первых, позвольте поговорить о переводах. Существуют очень простые правила:

  1. Эта

    for(x <- c1; y <- c2; z <-c3) {...}
    

    переводится на

    c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
    
  2. Эта

    for(x <- c1; y <- c2; z <- c3) yield {...}
    

    переводится на

    c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
    
  3. Эта

    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 есть. Дополнительную информацию об этом см. В разделе ниже.

  4. Эта

    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.

776
29 июня '09 в 20:33
источник

Да, как сказал 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.
23
27 июня '09 в 15:31
источник

Если вы не получите лучшего ответа от пользователя Scala (которого я не знаю), вот мое понимание.

Он появляется только как часть выражения, начинающегося с for, в котором указывается, как сгенерировать новый список из существующего списка.

Что-то вроде:

var doubled = for (n <- original) yield n * 2

Итак, есть один выходной элемент для каждого входа (хотя я считаю, что есть способ сбросить дубликаты).

Это сильно отличается от "императивных продолжений", разрешенных выходом на других языках, где он предоставляет способ генерации списка любой длины из некоторого императивного кода с почти любой структурой.

(Если вы знакомы с С#, он ближе к оператор LINQ select, чем к yield return).

12
27 июня '09 в 12:52
источник

Ключевое слово yield в Scala просто синтаксический сахар, который легко может быть заменен на map, так как Даниэль Собрал уже объяснил подробно.

С другой стороны, yield абсолютно вводит в заблуждение, если вы ищете генераторы (или продолжения), подобные тем, которые существуют в Python. См. Этот поток SO для получения дополнительной информации. Каков предпочтительный способ реализации "yield" в Scala?

12
07 июля '12 в 15:27
источник

Рассмотрим следующее для понимания

val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i

Может быть полезно прочитать это вслух следующим образом

" Для каждого целого i, если оно больше 3, тогда выведите (произведите) i и добавьте его в список A ".

В терминах обозначения математического набора-построек вышеприведенное для понимания аналогично

set-notation

который можно читать как

" Для каждого целого i, если он больше, чем 3, то он является членом множества A".

или, альтернативно, как

"A - это набор всех целых чисел i, так что каждый i больше, чем 3".

9
24 дек. '15 в 1:23
источник

Доход аналогичен циклу, который имеет буфер, который мы не видим, и для каждого приращения он добавляет следующий элемент в буфер. Когда цикл 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, с))

Надеюсь это поможет!!

0
12 авг. '18 в 15:02
источник
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 )

Эти две части кода также эквивалентны.

Карта является такой же гибкой, как и выход, и наоборот.

0
26 окт. '13 в 2:59
источник

выход более гибкий, чем 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), который, вероятно, не является тем, что вы намереваетесь.

-2
10 окт. '13 в 21:24
источник

Посмотрите другие вопросы по меткам или Задайте вопрос