Skip to content
LoveAg edited this page Jun 7, 2017 · 4 revisions

11장. 스칼라의 계층구조

  • 스칼라의 모든 클래스는 공통의 슈퍼클래스 Any를 상속하며 Any에 정의한 Null과 Nothing은 모든 다른 클래스의 서브 클래스이다.

11.1 스칼라의 클래스 계층구조

  • 모든 객체에서 사용 가능하며 ==, !=는 오버라이드 할 수 없다.

    final def == (that: Any): Boolean
    final def !=(that: Any): Boolean
    def equals(that: Any): Boolean
    def ##: Int
    def hashCode: Int
    def toString: String
  • AnyVal

    • 값 클래스의 부모 클래스. Byte, Short, Char, Int, Long, Float, Double, Boolean, Unit 값 클래스들은 new를 사용해 인스턴스화 할 수 없다.
    • 값 클래스는 서로 상속 관계가 없어서 암시적 변환을 제공한다. (예. min, max등 RichInt에 있는 메소드를 Int값에 호출하면 암시적 변환 적용)
  • AnyRef

    • 참조 클래스의 부모 클래스. String, List, Seq 등 스칼라 객체는 ScalaObject라는 스칼라 객체 표시를 위한 트레이트를 상속한다.

11.2 여러 기본 클래스를 어떻게 구현했는가?

11.3 바닥에 있는 타입

  • Null 클래스 : null 참조의 타입으로 모든 참조 타입의 서브클래스이며 값 타입과는 호환성이 없다.
  • Nothing 타입 : 모든 타입의 서브타입으로 값이 존재하지 않는다.(예 error 메소드의 반환 타입)



12장. 트레이트

  • 코드 재사용의 근간을 이루는 단위로 캡슐화하면 트레이트를 조합한 클래스에서 메소드나 필드를 재사용할 수 있고, 하나의 부모 클래스만 갖는 상속과는 달리 몇 개라도 조합해서 믹스인 할 수 있다.

12.1 트레이트의 동작 원리

  • 트레이트 정의는 trait 키워드를 사용한다는 점을 제외하면 클래스 정의와 같다.

class Animal
trait HasLegs 

trait Philosophical {
    def philosophize() {
        println("I consume memory, therefor I am!")
    }
}

// extends나 with 키워드를 사용해 '믹스인'을 한다.
class Frog extends Philosophical {
    override def toString = "green"
}

// with를 이용한 믹스인
class Frog extends Animal with Philosophical {
    override def toString = "green"
}

// 여러 트레이트의 믹스인
class Frog extends Animal with Philosophical With HasLegs {
    override def toString = "green"
}

object HelloWorld {
    def main(args: Array[String]) {
       val frog = new Frog
       frog.philosophize()
       
       val phil: Philosophical = frog    // phil 변수를 Philosophical 트레이트를 믹스인한 어떤 객체로도 초기화 할 수 있다.
       phil.philosophize()
    }
}

[결과]
I consume memory, therefor I am!
I consume memory, therefor I am!

class Frog2 extends Animal with Philosophical with HasLegs {
    override def toString = "Pink"
    override def philosophize() {
        println("It ain't easy being " + toString + "!")
    }
}

object HelloWorld {
    def main(args: Array[String]) {
       val phrog: Philosophical = new Frog2
       phrog.philosophize()
    }
}

[결과]
It ain't easy being Pink!
  • 트레이트와 클래스의 차이
    • 파라미터를 가질 수 없다. 생성자에 전달할 파라미터를 트레이트 정의에 넣을 수 없다.
    • super호출을 동적으로 바인딩한다. 호출한 메소드 구현은 트레이트를 클래스 구현에 믹스인 할 때 마다 새로 정해진다. 그래서 stackable modification을 가능하게 만든다.

12.2 간결한 인터페이스와 풍부한 인터페이스

  • 간결한 인터페이스를 풍부한 인터페이스로 만들 때 사용할 수 있다.

12.4 Ordered 트레이트

  • 하나의 비교 연산자만 작성하면 모든 비교 연산자 구현을 대신할 수 있다.
    • Ordered 트레이트를 믹스인한다. (Ordered는 타입 파라미터를 명시해야 한다.)
    • 두 객체를 비교하는 compare 메소드를 정의한다.

