스칼라의 수확량은 얼마입니까?


답변:


205

그것은에서 사용되는 시퀀스 함축 (당신이 사용할 수 파이썬의리스트 지능형 발전기처럼 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.

루비 yield는 다른 효과가 있습니다.


57
그렇다면 왜 맵 대신 yield를 사용합니까? 이 맵 코드는 val res = args.map (_. toUpperCase)와 동일합니다.
Geo

4
구문이 더 좋을 경우. 또한 alexey가 지적했듯이 이해력은 flatMap, 필터 및 foreach에 액세스하기위한 훌륭한 구문을 제공합니다.
Nathan Shively-Sanders

22
권리. 간단한지도-if가없는 발전기 하나-지도를 호출하는 것이 더 읽기 쉽다고 말할 것입니다. 서로에 따라 여러 생성기 및 / 또는 필터가있는 경우 식을 선호 할 수 있습니다.
Alexey Romanov 2016 년

13
주어진 예제는 맵 표현식과 동일하지 않습니다. 동일합니다. 이해력은지도, flatMap 및 필터 호출로 변환됩니다.
Daniel C. Sobral

9
대답은 다음과 같이 시작합니다. "순서를 이해하는 데 사용됩니다 (파이썬의 목록 이해 및 생성기와 같이 수율도 사용할 수 있습니다)." 이것은 실수로 스칼라의 수율이 파이썬의 수율과 유사하다고 생각하게합니다. 그렇지 않다. 파이썬에서는 수율이 코 루틴 (또는 연속)의 맥락에서 사용되지만 스칼라에서는 그렇지 않습니다. 자세한 설명을 보려면 다음 스레드를 방문하십시오 : stackoverflow.com/questions/2201882/…
Richard Gomes

817

나는 대답이 훌륭하다고 생각하지만 많은 사람들이 근본적인 요점을 파악하지 못한 것 같습니다.

첫째, 스칼라의 for이해는 하스켈의 이해와 동일합니다.do 표기법 , 여러 모나드 연산의 구성을위한 구문 설탕에 지나지 않습니다. 이 문장은 도움이 필요한 사람에게는 도움이되지 않을 것이므로 다시 시도합시다… :-)

스칼라의 for함축은지도 여러 작업의 구성에 대한 문법 설탕이다, flatMap하고 filter. 또는 foreach. 스칼라는 실제로 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 {...}

    스칼라 2.7에서

    c.filter(x => cond).map(x => {...})

    스칼라 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방법은 컬렉션의 엄격성에 따라 동작이 정의됩니다. 이것을 더 잘 이해하려면 List(엄격한) 및 Stream(엄격하지 않은 ) 스칼라 2.7을 살펴 보겠습니다 .

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사전에 전체 컬렉션에 적용되는 대신 주문형으로 간주 되기 때문에 많은 문제가 발생 했습니다.

도입 스칼라 2.8 withFilter입니다, 항상 비 엄격한은 더 컬렉션의 엄격 상관 없습니다. 다음 예제는 ListScala 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동작 을 바꾸지 않고 대부분의 사람들이 기대하는 결과를 만들어냅니다 . 참고로 RangeScala 2.7과 Scala 2.8 사이에서 엄격하지 않음에서 엄격으로 변경되었습니다.


2
scala 2.8에는 Filter를 사용하는 새로운 방법이 있습니다. for (x <-c; cond) yield {...}는 scala2.8에서 c.withFilter (x => cond) .map (x => {...})로 변환됩니다.
Eastsun

2
@Eastsun 자동 폴 백도 있지만 충분합니다. withFilter엄격한 컬렉션의 경우에도 엄격하지 않아야하며 설명이 필요합니다. 나는 이것을 고려할 것이다 ...
Daniel C. Sobral

2
@Daniel : Odersky 등의 "Programming in Scala"에서이 주제에 대한 대우가 있습니다. (나는 당신이 이미 그것을 알고 있다고 확신합니다). 그것을 표시하기 위해 +1.
Ralph

첫 번째 2 개 포인트와 올바른 : 1 for(x <- c; y <- x; z <-y) {...}로 변환됩니다 c.foreach(x => x.foreach(y => y.foreach(z => {...}))) 2.하기 for(x <- c; y <- x; z <- y) yield {...}로 번역c.flatMap(x => x.flatMap(y => y.map(z => {...})))
도미니크

이것은 for(x <- c; y = ...) yield {...}정말로로 번역 c.map(x => (x, ...)).map((x,y) => {...})됩니까? 번역 c.map(x => (x, ...)).map(x => { ...use x._1 and x._2 here...})된 것 같아요. 뭔가 빠졌나요?
prostynick 2016 년

