Что такое урожайность Скалы?

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

Он используется для понимания последовательностей (например, для составления списков 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 

в Линк.

У Ruby есть другой эффект.

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

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

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

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

  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, это в значительной степени эквивалентно select LINQ и имеет очень мало общего с yield Ruby’s и Python. В принципе, где в C # вы должны писать

 from ... select ??? 

в Scala у вас вместо этого

 for ... yield ??? 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

« Для каждого целого я , если он больше, чем 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 ) 

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

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

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

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

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

Давайте будем гением компьютера.