class Rational(n: Int, d: Int) extends Ordered[Rational] {
  private val g = gcd(n.abs, d.abs)
  val numer = n / g
  val denom = d / g
  def this(n: Int) = this(n, 1)
  
  private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a%b)
  
  def compare(that: Rational) = (this.numer * that.denom) - (that.numer * this.denom)
}

val half = new Rational(1, 2)
val third = new Rational(1, 3)
println("half <  third : ", half < third)
println("half >  third : ", half > third)

[결과]
(half <  third : ,false)
(half >  third : ,true) 

12.5 트레이트를 이용해 변경 쌓아올리기


abstract class IntQueue {
  def get() : Int
  def put(x: Int)
}

import scala.collection.mutable.ArrayBuffer
class BasicIntQueue extends IntQueue {
    private val buf = new ArrayBuffer[Int]
    def get() = buf.remove(0)     
    def put(x: Int) { 
      println("BasicIntQueue Put") 
      buf += x }
}

trait Doubling extends IntQueue {
    abstract override def put(x: Int) { 
      println("Doubling Put")
      super.put( 2 * x) }
}

class MyQueue extends BasicIntQueue with Doubling

val queue = new MyQueue
queue.put(10)
println("get : " + queue.get())
       
val queue2 = new BasicIntQueue with Doubling
queue2.put(10)
println("get : " + queue2.get())

[결과]
Doubling Put
BasicIntQueue Put
get : 20
Doubling Put
BasicIntQueue Put
get : 20

12.6 왜 다중 상속은 안 되는가?

  • 트레이트를 사용할 때는 특정 클래스에 믹스인한 클래스와 트레이트를 선형화해서 어떤 메소드를 호출할지 결정한다.



13장. 패키지와 임포트

13.1 패키지 안에 코드 작성하기


// Navigator 계층은 세가지 방식 모두 동일함 bobsrockets.navigation 패키지 안쪽
package bobsrockets.navigation
class Navigator

package bobsrockets.navigation {
    class Navigator
}

package bobsrockets {
    package navigation {
        class Navigator
        package tests {
            class NavigatorSuite  // bobsrockets.navigation.tests 패키지 안쪽
        }
    }
}

13.2 관련 코드에 간결하게 접근하기

  • 어떤 클래스가 속한 패키지 안에서는 아무 접두사가 없어도 해당 클래스에 접근 가능
  • 패키지를 포함하는 부모 패키지는 그 자식 패키지에 어떤 접두어를 붙이지 않고 접근 가능
  • 중괄호 패키징 문법을 사용하면 그 패키징 스코프 밖에서 접근 가능한 모든 이름을 그 패키징 안에서도 쓸 수 있다.
  • root 패키지 : 사용자가 작성한 모든 패키지 외부에 존재, 모든 최상위 패키지를 _root_패키지의 멤버로 취급

13.3 임포트

import 절을 통해 패키지와 그 멤버를 불러올 수 있다.


자바와 유사한 방식
* 자바의 싱글 타입 임포트           
    import bobsdelights.Fruit
* 주문식 임포트, 자바 별표와 동일   
    import bobsdelifhts._
* 정적 클래스 필드를 불러오는 자바 임포트에 해당
    import bobsdelights.Friuits._

자바와 다른 특징
* 어느 곳에서나 나타날 수 있다. 
* 패키지 뿐만 아니라 (싱글톤 또는 일반)객체도 참조할 수 있다.
* 임포트 셀렉터를 사용하여 불러온 멤버 이름 중 일부를 숨기거나 다른 이름을 지정할 수 있다.

import Fruits.{Apple => McIntosh, Orange}   // Apple을 McIntosh로 지정하고, Orange를 불러옴
import Fruits.{Pear => _, _}                // Pear를 숨기고, 다른 모든 멤버를 불러옴

13.4 암시적 임포트

  • 모든 프로그램에 항상 임포트 하는 것

import java.lang._
import scala._
import Predef._

