[scala] List 예시 - 2

scala 2016. 9. 19. 18:34


List의 메소드에 대한 설명이다. 

head 메소드는 리스트의 첫 번째 원소를 리턴하지만, tail 메소드는 head 메소드에서 리턴하는 값을 제외한 나머지를 리턴한다(나는 언제나 이 부분이 어색하다). isEmpty 메소드는 List가 비어 있는지 확인한다. 


val list = List(1,2,3)
println(list.head)
println(list.tail)
println(list.isEmpty)


결과는 다음과 같다.


1

List(2, 3)

false








리스트 소스를 보면, 리스트의 멤버 변수는 다음과 같다. 


def isEmpty: Boolean
def head: A
def tail: List[A]





빈 리스트에 isEmpty 메소드를 호출하고 head 메소드를 호출해본다. 

val list = List()
println(list.isEmpty)
println(list.head)

결과는 다음과 같다. head에 기본적으로 Nil이 저장되기때문에 관련 에러가 발생한다.


true

Exception in thread "main" java.util.NoSuchElementException: head of empty list

at scala.collection.immutable.Nil$.head(List.scala:420)

at scala.collection.immutable.Nil$.head(List.scala:417)



Nil을 할당하면, head메소드를 호출할 수 있다. 

val list2 = Nil
println(list2.head)


결과는 다음과 같다. 

Exception in thread "main" java.util.NoSuchElementException: head of empty list

at scala.collection.immutable.Nil$.head(List.scala:420)




Nil은 왜 List일까 생각할 수 있겠지만, Nil은 List[Nothing]을 상속받은 객체이다. 즉, 리스트  케이스 타입 중 하나이다. 


