관리 메뉴

HAMA 블로그

스칼라 강좌 (3) - List 본문

Scala

스칼라 강좌 (3) - List

[하마] 이승현 (wowlsh93@gmail.com) 2016. 6. 15. 21:25

이 시리즈는 스칼라언어의 창시자인 마틴 오더스키가 직접 저술한   Programming in Scala (2판)   

을 참고로 하여 정리할 예정입니다.  잘못된 점이 있으면 지적해주시면 바로 수정하겠습니다.


List

 
특성
 

스칼라의 배열이 값을 변경 할 수 있는 순서가 정해진 시퀀스라면 스칼라의 리스트는 기본적으로  값을 변경 할 수 없는 시퀀스입니다. 

함수형 스타일이라는것은 메소드 내에서 절대로 부수효과가 일어나면 안되는, 그래서 더 신뢰할 수 있고 재사용하기 쉬운 코드를 만드는게 주 목적이라 , 그 목적에 적합한 콜렉션이라 할 수 있습니다.
Linked List 식으로 구현 되 있으므로 head / tail  중간 삽입같은게 원할합니다. 
 
생성 

배열 생성과 비슷합니다.

val list = List(1,2,3)   // new 와 [Int] 가 생략되었습니다. apply 라는 팩토리 함수가 암시적으로 호출.
아래 처럼 다양하게 만들 수 있습니다. 

val x = List.range(1, 10) // 범위로 List(1, 2, 3, 4, 5, 6, 7, 8, 9)

val x = List.fill(3)("foo") // List(foo, foo, foo)
List.tabulate(5)(n => n * n) // List(0, 1, 4, 9, 16)

 

자바)  
List b = new List;  <-- 안된다 . 인터페이스임
String a[] = new String[2]; 
List b = Arrays.asList(a); 
List<String> list =new ArrayList<String>();
List<String> list =new LinkedList<String>();  

코틀린) 
val z = List()  <-- 안된다 . 인터페이스임
val a = listOf("hi", "bye")  
val b = List<String>(2, {it -> it.toString()})  
val c = mutableListOf<String>()

 

 

원소 가져오기 

val a = List(1,2,3) 

a(2)  

  

두개의 리스트 합치기 

두개의 리스트를 합쳐보겠습니다.

val a = List(1,2)

val b = List(3,4)

val c = a ::: b

이렇게 합니다.

:::  는 두개의 리스트를 합쳐주는 메소드 입니다.

val d = a ++ b  이렇게 할 수도 있습니다.  ( ::: 는 오로지 리스트에서만 사용) 

Scala list concatenation, ::: vs ++

 

리스트 앞에 요소 추가 

 

그럼 앞에다가 요소하나를 붙여주는 메소드는 무엇일까요?

List(1,2) 앞에다가 0 을 붙여서  (0,1,2) 로 만드는 방법 말이죠.

그건  :: 입니다.  콜론이 2개짜리네요.

근데 여기서 생각해 볼것은 서두에 List 는 변경 불가능하다고 했는데 앞에 숫자를 붙히다니? 

무슨 소리하는지 의심스러울거 같은데요.

예를 한번 봅시다.

val a = List(1,2)

val b = 0 :: a

이건데요. 

네 a 를 바꾼게 아니었습니다.  a 앞에 0 을 추가한 또 다른 b 라는 List 를 만든거에요.

즉 무엇을 변경해서 쓰려면, 기존것은 냅두고 기존것을 이용해서 새것을 만들어서 쓰라는 말입니다.

또 궁금한게 있을거 같은데요.

도대체 0 :: a 를 하는데 어떻게 a 앞에 0 이 들어가게 되는지 말이죠.

그 이유는 

:: 메소드는 0 의 메소드가 아니라  a 의 메소드입니다. 근데 앞에 있는 이유는 

그냥 규칙입니다. -.-;; 

우리의 마틴오더스키씨는 아주 많은걸  자신이 만든 언어에 쑤셔 넣었습니다.

그 규칙은 이름이 콜론(:) 으로 끝나는 메소드는 오른쪽 피연산자의 것으로 호출한다. 라고 합니다.

따라서

0 :: a  는  a.::(0) 이 되는 것입니다.

 

리스트 뒤에 요소 추가 

 

그럼 뒤에 추가하는것은 무엇일까요?

:+  입니다.

예를들어  

val a = List(1,2)

val b = a :+ 2

이런거죠.

하지만 웬간하면 이걸 쓰지마세요.

뒤에 추가하는 연산은 리스트의 길이만큼 오래 걸린다고 합니다.

이걸 효율적으로 하려면