13.5 접근 수식자

  • 비공개 멤버
    • private가 앞에 붙은 멤버는 오직 그 정의를 포함한 클래스나 객체 내부에서만 접근 가능
    • 클래스 외부에서 접근 시 java와 다르게 오류가 발생
  • 보호 멤버
    • protected가 앞에 붙은 멤버는 서브클래스에서만 접근 가능 (그 클래스와 같은 패키지 안에 있는 다른 클래스 접근 불가)
  • 공개 멤버
    • 어디서나 접근 가능
  • 보호 스코프
    • 접근 수식자의 의미를 지정자로 확장 가능하며 패키지나 클래스 또는 싱글톤 객체를 세밀하게 접근 제어 가능
    • private[X], protected[X]
  • 가시성과 동반객체
    • 동반 객체와 클래스는 비공개 또는 보호 접근에 대해 동일한 권리를 가진다.

13.6 패키지 객체

  • object 키워드를 패키지 이름 앞에 선언하며 패키지 객체 내부에 있는 모든 정의를 패키지 자체에 속한 멤버로 취급
  • 패키지 객체 내에 선언된 메소드는 패키지에 속한 클래스를 임포트 하는 것처럼 임포트 가능
  • 패키지 객체는 package.class라는 이름의 클래스 파일로 컴파일되며 파일은 패키지 클래스와 대응하는 디렉토리에 들어간다.

14장. 단언문과 단위 테스트

14.1 단언문

  • assert 메소드를 호출하는 방식으로 단언문을 작성하며 조건을 만족하지 않는 경우 AssertionError를 던진다.
  • assert(c, e) 두 개 인자로 사용되는 경우 : 조건 c를 만족하지 않을 경우 설명 e를 포함하는 AssertionError를 던진다. : e는 Any 타입으로 AssertionError객체에 e.toString을 호출한다.
  • ensuring 메소드는 술어 함수를 인자로 받고 true, false로 반환한다.

14.2 단위 테스트

  • 자바도구 JUnit, TestNG
  • 스칼라로 만들어준 ScalaTest, specs, ScalaCheck 등도 있다
    • org.scalatest.Suite 확장 클래스로 선언하여 test로 시작하는 메소드를 정의하고 execute()를 호출하면 스위트 안에 있는 테스트 메소드를 찾아서 실행
    • org.scalatest.FunSuite 확장 클래스로 선언하여 test 메소드를 정의하고 그 안에 코드 기입

14.3 실패 보고 시 더 많은 정보 제공하기

  • === 연산자, expect 메소드를 사용하여 메시지 나타내기
  • ScalaTest의 intercept 메소드 사용하여 예외 발생시키기

14.4 JUnit과 TestNG 사용

14.5 명세를 테스트로 사용하기

  • 동작 주도 개발 테스트 스타일 ScalaTest에서 Spec, WordSpec, FlatSpec, FeatureSepc 등을 제공

14.6 프로퍼티 기반 테스트



15장. 케이스 클래스와 패턴 매치

15.1 간단한 예

  • 케이스 클래스 : 클래스 선언에서 case 수식자를 넣음
    1. 컴파일러는 클래스 이름과 같은 이름의 팩토리 메소드를 추가한다. new 없이 객체를 생성할 수 있다.

    2. 파라미터 목록에 있는 모든 클래스 파라미터에 암시적으로 val 접두사를 붙인다. 각 파라미터가 클래스의 필드도 된다.
    3. toString, hashCode, equals 메소드의 자연스러운 구현을 추가한다.
    4. 컴파일러는 케이스 클래스에서 일부를 변경한 복사본을 생성하는 copy 메소드를 추가한다.

abstract class Expr
case class Var(name: String) extends Expr
case class Number(num:  Double) extends Expr
case class UnOp(operator: String, arg: Expr) extends Expr
case class BinOp(operator: String, left: Expr, right: Expr) extends Expr

object HelloWorld {
    def main(args: Array[String]) {
        val v = Var("x")
        val op = BinOp("+", Number(1), v)
        
        println("v.name : " + v.name)
        println("op.left : " + op.left)
        
        println("op : " + op)
        println("op.right == Var(x) : " + (op.right == Var("x")))
        
        val op2 = op.copy(operator = "-")
        println("op.copy(operator = -) : " + op2)     
        println("op == op2 : " + (op == op2))
    }
}

