map, foreach이번에는 리스트의 메소드들에 대해서 알아보도록 하겠습니다. 메소드의 종류가 좀 많습니다만, 어휘가 많으면 간결한 표현도 가능하고, 더불어 의도 또한 전달하기 좋아지겠죠.
map가장 자주 사용하는 대표적인 메소드는 map이라고 볼 수 있겠네요. 컬렉션의 요소를 말 그대로 매핑 합니다.
val ls = List(1, 2, 3)
위와 같은 배열이 있을 때, 각각의 요소에 x2를 하고 싶을 때 map을 사용 할 수 있습니다.
val ls2 = ls.map((x: Int) => x * 2)
(x: Int)에서, : 우측에는 인자(x)의 타입을 기입합니다. 타입 표기는 생략가능한 경우도 있고, 반드시 필요한 경우도 있습니다. 이 경우는 컴파일러가 추론 가능하니 생략하는 편이 좋겠네요. (생각보다 스칼라 컴파일러가 똑똑합니다. 아쉬울때도 있지만…)리스트에 Int 타입이 담긴 것이 뻔하니, 다음처럼 생략이 가능합니다.
val ls2 = ls.map(x => x * 2) //> ls2 = List(2, 4, 6)
조금 더 줄일 수 있습니다.
val ls2 = ls.map( _ * 2 ) //> ls2 = List(2, 4, 6)
_는 place holder라고 불립니다. 뭐가 올지 뻔한 상황에서 사용됩니다. 개인적으로 전라도 사투리의 “거시기”와 비슷하다고 생각합니다. (“거시기 두 배로 만들라…”(죄송합니다.;))ls2에 할당 되는 것입니다. ls에 저장 된 객체의 값(1, 2, 3)이 변경되는 것이 아닙니다.ls //> List(1, 2, 3)
ls2 //> List(2, 4, 6)
그리고 => 기호가 보입니다.
map(x => x * 2)
=> 기호는 정식 명칭은 right arrow라고 했다고 합니다. (오더스키 아저씨가…) 그런데, 아시다시피 ->기호(map에서 key-value를 지정 할 때 사용)도 있어서 혼란의 여지가 있습니다. 그래서 rocket, fat arrow 라는 이름도 거론 되는 것 같습니다. 참고: what-is-called-in-scala
아무튼, => 왼쪽에 있는 x인자는 리스트의 요소입니다.(이름은 변수명 짓듯이 임의로 정할 수 있습니다.), 즉x는 순차적으로 1, 2, 3 각각에 대응합니다. 그리고, => 우측에는 원하는 표현식을 써주면 됩니다.
x => x * 2부분을 function literal 이라고 부릅니다.이름에서도 알 수 있듯이 x => x * 2는 함수(리터럴)입니다.
위의 코드에서 함수(리터럴)를 추출해서, 다음과 같이 바꿔 보겠습니다.
val double = (x: Int) => x * 2
val ls2 = ls.map(double) // ls2 = List(2, 4, 6)
(x:Int) => x * 2 부분이 거의 비슷하죠. val로 할당한 함수는 다른 곳에서 재사용 할 수 있겠네요.
def키워드를 이용해서, 메소드를 정의 해서 사용 할 수도 있습니다.
def double(x: Int) = x * 2
ls.map(double) // ls = List(2, 4, 6)
def로 선언 된 부분은 함수(function)이라고 부르지 않고 메소드라고 하는 편이 옳습니다. 지금으로써는 크게 중요하지 않지만, 스칼라에서 말하는 함수와 메소드를 구분하는 것은 필요합니다. 메소드는 객체내에서 필드 조작에 주 목적이 있고, 함수는 그 자체가 독립적으로 사용되는 성격이 강합니다. (나중에 자세히 다룰 예정입니다.)다시 map으로 돌아와서 Int요소를 String 으로 변경하는 코드를 보겠습니다.
val ls = List(1, 2, 3)
ls.map(x => x.toString) // List("1", "2", "3")
다음은 대문자로 변경하는 코드입니다.
val cities = List("seoul", "tokyo", "paris")
cities.map(city => city.toUpperCase()) //> List("SEOUL", "TOKYO", "PARIS")
각각 _를 사용해서, 다음과 같이 줄일 수 있습니다.
ls.map( _.toString )
cities.map(_.toUpperCase())
map블럭을 ( ) 대신 { }로 감싸고 ;로 구분해서 다른 일들을 할 수도 있습니다.
cities.map{city => println(city); city.toCharArray()}
{ }를 사용하면 여러줄에 기술할 수도 있고, ;도 생략 할 수 있습니다.
cities.map {
city =>
println(city)
city.toCharArray()
}
//> seoul
//> tokyo
//> paris
//> List(Array(s, e, o, u, l), Array(t, o, k, y, o), Array(p, a, r, i, s))
foreach그리고foreach메소드가 있습니다. 단순히(, )내의 문장을 수행합니다.
val ls = List(1, 2, 3)
ls.foreach(x => println(x))
물론 할당도 가능하지만, 이경우는Unit = ()(아무것도 없음. void와 비슷함)이 됩니다.
val res = ls.foreach(x => println(x)) // res : Unit = ()
그래서 foreach 문에서 사이드이펙트를 일으키는 작업이나, 리스트 외부의 변수에 접근하는 작업이 수행되곤 합니다. 마찬가지로 _를 사용해서, 코드를 줄여보겠습니다.
ls.foreach(println(_))
//> 1
//> 2
//> 3
심지어는, _도 생략 할 수 있습니다. (foreach에서 전달되는 인자(arg)도 뻔하고, println에서 받는 인자(param)도 뻔하니… 인자를 배달하는 일은 컴파일러가 알아서 해주는 것이죠.)
ls.foreach(println)
좀 깔끔하게 다음과 같이 바꿀수도…
ls foreach println
다음 코드는 foreach 외부에 변수를 두어서 합계를 구하는 코드입니다.
var sum = 0
ls.foreach(x => sum += x)
println(sum) // sum = 6
foreach문이 길어지는 경우에도 다음처럼 ( )를 { }로 바꿀 수 있습니다. (항상 ( )를 { }로 대체 할 수 있는 것은 아닙니다.)
ls.foreach {
x =>
val y = doSomething(x)
println(y)
sum += y
}
--
Google 그룹스 '라 스칼라 코딩단' 그룹에 가입했으므로 본 메일이 전송되었습니다.
이 그룹에서 탈퇴하고 더 이상 이메일을 받지 않으려면 scala-korea...@googlegroups.com에 이메일을 보내세요.
이 그룹에 게시하려면 scala...@googlegroups.com(으)로 이메일을 보내세요.
웹에서 이 토론을 보려면 https://groups.google.com/d/msgid/scala-korea/cfb6ada3-caa7-47a5-ba46-32fc6361a4af%40googlegroups.com 을(를) 방문하세요.
더 많은 옵션을 보려면 https://groups.google.com/groups/opt_out을(를) 방문하세요.