일단 리스트를 뒤집고, 앞에다가 원소를 추가한후에 다시 뒤집으세요.

(4 :: List(1,2,3).reverse).reverse

- 새 리스트로 추가한다.

List(1,2,3) ::: List(4)

 

입니다.

 

 

List 의 다양한 메소드들 

 

자 이런 변경 불가능한 List 를 사용해서 편하게 작업할 수 있는 방법들이 무지 많습니다.

문제는 편해지려면 이거 공부하고 외워야한다는거죠.  러닝커브가 올라갑니다. ;;

대표적인 몇가지만 살펴보도록 하겠습니다.


// 빈리스트

List()   

 

// 두번째 인덱스 원소 얻기 

val a = List(1,2,3) 

a(2)

 

// 두번째 원소 까지  제거

val a = List(1,2,3) 

val b = a.drop(2) 

결과 : List(3)  

눈여겨 볼것은 자체의  원소를 제거한게 아니라,  원소 제거한 새로운 리스트를 반환합니다. 

 

// 요소중 길이가 3인것의 개수를 센다

val a = List ("hello", "world", "boy") 

a.count(s=> s.length == 3)   

s=> s.length == 3 은 람다식이죠? 인자가 s 인 함수란 야그입니다.  다음과 같아요

bool  func ( s ) {

  return s.length == 3

}

 

// 리스트의 각 원소를 변경하여 새 리스트를 반환합니다. 여기선 각 문자열 뒤에 X 를 붙힙니다.

val a = List ("hello", "world", "boy") 

val b = a.map(s => s + "X") 

자 다양한 메소드를 보았는데요. 이거 말고도 더 있긴합니다. 근데 여기서 생각해 봐야할것은 메소드가 많구나~~ 이게 아닙니다.
이런 메소드를 사용함으로써 var 를 안쓰게 됬다는겁니다. var 은 변경 가능한 변수를 만들때 씁니다.
예를들어 
위에서 count 같은 경우 대략 함수를 만들어 보면 

def count( all : Seq[String] ):Int = { 
   var temp = 0  
   for ( one <-  all ) {       
    if ( one.length == 4 )
         
       temp = temp + 1
   
    }
 
    temp
}

이렇게 되잖습니까? 위에 보면 var 가 사용되었네요. 스칼라에서는 var 를 사용하지 말도록 합시다!!! (마틴오더스키는 var, val 둘다 만들어서 알아서 적재적소에 쓰이길 바랬지만 , 제 글을 보시는 분들은 절대로 var 를 안쓰는 방향으로 '만' 생각하자구요. ) 

 

* 재귀를 활용한 count 함수  

 def count(list: Seq[String]): Int = {      
 	@tailrec def count(value: Int, remaining: Seq[String]): Int = {       
      remaining match {         
      	case head :: tail if head.length == 4 => count(value + 1, tail)         
        case head :: tail => count(value, tail)         
        case Nil => value       
        }     
     }      
     count(0, list)   
 }

이렇게 count 를 var 없이 구현 할 수 있습니다. 꼬리재귀라는것을 사용한것인데요

순수함수형 언어에서는 재귀를 주로 사용한다네요. 공재귀로 분해해보는 연습을 해야 합니다. 이거보고 스칼라는 쓸 때없이 복잡한거네라고 생각하지마시구요 여기서는 그냥 이런게 있구나 하고 지나치시고 나중에 다시 차근차근 쉽게쉽게 살펴보자구요. 분명히 먼가 좋으니깐 저렇게 쓰는거다라고 생각하시고 넘어갑시다.

 
 

마지막 

List 의  짜증나는 규칙을 하나 억지로 외워봅시다.

val a = 1 :: 2 :: 3 :: Nil

는 List(1,2,3) 을 만들게 됩니다.

저 Nil 은 뭘까요? 

리스트 끝에 Nil 이 필요로 하는 이유는 :: 메소드가 List 클래스의 멤버이기 때문이랍니다.

만약 1 :: 2 :: 3 만 사용했다고 치면 , 마지막의 3 이 Int 형이라서 :: 메소드가 없기때문에

그냥 꽝이 되버리는 반면

마지막에 Nil 을 넣어주게되면 Nil 은 List 의 멤버이기때문에 타입을 추론해서 List 로 만들어 줍니다. 

'Scala' 카테고리의 다른 글

스칼라 강좌 (6) - Map  (0) 2016.06.20
스칼라 강좌 (5) - Set  (0) 2016.06.19
스칼라 강좌 (4) - Tuple  (0) 2016.06.19
스칼라 강좌 (2) - Array  (0) 2016.06.13
스칼라 강좌 (1 ) - 소개  (0) 2016.06.13
Comments