[결과]
v.name : x
op.left : Number(1.0)
op : BinOp(+,Number(1.0),Var(x))
op.right == Var(x) : true
op.copy(operator = -) : BinOp(-,Number(1.0),Var(x))
op == op2 : false

  • 패턴 매치

    • 셀렉터 match { 대안들 }
    • 패턴 매치에는 case 키워드로 시작하는 대안들이 들어가고 각 대안은 패턴과 => 심볼로 분리되는 패턴과 일치했을 때 계산할 표현식으로 구성된다.
  • switch와 match의 비교
    1. 스칼라의 match는 표현식이고 결과 값을 내놓는다.
    2. 스칼라의 case는 다음 case로 빠지지 않는다. (break 불필요)
    3. 매치에 성공하지 않는 경우 MatchError 예외가 발생하므로 디폴트 케이스를 반드시 추가해야 한다.


val t = "a"
t match {
    case "b" => println("b")
}
[결과]
Exception in thread "main" scala.MatchError: a (of class java.lang.String)

15.2 패턴의 종류

  • 와일드카드 패턴 : 어떤 객체라도 매치할 수 있고 어떤 객체에서 값을 알 필요가 없는 부분을 무시하기 위해 사용한다.

def matchTest(arg: Expr){
    arg match{
        case BinOp(_, _, _) => println(arg + " is a binary operation")
        case _ => println("It's somethisng else")
    }
}

matchTest(op)
matchTest(v)

[결과]
BinOp(+,Number(1.0),Var(x)) is a binary operation
It's somethisng else

  • 상수 패턴 : 자신과 똑같은 값과 매치되며 어떤 종류의 리터럴이든 상수로 사용할 수 있다.

  • 변수 패턴 : 와일드카드처럼 어떤 객체와도 매치되나 변수 객체를 바인딩한다.

  • 변수 또는 상수?

    • 소문자로 시작하는 이름은 패턴 변수로 취급하고 다른 모든 참조는 상수로 간주한다.
    • 디폴트 케이스를 추가 시 컴파일 에러 발생(Warning 발생하는 듯)
    • 소문자 이름을 상수 패턴으로 사용하고 싶다면 긴 이름을 사용하거나 역따옴표를 사용해 변수 이름을 감싸면 된다
  • 생성자 패턴 : 어떤 값이 해당 케이스 클래스에 속하는지 검사한 다음 그 객체의 생성자가 인자로 전달받은 값들이 괄호 안의 패턴과 정확히 매치될 수 있는지 검사하는 것으로 스칼라 패턴이 깊은 매치를 지원한다는 뜻이다.


def deepMatchTest(expr: Expr){
      expr match {
        case BinOp("+", Number(1), v) => println("a deep match")
        case _ => println("no match")
      }
}

val opA = BinOp("+", Number(1), v)
val opB = opA.copy(operator = "-")
val opC = BinOp("+", Number(0), opA)
deepMatchTest(opA)
deepMatchTest(opB)
deepMatchTest(opC)

[결과]
a deep match
no match
no match

  • 시퀀스 패턴 : 배열이나 리스트 같은 시퀀스 타입에 대해서도 매치시킬 수 있으며 패턴 내부에 원하는 개수만큼 원소를 명시하고 길이를 한정하지 않고 매치할 수 있다.

expr match{
    case List(0, _, _) => println("found it")
    case List(0, _*) => println("found it")
    case _ =>
}
  • 튜플 패턴 : 튜플도 매치 가능 (case(a, b, c) => println("matched" + a + b + c))
  • 타입 지정 패턴 : 타입 검사나 타입 변환을 대신하기 위해 사용되며 특히 타입 검사와 변환을 동시에 사용해야 한다면 유용하다.

def fun(x: Any) = x match {
    case s: String => s.length
    case m: Map[_, _] => m.size
    case _ => -1
}
위와 같은 동작을 isInstanceOf[typeVal] 와 asInstanceOf[typeVal] 호출할 경우 코드가 더 길어지고 복잡해짐
  • 스칼라는 자바와 마찬가지로 제네릭에서 타입 소거 모델을 사용하기 때문에 match를 통해 원소 타입 확인이 어렵지만 배열은 원소 타입과 값을 함께 저장하므로 가능하다.
  • 변수 바인딩 : @ 기호를 넣고 패턴을 쓰면 일반적인 매치를 시도하고 그 패턴 매치에 성공하면 변수가 하나 있는 패턴에서처럼 매치된 객체를 변수에 저장한다.