case object Nil extends List[Nothing] {


리스트 객체에 tail을 계속 붙일 수 있다.
val list = List(1,2,3)
println(list.tail.tail)
println(list.tail.tail.tail)

결과는 다음과 같다.

List(3)
List()



tail을 엘리먼트 개수 대비해서 많이 호출하면 에러가 발생할 수 있다. 


println(list.tail.tail.tail.tail)

결과는 다음과 같은 Exception이 발생된다. 



Exception in thread "main" java.lang.UnsupportedOperationException: tail of empty list

at scala.collection.immutable.Nil$.tail(List.scala:422)

at scala.collection.immutable.Nil$.tail(List.scala:417)




head, tail과 비슷한 last와 init이 있다. 


val list = List(1,2,3)
println(list.init)
println(list.last)


결과는 다음과 같다.


List(1, 2)

3




리스트의 length 메소드는 리스트의 길이를 리턴한다.

val list = List(1,2,3)
println(list.length)


결과는 다음과 같다.


3



리스트의 reverse 메소드이다. 

val list = List(1,2,3)
println(list.reverse)

결과는 다음과 같다.


List(3, 2, 1)




리스트의 drop 메소드이다. drop(1)은 처음부터 첫 번째 엘리먼트까지 버리고 나머지만 얻고, drop(2)는 처음부터 두 번째 엘리먼트까지 버리고 나머지를 버린다는 의미이다.

val list = List(1,2,3)
println(list.drop(1))
println(list.drop(2))

결과는 다음과 같다. 


List(2, 3)

List(3)



리스트의 splitAt 메소드는 다음과 같다. 


val list = List(1,2,3)
println(list.splitAt(1))
println(list.splitAt(2))

결과는 다음과 같다.


(List(1),List(2, 3))

(List(1, 2),List(3))



스칼라는 각 엘리먼트 요소에 index로 접근할 수 있다. 

val list = List(1,2,3)
println(list(0))
println(list(1))
println(list(2))
println()
println(list.apply(0))
println(list.apply(1))
println(list.apply(2))

결과는 다음과 같다.


1

2

3


1

2

3



만약 index를 잘못 넣어 가르키는 index에 엘리먼트가 없다면 IndexOutOfBoundsException이 발생한다. 


Exception in thread "main" java.lang.IndexOutOfBoundsException: 3
at scala.collection.LinearSeqOptimized$class.apply(LinearSeqOptimized.scala:65)
at scala.collection.immutable.List.apply(List.scala:84)


List에 index를 두든, apply(index)를 두든 아래 소스를 찾도록 되어 있다. 소스를 찾아보면 다음 구현으로 되어 있다.
먼저 drop으로 list를 얻은 후, 비어있지 않는다면 가장 맨 앞의 엘리먼트를 리턴하도록 되어 있다. 
def apply(n: Int): A = {
val rest = drop(n)
if (n < 0 || rest.isEmpty) throw new IndexOutOfBoundsException("" + n)
rest.head
}

flatten과 zip은 다음에서 설명했다.
http://knight76.tistory.com/entry/scala-map-flatten-flatmap-%EC%98%88%EC%8B%9C

zip와 unzip은 다음에서 설명했다.
http://knight76.tistory.com/entry/scala-zip-unzip-%EC%98%88%EC%8B%9C



Array의 mkString 메소드와 달리 List의 mkString은 비슷하다.
(http://knight76.tistory.com/entry/scala-Array-%EC%98%88%EC%8B%9C)

val list = List("s", "a", "m", "u", "e", "l")
println(list.mkString(":"))

결과는 다음과 같다.

s:a:m:u:e:l


리스트의 toArray는 배열로 변경한다. 
val list = List("s", "a", "m", "u", "e", "l")
println(list.toArray mkString " ")

결과는 다음과 같다.

s a m u e l




리스트의 copyToArray는 기존의 배열에 복사한다. 
val list = List("s", "a", "m", "u", "e", "l")
val emptyList = new Array[String](7)
list.copyToArray(emptyList, 0)
println(emptyList mkString " ")
결과는 다음과 같다. 초기화된 배열은 모두 null이기 때문에 맨 마지막 요소는 null로 표시되었다.

s a m u e l null



리스트의 map 예시이다. 
val list = List("s", "a", "m", "u", "e", "l")
println(list.map(_ + "X"))
println(list.flatMap(_ + "X"))

결과는 다음과 같다. 

List(sX, aX, mX, uX, eX, lX)
List(s, X, a, X, m, X, u, X, e, X, l, X)




List의 filter와 find 메소드의 예시이다. 
val list = List("s", "a", "m", "u", "e", "l")
println(list.filter(_ == "s"))
println(list.find(_ == "s"))

결과는 다음과 같다. filter의 결과 타입은 List이지만, find의 결과 타입은 Some이다. 

List(s)
Some(s)



만약 없는 요소를 filter, find하면 확연히 결과를 자세히 알 수 있다.

println(list.filter(_ == "z"))
println(list.find(_ == "z"))

결과는 다음과 같다.

List()
None



리스트의 partition은 리스트를 두 개로 나눈다.
val list = List(0,1,2,3,4,0)
println(list.partition(_ > 3))


결과는 다음과 같다.
(List(4),List(0, 1, 2, 3, 0))

partition메소드의 원형을 보면, predicate가 참이면, 두 개의 리스트를 리턴한다. 
def partition(p: A => Boolean): (Repr, Repr) = {
val l, r = newBuilder
for (x <- this) (if (p(x)) l else r) += x
(l.result, r.result)
}




리스트의 takeWhile은 좀 특이한 메소드이다. 함수를 받고, 조건식이 맞으면 ListBuffer에 넣었다가 조건식이 맞지 않을 때의 값을 리턴한다.  여기서 완전 주의할 것이 처음 엘리먼트부터 참이어야 한다는 점이다.!!!

@inline final override def takeWhile(p: A => Boolean): List[A] = {
val b = new ListBuffer[A]
var these = this
while (!these.isEmpty && p(these.head)) {
b += these.head
these = these.tail
}
b.toList
}

예시를 살펴본다. 
첫 번째, 4보다 작은 predicate을 주었다. 리스트의 처음 엘리먼트부터 참으로 가다가 엘리먼트의 값이 4에서 false가 되었다. (결과는 List(0,1,2,3) 일 것이다.)
두 번째, -1보다 큰 predicate을 주었다. 리스트의 처음 엘리먼트부터 참이다가, 엘리먼트가 -1인 부분에서 false가 되었다. (결국 결과는 List(0,1,2,3,4,5)가 될 것이다)
세 번째, 리스트의 처음 엘리먼트의 값은 3보다 크지 않다. 따라서 어떠한 List 엘리먼트도 추가되지 않는다. 결과는 List()가 된다.

val list = List(0,1,2,3,4,5,-1,0)
println(list.takeWhile(_ < 4))
println(list.takeWhile(_ > -1))
println(list.takeWhile(_ > 3))



예상대로 되었는지 결과를 살펴본다.


List(0, 1, 2, 3)

List(0, 1, 2, 3, 4, 5)

List()



다음은 takeWhile의 반대 메소드인 dropWhile이다. 결과를 살펴보면, 상반된 결과이지만, 구현이 조금 다르다.

dropWhile의 메소드 원형은 다음과 같다. 내부 메소드와 재귀 메소드를 적절히 사용했다.


@inline final override def dropWhile(p: A => Boolean): List[A] = {
@tailrec
def loop(xs: List[A]): List[A] =
if (xs.isEmpty || !p(xs.head)) xs
else loop(xs.tail)

loop(this)
}


dropWhile 예시는 다음과 같다. 

val list = List(0,1,2,3,4,5,-1,0)
println(list.dropWhile(_ < 4))
println(list.dropWhile(_ > -1))
println(list.dropWhile(_ > 3))

결과는 다음과 같다. 


List(4, 5, -1, 0)

List(-1, 0)

List(0, 1, 2, 3, 4, 5, -1, 0)




span 메소드는 takeWhile과 dropWhile 두 메소드를 하나도 합쳐 튜플로 리턴한다.


val list = List(0,1,2,3,4,5,-1,0)
println(list.span(_ < 4))

결과는 다음과 같다.


(List(0, 1, 2, 3),List(4, 5, -1, 0))



구현은 다음과 같다.

@inline final override def span(p: A => Boolean): (List[A], List[A]) = {
val b = new ListBuffer[A]
var these = this
while (!these.isEmpty && p(these.head)) {
b += these.head
these = these.tail
}
(b.toList, these)
}


리스트의 forall 메소드와 exists 메소드의 예시이다.

forall 메소드는 리스트의 모든 엘리먼트가 참일 때만 true를 리턴하고, exists 메소드는 하나라도 참이면 true를 리턴한다. 

val list = List(0,1,2,3,4,5,-1,0)
println(list.forall(_ > -100))
println(list.exists(_ == 0))


결과는 다음과 같다.


true

true




foldLeft와 foldRight는 우선 순위 연산을 지원하는 메소드이다.

val list = List(1,2,3)
println(list.foldLeft(1)(_ * _))
// ((1 * 1) * 2) * 3 = 6
println(list.foldLeft(2)(_ * _))
// ((2 * 1) * 2) * 3 = 12

println(list.foldRight(1)(_ * _))
// 1 * (2 * (3 * 1)) = 6
println(list.foldRight(2)(_ * _))
// 1 * (2 * (3 * 2)) = 12

결과는 다음과 같다.


6

12

6

12



리스트의 sortWith 메소드는 소팅을 지원한다. 다음 예시는 오른차순, 내림차순을 출력한다.

val list = List(1,2,3,-1,0)
println(list.sortWith(_ < _))
println(list.sortWith(_ > _))

결과는 다음과 같다.


List(-1, 0, 1, 2, 3)

List(3, 2, 1, 0, -1)




리스트의 팩토리 메소드이다.

http://www.scala-lang.org/docu/files/collections-api/collections_45.html




List.empty 예시이다.

println(List.empty)

결과는 List()이다.




List.range 예시이다.

println(List.range(1,5))
println(List.range(1,5,1))
println(List.range(1,6,2))


결과는 다음과 같다. 매개변수는 (start, end, step)을 의미한다. 

List(1, 2, 3, 4)

List(1, 2, 3, 4)

List(1, 3, 5)




List.concat 예시이다.

println(List.concat(List('s'), List('a'), List('m')))

결과는 다음과 같다.




List.fill 예시이다.

println(List.fill(2)("samuel"))

예시는 다음과 같다.



List.tabulate 예시이다.



println(List.tabulate(2)(a => a + 1))


결과는 다음과 같다. 


List(1, 2)




List.tabulate 메소드로 List(samuel, samuel)를 출력할 수도 있다. 


println(List.tabulate(2)(_ => "samuel"))






Posted by '김용환'
,