head :: tailhead는 제일 앞에 요소 하나를 빼옵니다.
val fruits = List("Apple", "Banana", "Cherry", "Durian", "Lemon", "Pear")
val head = fruits.head //> "Apple"
대응 되는 메소드는 tail입니다.
val tail = fruits.tail //> List("Banana", "Cherry", "Durian", "Lemon", "Pear")
그래서 이렇게 표현됩니다.
fruits == head :: tail //> true
주의 할 점으로head는 String 타입, tail은 List[String] 타입이라는 것입니다.
요소가 두 개여도 마찬가지입니다.
val a = List("Red", "Blue")
a.head // "Red": String
a.tail // List("Blue"): List[String]
그래서 다음과 같죠.
a.head :: a.tail == "Red" :: List("Blue") //> true
그럼 리스트의 요소가 하나 있을 때 tail은 무슨 값을 가질까요?
val sol = List("A")
sol.tail //> List()
빈 리스트, 즉 Nil입니다.
실수하기 쉬운 부분으로, ::은 요소와 리스트를 연결한다는 것이죠. 그래서, 리스트와 리스트를 ::으로 연결하면
List(1, 2, 3) :: List(4, 5, 6) //> List(List(1, 2, 3), 4, 5, 6)
리스트 안에 리스트가 포함된 형태로 연결됩니다.
참고로, 리스트의 요소를 연결하는 메소드는 :::입니다.
List(1, 2, 3) ::: List(4, 5, 6) //> List(1, 2, 3, 4, 4, 5)
head, tail은 재귀적인 처리를 할 때 자주 사용됩니다. Coursera의 Functional Programming Principles in Scala의 강의를 들으시면, 아주 자주 보게 될 내용입니다.
init :+ lasthead, tail과 비슷하게 init, last가 있습니다.
val ns = List(1, 2, 3)
ns.init //> List(1, 2)
ns.last //> 3
List(1, 2) :+ 3 == ns //> true
:+연산자를 주의하세요. 아! 사실 :+도 함수입니다, :: 도 함수이구요 ;-)
(head, tail) 과 (init, last)는 거울처럼 대칭 되는 메소드입니다. 리스트의 앞에서 부터 재귀처리등을 하는 경우는 head :: tail이 사용되고, 리스트 끝에서 부터 뭔가 할때에는init :+ last를 활용합니다.
메소드는 주입식으로 외우는게 장땡인것 같습니다. 물론, 기본적으로 어떻게 돌아가는지 아는 상태에서요. List에 대해서 더 알아 보고 싶은데, 그 전에Tuple을 먼저 봐야 할 것 같네요.