15.3 패턴 가드

패턴 뒤에 오고 if로 시작하며 패턴 안에 있는 변수를 참조하는 어떤 불리언 표현식도 가드로 사용할 수 있다. 가드가 있으면 가드가 true가 될 때만 매치에 성공한다.


case n: Int if 0 < n => ...  // 양의 정수만 매치
case s: String if s == 'a' => ... // 'a'문자로 시작하는 문자열만 매치

15.4 패턴 겹침

case문의 순서가 중요하며 모든 경우를 처리하는 case문의 위치가 상위에 있을 경우 컴파일러가 경고 표시를 한다. (error : unreachable code)

15.5 봉인한 클래스

  • 최상위 슈퍼 클래스 앞에 sealed 키워드를 선언하여 그 클래스와 같은 파일이 아닌 다른 곳에서는 새로운 서브 클래스를 만들 수 없도록 한다.
  • 봉인한 클래스는 패턴 매치에서 유용하며 컴파일러가 경고 메시지와 함께 놓친 패턴 조합을 환기시켜준다.
  • 매치의 셀렉터에 @unchecked 애노테이션을 추가하면 컴파일러가 case문이 모든 패턴을 다 다루는지 검사하는 것을 생략하여 놓친 패턴에 대한 경고 메시지를 안 볼 수 있다.

15.6 Option 타입

  • Option이라는 표준 타입은 선택적인 값을 표현하며 값이 있을 경우 Some(x), 없으면 None이라는 객체가 된다.
  • 스칼라 컬렉션의 몇몇 표준 연산은 선택적인 값을 생성하고 이 옵션 값을 분리하는 일반적인 방법은 패턴 매치이다.

val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo")
println("capitals get France : " + (capitals get "France"))         // capitals get France : Some(Paris)
println("capitals get North Pole : " + (capitals get "North Pole")) // capitals get North Pole : None

def show(x: Option[String]): String = {
    x match {
        case Some(s) => s
        case None => "????"
    }
}
println("capitals get France : " + show(capitals get "France"))         // capitals get France : **Paris**
println("capitals get North Pole : " + show(capitals get "North Pole")) // capitals get North Pole : **????**
  • null이 반환되지 않기 때문에 변수를 사용할때 마다 null 검사를 하지 않아도 되고 NullPointerException을 발생하지도 않는다. option 사용으로 인해 null이 될 수 있는 변수를 사용하는 프로그램에서 스칼라는 타입 오류를 발생한다. 어떤 변수가 Option[String] 타입이라면, 그 변수를 String으로 사용하려 하는 스칼라 프로그램은 컴파일을 할 수 없다.

val optionSize = (capitals get "France").length()  // **Compile Error : value length is not a member of Option[String]**
val noralsize = (show(capitals get "France")).length()

15.7 패턴은 어디에나

  • 변수 정의에서 패턴 사용하기 : val이나 var를 정의할 때, 패턴을 사용. 케이스 클래스와 같이 사용하며 패턴을 사용해 값일 해체할 수 있다.

val exp = new BinOp("%", Number(5), Number(1))
val BinOp(opr, left, right) = exp
println("opr : " + opr + ", left : " + left + ", right : " + right)



16장. 리스트

16.1 리스트 리터럴

  • 배열과 비슷하나 변경이 불가능하고 재귀적이다. (예 : linked list)

16.2 리스트 타입

  • 리스트 타입은 공변적이다.
  • 빈 리스트의 타입이 List[Nothing]이며 Noting은 클래스 계층구조의 맨 아래 타입이므로 List()는 List[String] 타입이기도 하다.

16.3 리스트 생성

  • Nil은 빈리스트를 의미하고 ::는 리스트의 앞에 원소를 추가한다.

val nums: List[Int] = List(1, 2, 3, 4)
val nums = 1 :: 2 :: 3 :: 4 :: Nil