23

예, Earwicker가 말했듯이 LINQ select와 거의 동일 하며 Ruby 및 Python과 거의 관련이 없습니다 yield. 기본적으로 C #에서 작성하는 위치

from ... select ??? 

스칼라에서는 대신에

for ... yield ???

for-comprehensions는 시퀀스에서만 작동하는 것이 아니라 LINQ와 같이 특정 메소드를 정의하는 모든 유형에서 작동 한다는 것을 이해하는 것이 중요합니다 .

  • 유형이 just을 정의 하면 단일 생성기로 구성된 표현식 map을 허용 for합니다.
  • flatMap뿐만 아니라를 정의 map하면 for여러 생성기로 구성된 표현식 을 허용 합니다.
  • 로 정의 foreach하면 for수율없이 단일 루프와 다중 생성기를 사용하여 -loops를 허용 합니다.
  • 이 정의하는 경우 filter, 그것은 수 for로 시작 -filter 표현 iffor표현.

2
@Eldritch Conundrum-흥미롭게도 원본 SQL 사양과 동일한 순서입니다. SQL 언어가 어딘가에서 순서를 거꾸로 옮겼지만 먼저 무엇을 가져 오는지 설명하고 나올 것으로 예상되는 것을 설명하는 것이 합리적입니다.
Jordan Parmer

13

Scala 사용자로부터 더 나은 답변을 얻지 못하면 (내가 아님), 여기 내 이해가 있습니다.

for기존 목록에서 새 목록을 생성하는 방법을 나타내는으로 시작하는 표현식의 일부로 만 나타납니다 .

다음과 같은 것 :

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

따라서 각 입력에 대해 하나의 출력 항목이 있습니다 (중복을 삭제하는 방법이 있다고 생각하지만).

이것은 거의 모든 구조를 가진 일부 명령 코드에서 길이의 목록을 생성하는 방법을 제공하는 다른 언어의 yield로 활성화 된 "제한 연속"과는 매우 다릅니다.

(C #에 익숙하다면 LINQ select 연산자보다 LINQ 연산자에 더 가깝 습니다 yield return).


1
"var doubled = for (n <-original) yield n * 2"여야합니다.
Russel Yang


11

다음과 같은 이해력을 고려하십시오

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

다음과 같이 큰 소리로 읽는 것이 도움이 될 수 있습니다

" 위해 각각의 정수 i, 만약 그것보다 큰 3수율 (생산) i과 목록에 추가A ."

수학적 set-builder 표기법 측면 에서 위의 이해력은 다음과 유사합니다.

세트 표기

로 읽을 수 있습니다

" 위해 각각의 정수 나는, 만약 그것보다 크면 삼, 그것은 구성원 세트 중 ㅏ."

또는 대안으로

" ㅏ는 모든 정수의 집합 나는이므로 각각 나는이보다 큽니다 삼."


2

수율은 우리가 볼 수없는 버퍼를 가지고 있고 각 증가마다 버퍼에 다음 항목을 계속 추가하는 for 루프와 유사합니다. for 루프가 실행을 마치면 생성 된 모든 값의 컬렉션을 반환합니다. 수율은 간단한 산술 연산자로 사용하거나 배열과 함께 사용할 수도 있습니다. 이해를 돕기위한 두 가지 간단한 예가 있습니다.

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

입술 : 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)] =리스트 ((1, a), (1, b), (1, c), (2, a), (2, b), (2, c), ( 3, a), (3, b), (3, c))

도움이 되었기를 바랍니다!!


이 오래된 (9 년 전) 질문에 답변 할 때는 이미 제출 된 다른 답변과 답변이 어떻게 다른지 지적하면 도움이됩니다.
jwvh

나는이 언어를 배우는 초보자이기도하기 때문에 의심을 명확히하는 것이 중요하고 다른 대답을하지 않는 것으로 생각했습니다. 제안 해 주셔서 감사합니다.
마나 사 차다

0
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 )

이 두 코드도 동일합니다.

지도는 수확량만큼 유연하고 그 반대도 마찬가지입니다.


-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 )

yield는 List (5, 6)과 같은 결과를 출력합니다.

반면 map ()은 List (false, false, true, true, true)와 같은 결과를 반환합니다.


4
그 비교가 잘못되었습니다. 서로 다른 두 가지를 비교하고 있습니다. 수율 표현은지도 표현과 똑같은 일을하지 않습니다. 또한,지도와 비교하여 수율의 "유연성"을 전혀 나타내지 않습니다.
dotnetN00b 2013 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.