it-swarm.com.ru

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

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

303
Geo

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

201
Dario

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

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

Компоненты Scala for являются синтаксическим сахаром для составления нескольких операций с map, flatMap и filter. Или foreach. Scala на самом деле переводит выражение 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.

805
Daniel C. Sobral

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

from ... select ??? 

в Scala вместо

for ... yield ???

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

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

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

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

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

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

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

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

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

13
Daniel Earwicker

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

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

12
Richard Gomes

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

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

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

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

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

set-notation

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

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

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

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

9
Mario Galic

Выход аналогичен циклу 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, в))

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

1
Manasa Chada
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
dotnetN00b