[scala] for 내장

scala 2016. 12. 8. 11:44

for 내장(comprehension) (또는 for yield문)에 대한 예시이다. 


for 내장은 다중 for 문으로 생각하는 게 좋다. 


그리고, yield를 map/flatmap/foreach와 비슷한 느낌으로 보는게 좋은 듯 하다. 따라서 for 내장의 라인은 큰 의미가 있기 때문에 주의해서 쓸 필요가 있다. 





즉, 아래와 같은 문장은 쓸 수 없고, 컴파일 에러가 난다. 

val m = for {
a = 5
b = 3
} yield (a + b)

에러 내용이다.


Error:(12, 7) '<-' expected but '=' found.

    a = 5

Error:(13, 1) illegal start of simple expression

          b = 3




아래 코드도 에러가 발생하지만, for 내장에 대해서 조금 이해할 수 있다. 

val m = for {
a <- 1
b <- 2
} yield (a + b)


에러 내용이다.


Error:(12, 10) value flatMap is not a member of Int

    a <- 1

Error:(13, 10) value map is not a member of Int

    b <- 2




다음과 같은 에러가 발생하는데. 첫 번째 라인에서는 flatmap, 다음은 map관련 에러가 발생한다. 

즉, for 내장은 내부적으로 변환된다는 의미를 가진다. 

마지막 문장은 map이고, 이전 문장은 flatmap으로 변환된다.





동작되는 for 내장 예시를 소개한다. 

for {
l <- List(1,2,3,4,5)
} yield l.println


결과는 다음과 같다.


1

2

3

4

5





val m = for {
s <- List("a", "bb", "ccc")
} yield s.length
println(m)

결과는 다음과 같다. 


List(1, 2, 3)



그리고 2차 for문처럼 쓸 수 있다. 


for {
i <- 1 to 2
j <- 3 to 4
} println(s"$i + $j = ${i + j}")


결과는 다음과 같다.


1 + 3 = 4

1 + 4 = 5

2 + 3 = 5

2 + 4 = 6





object Main extends App {
val m = for {
s <- List("1", "a", "3")
c <- s
} yield s"${s} - ${c}"
println(m)
}

결과는 다음과 같다.


List(1 - 1, a - a, 3 - 3)


val m = for {
s <- List("1", "a", "3")
c <- s
if c.isLetter
} yield s"${s} - ${c}"
println(m)

결과는 다음과 같다.


List(a - a)




yield에 콜렉션(튜플)을 사용할 수 있다. 

val m = for {
s <- List("b4", "a", "3")
c <- s
} yield (s, c)

m.foreach(println)


결과는 다음과 같다.

(b4,b)

(b4,4)

(a,a)

(3,3)



option으로 테스트 해본다.

val m = for {
i <- List(1, 2, 3, 0)
} yield (i * 10)

m.foreach(println)

결과는 다음과 같다.


10

20

30

0



Some으로 내용을 감싸면 내부적으로 Some  None에 따라 값을 구한다. 

val m = for {
Some(i) <- List(Some(1), Some(2), Some(3), None)
} yield (i * 10)

m.foreach(println)



결과는 다음과 같다.


10

20

30




참고로 인스턴스는 사용한 콜렉션을 따르다. 아래 결과는 Vector(10, 20, 30)이다. 


val m = for {
Some(i) <- Vector(Some(1), Some(2), Some(3), None)
} yield (i * 10)

println(m)






함수를 사용할 때는 리턴 타입이 Option로 감쌀 때 잘 동작한다. 그냥 하면 에러가 발생한다. 



다음은 에러 케이스이다. 

def isAllDigits(s: String) : Int = {
if (s forall Character.isDigit) {
return s.toInt
}
0
}

val m = for {
a <- isAllDigits("1")
b <- isAllDigits("2")
c <- isAllDigits("9")
} yield (a + b + c)

println(m)

결과는 다음과 같다. 


Error:(33, 21) value flatMap is not a member of Int

    a <- isAllDigits("1")

Error:(34, 21) value flatMap is not a member of Int

    b <- isAllDigits("2")

Error:(35, 21) value map is not a member of Int

    c <- isAllDigits("9")




다음은 Option을 래핑한 예시이다.


def isAllDigits(s: String) : Option[Int] = {
if (s forall Character.isDigit) {
return Some(s.toInt)
}
Some(0)
}

val m = for {
a <- isAllDigits("1")
b <- isAllDigits("2")
c <- isAllDigits("9")
} yield (a + b + c)

println(m)


결과는 다음과 같다. 


Some(12)



잘못된 결과가 나와도 잘 동작한다. 

def isAllDigits(s: String) : Option[Int] = {
if (s forall Character.isDigit) {
return Some(s.toInt)
}
Some(0)
}

val m = for {
a <- isAllDigits("1")
b <- isAllDigits("a")
c <- isAllDigits("2")
} yield (a + b + c)

println(m)



Posted by '김용환'

댓글을 달아 주세요