Каков урожай Scala?

Я понимаю доходность Ruby и Python. Что делает урожай Scala?

11 ответов

Решение

Он используется в понимании последовательностей (например, в списках и генераторах Python, где вы можете использовать yield тоже).

Применяется в сочетании с for и записывает новый элемент в результирующую последовательность.

Простой пример (из Скала-Ланга)

/** 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 Понимания эквивалентны Хаскеллу do нотации, и это не более чем синтаксический сахар для составления нескольких монадических операций. Поскольку это утверждение, скорее всего, не поможет никому, кто нуждается в помощи, давайте попробуем еще раз…:-)

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

Сначала поговорим о переводах. Есть очень простые правила:

  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.

Да, как сказал Earwicker, это в значительной степени эквивалентно LINQ select и имеет очень мало общего с Ruby и Python yield, В принципе, где в C# вы бы написали

from ... select ??? 

в Scala у вас есть вместо

for ... yield ???

Также важно понимать, что for-comprehensions работают не только с последовательностями, но и с любым типом, который определяет определенные методы, например LINQ:

  • Если ваш тип определяет просто map, Это позволяет for-выражения, состоящие из одного генератора.
  • Если это определяет flatMap так же как map, Это позволяет for-выражения, состоящие из нескольких генераторов.
  • Если это определяет foreach, Это позволяет for- циклы без выхода (как с одним, так и с несколькими генераторами).
  • Если это определяет filter, Это позволяет for-фильтров выражения, начинающиеся с ifв for выражение.

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

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

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

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

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

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

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

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

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

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

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

С точки зрения математической нотации построителя множества вышеприведенное "понимание" аналогично

Набор-обозначение

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

" За каждое целое число я если оно больше чем 3 тогда он входит в набор ".

или в качестве альтернативы

" множество всех целых я так, что каждый я больше, чем 3 ".

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

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

Выход аналогичен циклу for, в котором есть буфер, который мы не видим, и для каждого приращения он продолжает добавлять следующий элемент в буфер. Когда цикл for завершает работу, он возвращает коллекцию всех полученных значений. Выход можно использовать как простые арифметические операторы или даже в сочетании с массивами. Вот два простых примера для вашего лучшего понимания

scala>for (i <- 1 to 5) yield i * 3

res: scala.collection.immutable.IndexedSeq [Int] = Vector (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, в))

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

Согласно документации Scala , там четко сказано «получить новую коллекцию из существующей коллекции».

В другой документации Scala говорится: «Scala предлагает облегченную нотацию для выражения вложений последовательности . Включения имеют формуfor(перечисления)yield e, где enums относится к списку перечислителей, разделенных точкой с запятой. Перечислитель — это либо генератор, вводящий новые переменные, либо фильтр. "

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 doubledNums = for (n <- nums) yield n * 2
val ucNames = for (name <- names) yield name.capitalize

Обратите внимание, что оба этих выражения for используют ключевое слово yield :

Использование yield после for - это «секретный соус», который гласит: «Я хочу получить новую коллекцию из существующей коллекции, которую я повторяю в выражении for, используя показанный алгоритм».

взято отсюда

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 )

yield выдаст результат наподобие: List(5, 6), что хорошо

в то время как map () вернет результат как: List(false, false, true, true, true), что, вероятно, не то, что вы намереваетесь.

Другие вопросы по тегам