본문 바로가기
Programming/Scala

[2강] 배열과 리스트

by 유주원 2015. 4. 14.

배열


기타 다른 언어들과 마찬가지로 스칼라도 배열과 리스트 타입의 자료구조를 제공한다.

아래 보이는 예는 배열 타입에 대한 사용 방법을 예시하였다.


val greetStrings = new Array[String](3)

greetStrings(0) = "Hello"

greetStrings(1) = ", "

greetStrings(2) = "World!\n"


for ( i <- 0 to 2)

    print(greetStrings(i))


코드는 간단하지만 이 코드에는 상당히 심오한 스칼라의 내용이 숨겨져 있다.


우선 new Array[String] 이란 타입으로 객체를 선언한 후 3이란 파라미터 값을 넘겨서 크기가 3인 String 타입의 배열로 인스턴스화 하였다.

앞에 val로 선언하였기 때문에 분명 이 배열은 immutable이다. 


여기서 첫번째로 설명할 것이 이 해당 배열의 타입이나 크기가 immutable이지 해당 배열 내의 원소들이 가리키는 객체가 immutable은 아니라는 것이다. val로 선언한 배열에 대한 Array[String](3)의 타입과 크기는 불변인 반면에 각각의 배열 원소가 가리키는 내용들은 mutable 즉 변경 가능하다라는 이야기다.

초 간단하게 설명해서 greetStrings(1) =" ????" 로 코딩하면 1의 원소가 "????"를 가리키는 걸로 바뀔 수 있다는 것이다.

그럼 결과는 "Hello ???? World!"로 나타날 것이다.


두 번째 설명 사항은 기타 다른 배열에서는 []를 쓰는 반면에 스칼라에서는 ()로 배열 값을 가져오고 있다.

스칼라에서 greetStrings(0)이라고 호출하면 인터프리터에서는 그 코드를 greetStrings.apply(0)이란 매소드로 바꾼다.

결국 배열의 원소를 가져오는 것은 배열 객체안에 있는 apply라는 매소드를 호출하는 것과 같다고 생각하면 되고, () 안의 해당하는 값은 매소드의 파라미터라고 생각하면 쉽다.

그렇기 때문에 []를 사용하지 않고 ()를 사용하는 것이다.

스칼라처럼 사용하는 방식이 어떻게 보면 더 일관적인 표현 방법인 것 같기도 하다.

참고적으로 greetStrings(0) = "Hello"라는 코드는 인터프리터에서 greetStrings.update(0, "Hello")란 매소드로 치환한다.

인터프리터에서는 greetStrings(0) 뒤에 = 이라는 할당 값을 보고 update 매소드로 치환을 하는 것이다.


마지막으로 for( i <- 0 to 2) 구문에 대한 설명이다.

분명 처음 본 코딩 방식이다.

0 to 2는 (0).to(2)의 표현 방식과 동일하다. 매소드가 파라미터 하나만 요구할 경우에는 점과 괄호를 생략할 수 있다는 전제하에 (0).to(2)가 0 to 2로 표현될 수 있는 것이다.

(0).to(2)는 0이란 객체에 있는 to란 매소드를 호출하는데 2라는 파라미터를 넘겨라.. 정도로 해석할 수 있을 것이다.

결국 (0).to(2)는 0,1,2를 가지는 sequence란 객체를 생성하며 for문에는 이 sequence 객체를 iterate 함으로써 print 하는 것이다.


스칼라에는 연산자 오버로딩을 제공하지 않으며, 모든 연산자가 객체의 매소드이다.

예를 들어, 1+2란 표현식이 있다면 이는 1이란 Int 객체에 +란 매소드를 호출한 것이다. 2는 당연히 + 매소드의 파라미터이다.


1 + 2 => (1).+(2)


배열을 초기화 할 때에는 위와 같은 방법을 사용하기도 하지만 더 간편하게 아래와 같이 사용하기도 한다.


val greetStrings = Array("Hello", ", ", "world!\n")

 

위의 방식은 Array.apply("Hello", ", ", "world!\n")와 동일하게도 생각할 수 있다.


리스트


리스트도 배열과 비슷한 방식으로 선언하고 사용할 수 있다. List는 배열과 다르게 내부의 원소값을 변경할 수가 없는 immutable 객체이다. 만약 List에 어떤 원소를 append 한다고 하면, 기존 List에 추가되는 것이 아니라 새로운 List객체가 만들어진 것이라고 생각하면 된다.


val oneTwo = List(1,2)

val threeFour = List(3,4)

val oneTwoThreeFour = oneTwo ::: threeFour 


::: 매소드가 새롭게 등장하였다. :::는 리스트와 리스트를 합칠 때 사용하는 매소드이다. oneTwoThreeFour 리스트의 값은 아마 List(1,2,3,4)가 될 것이다.


기존 리스트에 새로운 원소를 추가할 경우에는 :: 매소드를 사용한다. Lisp을 공부해 보신 분이라면 ::는 cons라고 생각하면 쉬울 것이다.


val twoThree = List(2,3)

val oneTwoThree = 1 :: twoThree


:(콜론)으로 끝나는 매소드는 기존 방식과는 다르게 오른쪽 객체의 매소드라고 생각하면 된다.

기존의 1 + 2 에서 +가 1에 대한 매소드였다면, 1::twoThree에서 ::는 twoThree에 대한 매소드이고 1이 파라미터에 해당한다.

그래서 1::twoThree는 (twoThree).::(1)이란 동일하다고 보면 된다.


아래 그림은 List에서 제공하고 있는 다양한 매소드를 실습한 샘플이다. 한번 보면 어떤 기능을 하는지 대략적으로 알 수 있을 것이다.