16.4 리스트 기본 연산

  • head : 리스트의 첫번째 원소
  • tail : 리스트의 첫번째 원소를 제외한 나머지 원소로 이뤄진 리스트
  • isEmpty : 리스트가 비어 있다면 true를 반환

16.5 리스트 패턴

패턴 매치를 사용해 각 부분으로 나눌 수 있다.


val fruit: List[String] = List("apple", "orange", "pear", "melon", "grape")
val a :: b :: rest = fruit
println("a : " + a)
println("b : " + b)
println("rest : " + rest)

[결과]
a : apple
b : orange
rest : List(pear, melon, grape)

16.6 List 클래스의 1차 메소드

  • 두 리스트 연결하기 : ":::" 두 인자를 리스트로 받아 연결한다.
  • 분할 정복 원칙 : ??
  • 리스트 길이 구하기: length - 리스트를 순회하여 검사하기 때문에 원소 개수만큼 시간이 걸린다.
    1. 리스트 양 끝에 접근하기: init, last (head 및 tail과 달리 계산하기 위해 전체 리스트를 순회한다.)
      init : 마지막 원소를 제외한 모든 원소를 포함한 리스트를 반환
      last : 마지막 원소를 반환
    2. 리스트 뒤집기: reverse 리스트의 뒤집어 새로운 리스트를 생성한다.
    3. 접두사와 접미사: drop, take, splitAt
      drop : 첫번째에서 n번째까지 원소를 제외한 리스트 원소들 반환
      take : n 번째까지 원소를 반환
      splitAt : n번째 인덱스 위치에서 리스트를 분할하여 반환
    4. 원소 선택: apply와 indices
      apply : n 번째 인덱스 원소를 반환
      indices : 유효한 모든 인덱스의 리스트를 반환
    5. 리스트의 리스트를 한 리스트로 반듯하게 만들기: flatten
      flatten : 리스트의 리스트를 인자로 받아서 하나의 리스트로 반듯하게 펼친다.
    6. 두 리스트를 순서쌍으로 묶기: zip과 unzip
      zip : 두 리스트를 인자로 받아서 순서쌍의 리스트를 만들면 길이가 다르면 긴 쪽의 남는 원소를 버린다.
      zipWithIndex : 리스트의 모든 원소와 그 위치를 묶는다.
      unzip : 튜플의 리스트를 리스트릐 튜플로 바꾼다.
    7. 리스트 출력하기: toString, mkString
      toString : 리스트에 대한 표준 문자열 표현을 반환
      mkString : 접두사 문자열, 분리 문자열, 접미사 문자열을 받아 문자열로 반환
      addString : mkString과 유사하나 StringBuilder객체에 추가
    8. 리스트 변환하기: iterator, toArray, copyToArray
      iterator : 리스트 원소를 이터레이터를 사용해서 접근
      toArray, toList : List <-> Array 변환
      copyToArray : 리스트 원소를 배열의 특정 지점부터 연속적으로 복사

16.7 List 클래스의 고차 메소드

  1. 리스트 매핑: map, flatMap, foreach
    map : 리스트를 함수에 적용하여 그 결과 값으로 이뤄진 리스트를 반환
    flatMap : map과 유사하나 결과 리스트를 서로 연결한 하나의 리스트로 반환
    foreach : 오른쪽 피연산자로 프로시저를 받아 적용
  2. 리스트 걸러내기:filter, partition, find, takeWhile, dropWhile, span
    filter : Boolean 타입의 술어 함수를 받아서 true인 원소의 리스트를 반환
    partition : filter와 같이 술어 함수를 받아서 true, false 각 리스트로 반환
    find : filter와 유사하나 술어 함수에 만족하는 첫 번째 원소만 반환
    takeWhile : 술어 함수를 만족하는 가장 긴 접두사를 반환
    dropWhile : 술어 함수를 만족하는 가장 긴 접두사를 제거
    span : takeWhile, dropWhile을 하나로 묶은 것
  3. 리스트 전체에 대한 술어: forall과 exists
    forall : 모든 원소가 술어 함수를 만족할 때 true를 반환
    exists : 술어 함수를 만족하는 원소가 하나라도 존재하면 true를 반환
  4. 리스트 폴드: /:, :<br/> fold연산 : 왼쪽폴드 /: 오른쪽 폴드 :\ 전달받는 연산자를 각 방향으로 부터 연속 적용
  5. 리스트 정렬: sortWith
    sortWith : List sortWith before 형식으로 before 자리에 정의된 비교함수대로 정렬 (예 : ListX sortWith (_ < _))

