val sites = Set("google.com", "yahoo.com")
var newSites = sites
newSites += "daum.net"
println("sites : " + sites)
println("newSites : " + newSites)
// output
// sites : Set(google.com, yahoo.com)
// newSites : Set(google.com, yahoo.com, daum.net)
위의 코드처럼 "daum.net"을 추가하면 기존의 Set을 수정하지 않고 새 엘리먼트가 추가된 새로운 Set을 리턴하는데 이는 기본적으로 인클루드되는 Predef가 immutable구현의 Set과 Map의 별칭을 제공하고 있기 때문에 Immutable Set을 사용하게 됩니다. new없이 Set의 인스턴스를 생성했는데 Set(1,2,3)을 사용하면 Set[Int]를 리턴받게 되는데 이는 factory메서드라고도 불리는 apply()때문에 가능합니다.
Set
val temp = Set("First", "Second", "Third")
filter() - 원하는 문자열이 들어간 결과를 검색합니다. - temp filter(_ contains "First") 하면 Fisrt가 추가된 새로운 Set이 리턴됩니다.
mkString() - 각 엘리먼트들을 파라미터로 넘김값으로 연결한 스트링을 생성합니다 : temp.mkString(", ")
++() - 2개의 Set을 합쳐서 새로운 Set을 생성합니다 : temp ++ Set("111", "222")
**() - 2개의 Set에서 공통적인(intersect) 부분을 찾아줍니다 : temp ** Set("Second", "333")
foreach() - Set 엘리먼트를 이터레이트합니다 : temp.foreach { a => println( a ) }
Map
val temp = Map("a" -> "ex1", "b" -> "ex2", "c" -> "ex3")
filterKeys() - 특정키로 선택한 Map을 리턴합니다. : temp filterKeys(_ startsWith "a" )
filter() - 키대신 값을 통해서 필터링 할 수 있으며 filter()에 제공한 펑션밸류는 (key, value) 튜플을 받습니다.
temp filter { a =>
val (key, value) = a
(key startsWith "a") && (value contains "ex")
}
get() - 특정 엘리먼트를 얻을 수 있습니다. 주어진 키를 위한 값이 없다면 리턴타임은 Option[T]이고 결과는 Some[T]이거나 None이 됩니다.(여기서 T는 Map 값의 타입입니다.) : temp.get("a") val (key, value) = a
(key startsWith "a") && (value contains "ex")
}
get()하는 대신에 apply()를 사용해서도 키에 대한 값을 얻을수 있는데 차이점은 Option[T] 대신에 값을 리턴하게 되며 주어진 키에 맞는 값이 없다면 예외를 던지기 때문에 코드를 try-catch블럭안에 넣어줘야 합니다.
update를 사용하면 엘리먼트를 추가할 수 있습니다만 immutable 컬렉션이기 때문에 원래의 Map에 추가한 새로운 Map을 리턴합니다. : temp.update("d", "ex4")
할당의 좌편에서 클래스나 인스턴스에 괄호를 사용하면 자동으로 update()메서드를 호출하기 때문에 X() = b는 X.update(b)와 동일하게 사용할 수 있습니다. update()에 파라미터를 전달한다면 다음과 같이 괄호 안에 파라미터를 두면 됩니다. X(a) = b는 X.update(a,b)와 동일합니다.
List
List는 Set, Map과는 다르게 오직 Immutable만 가지고 있습니다. 첫 엘리먼트는 head 메서드를 이용해서 접근하고 나머지는 모두 tail메서드를 이용해서 접근합니다. last() 메서드는 엘리먼트를 모두 가로질러야 하기 때문에 head나 tail보다 많은 비용이 들게 됩니다.
val temp = List("ex1", "ex2", "ex3", "ex4")
head() - List의 첫번째 엘리먼트에 접근할수 있습니다. 0부터 시작하는 index를 이용해서도 접근이 가능합니다. : temp.head 와 temp(0) 은 같습니다.
::() - List앞에 엘리먼트를 추가합니다. : "ex5" :: temp
:::() - 메서드 뒤에 나오는 리스트 앞에 리스트를 추가합니다. : temp ::: List("ex6", "ex7")
filter() - 특정조건을 만족시키는 엘리먼트를 찾습니다. : temp.filter(_ contains "ex")
forall() - 모든 엘리먼트가 조건을 만족하는지 확인합니다. : temp.forall(_ contains "ex")
exists() - 조건을 만족시키는 엘리먼트가 있는지 확인합니다. : temp.exists(_ contains "ex2")
메서드명의 관례(Method Name Convention)
메서드명의 첫번째 캐릭터로 실행의 우선순위를 결정하는데 메서드의 마지막 캐릭터는 메서드 호출의 타겟을 결정하는데 영향을 줍니다. 메서드명 끝에 콜론(:)이 있다면 메서드의 호출 타겟이 메서드 뒤에오는 인스턴스가 됩니다. 그래서 value :: list는 실제로는 value를 아규먼트로 갖는 list로 list.::(value)와 같습니다.
단항연산자로
class Ex {
def unary_+ = println("1")
def unary_! = println("2")
}
val sample = new Ex
+sample
!sample
단항연산자로 +나 -를 사용하면 unary_+()나 unary_!()를 호출합니다.
for 표현식
for([pattern <- generator; definition*]+; filter*)
[yield] expression
위 정의처럼 for 표현식은 정의로 0개이상의 defenition과 filter와 함께 하나 이상의 generator를 파라미터로 취하며 세미콜론(;)으로 분리가 되어 있습니다. yield 키워드는 선택적이며 Unit대신의 값의 List를 리턴합니다.
val result = for (i <- 1 to 10)
yield i * 2
yield i * 2
위 코드는 1부터 10까지의 수의 2배값의 컬렉션이 리턴됩니다.
val result2 = (1 to 10).map(_ * 2)
map을 사용하면 같은 결과를 위와 같은 코드로 얻을 수 있습니다.
val doubleEven= for (i <- 1 to 10; if i % 2 == 0)
yield i * 2
yield i * 2
위 코드처럼 filter를 사용하면 짝수일 경우에만 2배값으로 처리하도록 할 수 있습니다. 펑셔널 프로그래밍에서 이것을 List comprehension 이라고 부르며 아래와 같이 curly brace로 사용할 수도 있습니다.
for {
i <- 1 to 10
if i % 2 == 0
}
yield i * 2
i <- 1 to 10
if i % 2 == 0
}
yield i * 2
아래처럼 제너레이터와 함께 정의를 둘 수 있습니다.
val temp = List("ex1", "ex2", "ex3")
val result = for( t <- temp; p = t + " is good") yield p // List(ex1 is good, ex2 is good, ex3 is good)
제너레이터를 2개 사용하려면 아래처럼 사용합니다.
for (i <- 1 to 3; j <- 4 to 6) {
print("[" + i + "," + j + "] ")
}
// output
// [1,4] [1,5] [1,6] [2,4] [2,5] [2,6] [3,4] [3,5] [3,6]
와.. 역시 "정리대마왕"님 답네요. 좋은 요약 감사합니다.
책으로 읽는게 잘 기억에 안남아서 시작했는데 먼가 어려워요...
근데 그 아이콘 작게 뜨니까 좀 무섭;;;
아름다운 글이네요.
이글을 아름답다고 느끼실 정도면 경지가.. ㄷㄷ
역시 파블로!! 우훗~
8장을 이제야 한거지.. 이제 10장 차례인데 덜덜