일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 이더리움
- 파이썬 강좌
- Actor
- 주키퍼
- Play2 로 웹 개발
- hyperledger fabric
- CORDA
- 플레이프레임워크
- Hyperledger fabric gossip protocol
- 엔터프라이즈 블록체인
- Akka
- play2 강좌
- Golang
- play 강좌
- 안드로이드 웹뷰
- 스칼라 동시성
- akka 강좌
- 파이썬 데이터분석
- 스칼라 강좌
- Adapter 패턴
- Play2
- 그라파나
- 파이썬
- 스칼라
- 하이퍼레저 패브릭
- 스위프트
- 파이썬 동시성
- 하이브리드앱
- 블록체인
- 파이썬 머신러닝
- Today
- Total
HAMA 블로그
스칼라 강좌 (29) - for comprehensions 본문
스칼라에서의 for - comprehensions
1. 개념
스칼라 Doc 에서는 이렇게 말합니다. (http://docs.scala-lang.org/tutorials/FAQ/yield.html)
파이썬,루비등에 있는 yield 처럼 스칼라도 yield 를 가지고 있지만 좀 다릅니다. 스칼라의 yield 는 for comprehensions 의 일부분으로 사용되며, 다른 언어의 list-comprehensions 의 일반화 입니다. 스칼라의 "for comprehensions" 는 하스켈의 "do" 와 동등하며 멀티플모나딕 연산을 위한 사용편의 정도일 뿐입니다.
사실 for - comprehensions 같은것들은 syntactic sugar 라고 합니다. 번역하면 사용자가 편하게 사용하기 위한 사탕발림 쯤 되는데요. 일단 외우는세요. 자바나 C++에서 스칼라로 넘어 왔을때 사실 모든게 syntatic sugar 로 보이긴하죠 ㅎㅎ
스칼라 for - comprehensions 를 다양한곳에서 가져온 예제를 통해서 살펴보겠습니다. (개발자는 코드로 말합니다. 이해안가면 코드만 계속 반복해서 보세요. 각성 하실 겁니다. )
혹시 python 의 yield 를 사용했던 분이라면 그것은 순회하면서 하나씩 바깥으로 던지는데 반해, scala yield 는 모아뒀다가 컬렉션으로 던저 주는 느낌으로 받아 드리면 될 거 같습니다.
예제 0) flatMap 이란?
scala> val nestedNumbers = List(List(1, 2), List(3, 4)) scala> nestedNumbers.flatMap(x => x.map(_ * 2)) res0: List[Int] = List(2, 4, 6, 8)
하나의 리스트로 '합쳐' 준다는 것을 유념하세요. 그냥 map 을 적용시키면 List(2,4) List(6,8) 이렇게 나오겠죠.
flatMap 이 스칼라나 함수형 프로그래밍에서 굉장히 중요합니다.
F[A]에 대한 Monad는 아래 함수를 가진다
(F[A], A => F[B]) => F[B]
타입을 가진flatMap
메소드
(즉 A를 감싼F타입에서 A를 빼내어 B로 만든후 F 타입으로 다시 감싼다)A => F[A]
타입을 가진 pure 메소드 (A 를 감싼 타입으로 변경)
모나드는 3개의 법칙을 따라야 한다.
- Left identity:
(pure(a) flatMap f) == f(a)
- Right identity:
(m flatMap pure) == m
- Associativity:
(m flatMap f flatMap g) == (m flatMap (x => f(x) flatMap g))
모나드는 pure
와 flatMap
을 가지며 결합법칙과 항등법칙을 만족하도록 만든 구현이다. 즉 이 두 함수들을 이용해서 변수의 도입과 결속 그리고 변수 치환수행을 위한 문맥을 제공한다고 볼수 있으며 이 두 함수를 이용해서 많은 함수를 파생 시킬 수 있으며 코드를 줄이는 것도 가능하다
http://www.bench87.com/content/24 발췌 (COMET)
이런것들이 이해 안가면 예제만 반복해서 봅니다. 그럼 이해갑니다. 인간의 능력이란 반복에서 나옵니다.
예제 1)
for(x <- c1; y <- c2; z <-c3) {...}
위의 문장은 아래 처럼 사용 될 수 있습니다.
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
c1의 요소가 3개 , c2 가 3개, c3 가 3개라면 27번의 행위가 발생되겠군요.
예제 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 {...}
위의 문장은 아래 처럼 사용 될 수 있습니다.
c.withFilter(x => cond).map(x => {...})
cond 에 합당하는 x 들을 이용하여 어떤 행위를 한 후에 다시 c 타입을 갖게 합니다.
예제 4)
for(x <- c; y = ...) yield {...}
위의 문장은 아래 처럼 사용 될 수 있습니다.
c.map(x => (x, ...)).map((x,y) => {...})
map 을 대신해서 사용 할 수 있지만 아래 예를 보시면 느껴지다 시피 무엇이 더 이해하기 쉽습니까?
괄호가 연속된 map 보다는 for yield 를 사용한게 훨씬 명료하네요.
l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length))
or
for{
sl <- l
el <- sl
if el > 0
} yield el.toString.length
예제 5)
for 문을 통해서 하나씩 실행한 후에 yield 를 통해서 names 의 capitalize 만 따로 추출해서 새로운 배열로 만들어집니다.
이번에는 숫자를 추출해서 그것으로 배열이 만들어 졌습니다.
예제 6)
모든 숫자중에서 10보다 작은 숫자를 2배해서 set s 에 담아줬습니다.
예제 7)
- def eitherExample(num: Some(Int)): Either[String, Int] = num match {
- case Some(n) => Right(n)
- case None => Left("Error! Number is missing!")
- }
match case 를 이용하였습니다.
- val x: Either[String, Long] = Right(7)
- x.right.map(num => println(num))
- x.left.map(msg => println(msg))
- val x: Either[String, Int] = Right(7)
- val result1 = for( res <- x.right ) { res }
- val result2 = for( res <- x.left) { res }
- for {
- id <- x.right
- name <- y.right
- } yield {
- val level = lookupLevel(id)
- Member(id, name, level)
- }.fold(
- err => NotAuthorized(err),
- member => member
- )
마지막으로 for - yield 문을 보시면 Either 의 x, y 값을 id , name 으로 담습니다.
해당 값들에 문제가 없을 경우에 yield 가 실현됩니다. 여기선 Member 객체를 만들죠. 그 후에 fold 를 통해서
문제가 있다면 첫번째 함수를 따르고 문제가 없다면 두번째 member => member 를 실행해서 결국 member 가 리턴됩니다.
예제 8)
case class Postcard(msg: String)
def sendPostcards: List[Postcard] = {
val states = List("Arizona", "New Mexico",
"Texas", "Louisiana",
"Mississippi", "Virginia",
"New York", "Ohio",
"Illinois")
val relatives = List("Grandma", "Grandpa", "Aunt Dottie", "Dad")
val travellers = List("Kelsey", "DJ")
var postcardList: List[Postcard] = List()
for (h <- 0 until travellers.length) {
val sender = travellers(h)
for (i <- 0 until relatives.length) {
val recipient = relatives(i)
for (j <- 0 until states.length) {
val theState = states(j)
postcardList ::=
new Postcard("Dear " + recipient + ", " +
"Wish you were here in " +
theState + "! " +
"Love, " + sender)
}
}
}
postcardList
}
위의 예제를 아래처럼 for - yield 를 사용해서 바꿔 줄 수 있습니다. (정확히 동일하지 않습니다)
def sendPostcards3: List[Postcard] = {
val states = List("Arizona", "New Mexico",
"Texas", "Louisiana",
"Mississippi", "Virginia",
"New York", "Ohio",
"Illinois")
val relatives = List("Grandma", "Grandpa", "Aunt Dottie", "Dad")
val travellers = List("Kelsey", "DJ")
for {
traveller <- travellers
sender = traveller + " (your favorite)"
relative <- relatives
theState <- states
if (relative.startsWith("G"))
} yield {
new Postcard("Dear " + relative + ", " +
"Wish you were here in " +
theState + "! " +
"Love, " + sender)
}
}
각각의 리스트들을 for 문 안에서 하나를 가져와서 if 문을 통해 데이터를 솎아내고, 그 데이터들을 이용하여 yield 를 통해서 하나의 객체를 만든 후에 다시 순회하면서 객체의 List 를 리턴해 주고 있습니다.
각각의 리스트가 5,5,5 개라면 최대 5*5*5 만큼의 객체가 생성될것입니다.
'Scala' 카테고리의 다른 글
스칼라 강좌 (31) - 스칼라에서 사용되는 심볼들 (0) | 2016.12.07 |
---|---|
스칼라 강좌 (30) - type projection ( # 에 관하여) (0) | 2016.12.06 |
스칼라 강좌 (28) - Currying(커링) (0) | 2016.11.08 |
스칼라 강좌 (27) - 모나드 (Monad) (0) | 2016.11.08 |
스칼라 강좌 (26) - JSON 다루기 (0) | 2016.10.19 |