16.8 List 객체의 메소드

  1. 원소로부터 리스트 만들기: List.apply
    List.apply : List.apply(1,2,3) == List(1, 2, 3)
  2. 수의 범위를 리스트로 만들기: List.range
    List.range : 어떤 범위를 구성하는 수로 이루어진 리스트를 만든다. List.range(from, until, increment)
  3. 균일한 리스트 생성: List.fill
    List.fill : 생성할 리스트의 길이와 반복할 원소를 받아 리스트를 채움 (예 : List.fill(5)('a'), List.fill(2,3)('b'))
  4. 함수 도표화:List.tabulate
    List.tabulate : 제공된 함수로 계산한 원소의 리스트를 생성 (예 : List.tabulate(5)(n => n * n), List.tabulate(5,5)(*))
  5. 여러 리스트 연결하기: List.concat
    List.concat : 여러 리스트를 연결한다. (예 : list.concat(List(), List('a'), List('b'))

16.9 여러 리스트를 함께 처리하기

17장. 컬렉션

17.1 시퀀스

  • 리스트

    • 변경 불가능 연결 리스트로 앞 쪽에는 빠른 추가/삭제가 가능하지만 임의의 위치 접근 시에는 순차적으로 따라가기 때문에 느리다.
  • 배열

    • 임의의 위치에 있는 원소를 효율적으로 적급하게 해주며 기존 자바 메소드도 사용할 수 있다.
  • 리스트 버퍼

    • 변경 가능 객체로 +=, +=:를 사용하여 원소를 앞 뒤로 추가하고 잠재적인 스택 오버플로우를 피하기 위해 사용된다.
  • 배열 버퍼

    • 변경 가는 객체로 원소를 추가하거나 삭제할 수 있다는 점을 빼면 배열과 동일하다.
  • 문자열(StringOps를 통함)

    • Predef에 String을 StringOps로 바꾸는 암시적 변환이 있기 때문에 시쿼스처럼 문자열을 다룰 수 있다.

17.2 집합과 맵

  • Set이나 Map을 만들면 디폴트로 변경 불가능한 객체가 생기며 변경 가능한 객체를 원한다면 명시적으로 임포트 해야 한다.

  • import scala.collection.mutable

  • 집합의 사용 : 특정 객체가 최대 하나만 들어가도록 보장, 중복 배제

  • 맵의 사용 : 각 원소의 값에 대해 연관 관계를 만들고 키와 값을 갖는다.

  • 디폴트 집합과 맵

    • Set이나 map 팩토리 메소드는 내부적으로 해시테이블을 사용하는 HashSet, HashMap을 반환한다.
    • 특정 크기의 집합만을 담당하는 특별한 클래스를 사용하는데 원소가 5개 이상인 집합을 팩토리 메소드에 요구하면 해시를 사용하는 구현을 사용한다.
  • 정렬된 집합과 맵

    • 정해진 순서대로 원소를 반환하는 이터레이터를 제공하는 SortedSet, SortedMap 트레이트
    • 두 트레이트의 구현은 각각 TreeSet, TreeMap이고 순서는 Ordered 트레이트를 따라 결정

17.3 변경 가능 컬렉션과 변경 불가능 컬렉션 비교

  • 변경 불가능한 컬렉션이 프로그램을 추론하기 쉽고 사용하는 공간 크기도 작다.
  • 변경 불가능한 컬렉션을 가능한 컬렉션으로 바꾸거나 그 역으로 바꾸는 일을 쉽게 하기 위해 몇가지 문법적 편의를 제공한다.
  • 변경 가능한 컬렉션으로 바꾸고 싶다면 변경 가능한 컬렉션을 임포트하거나 val 대신 var를 사용한다.

17.4 컬렉션 초기화

Clone this wiki locally