Каков урожай Scala? - PullRequest
       61

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

306 голосов
/ 27 июня 2009

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

Ответы [ 9 ]

806 голосов
/ 29 июня 2009

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

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

Scala's for - синтаксический сахар для составления множества операций с картой, 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.

202 голосов
/ 27 июня 2009

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

23 голосов
/ 27 июня 2009

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

from ... select ??? 

в Scala у вас взамен

for ... yield ???

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

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

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

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

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

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

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

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

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

12 голосов
/ 07 июля 2012

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

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

9 голосов
/ 24 декабря 2015

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

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."

1 голос
/ 12 августа 2018

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

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

0 голосов
/ 26 октября 2013
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 голосов
/ 10 октября 2013

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 выводит результат в виде: Список (5, 6), что хорошо

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...