Icednut's Note

Scala 강의 요약

2017-04-19

목차

  1. Scala 환경 구축하기
  2. Scala 변수, 상수, 타입
  3. 함수와 메소드
    • Lab 1
  4. 제어구조
  5. 함수 정의
    • Lab 2
  6. Arrays, Maps and Tuples
    • Lab 3.1 Remove First Negative
    • Lab 3.2 Word Count
    • Lab 3.3 Grouping
    • Lab 3.4 Partitions and Zips
  7. Classes and Objects
    • Lab 4.1 Time
    • Lab 4.2 Uniform Access
    • Lab 4.3 Uniform Access (con’t)
    • Lab 4.4 Operators
  8. Packages, Inheritnce, Traits
  9. Functional Programming
    • 9.1 Function
    • 9.2 Closures
    • 9.3 Currying
  10. Pattern Matching
  11. Case Class

1. Scala 환경 구축

  • JDK 1.8 설치
  • SBT 설치
  • Scala 설치
  • IntelliJ 설치 (스칼라가 처음라면 Scala IDE for Eclise)

2. Scala 변수, 상수, 타입

  • 변수는 var
  • 상수는 val로 선언
  • Scala에서 데이터 타입의 종류
    • 자바와 비슷 Byte, Short, Int, Long, Float, Double, Char, String, Char, Boolean
    • 단 자바에서는 없는 Unit, Null 이라는 타입도 있음
lesson1.sc
1
2
3
4
object Lesson1 {
val answer = 8 * 5 + 2
var greeting: String = "Hello World"
}
lesson2.sc
1
2
3
4
5
6
7
8
9
10
11
12
object Lesson2 {
1.to(10) // 1 ~ 10까지 값을 갖고 있는 배열이 만들어짐
"Hello".intersect("World") // lo
"Hello" intersect "World"
1.+(10)
1 + 10
var i = 0
// i++ 이거는 안됨
i += 1
}

3. 함수와 메소드

  • 파라미터 없는 함수 호출은 다음과 같다.
    1
    "hello".distinct
  • 스트링에 아래와 같이 함수 이름 없이 호출하면 apply 함수가 실행 되는 것과 같음
    1
    2
    3
    4
    "Hello".(4)
    "Hello".apply(4)
    "Hello"(4)

Lab 1

lesson4.sc
1
2
3
4
5
6
7
object Lesson4 {
import scala.math._
sqrt(10)
1.to(10).map(sqrt(_))
6.*(7)
}

4. 제어구조

  • if 표현식은 값을 반환할 수 있다.
    1
    2
    3
    4
    5
    if (x > 0) 1 else -1
    if (x > 0) "positive" else -1 // Type is Any
    if (x > 0) 1 // Missing else... else일 때는 Unit을 반환
    if (x > 0) 1 else () // 위 수식과 이 수식은 같음
  • 변수나 상수 선언 시 아래와 같이 스코프 지정이 가능
    1
    2
    3
    4
    5
    6
    val distance = {
    import scala.math._
    val dx = x - x0
    val dy = y - y0
    sqrt(dx * dx + dy * dy)
    }
  • 기본적인 For Loop
    1
    2
    3
    for (i <- 1 to n)
    for (ch <- "Hello")
    for (i <- 1 to 3; j <- 1 to 3) print((10 * i + j) + " ") // multiple generator
  • For Loop Guard
    1
    2
    3
    for (ch <- "Hello") // 한 글자씩 Loop
    for (i <- 1 to 3; j <- 1 to 3 if i != j) print((10 * i + j) + " ")
    for (i <- 1 to 10) yield i % 3

5. 함수 정의

  • 함수 정의 시 함수 몸통 앞에 ‘=’를 붙이고 안붙이고 차이는 뭘까?
    • 반환값이 있을 때 ‘=’를 붙인다.
    • 반환값이 없을 때는 ‘=’를 붙이지 않는다.
1
2
3
4
5
6
7
8
9
def abs(x: Double) = if (x >= 0) x else -x
def fac(n: Int): Int = {
if (n <= 0) 1 else n * fac(n - 1)
}
def box(s: String) {
val border = "-" * s.length + "--\n"
println(border + "|" + s + "|\n" + border)
}
  • 아래와 같이 반환값이 있는 함수를 작성하고 싶은데 =를 안붙이면 컴파일 오류가 발생한다.

    1
    2
    3
    4
    5
    def fac(n: Int) {
    var r = 1
    for (i <- 1 to n) r = r * i
    r
    }
  • Named argument

    1
    2
    3
    def decorate(str: String, left: String = "[", right: String = "]") = left + str + right
    decorate("Hello", right = "]<<<") // [Hello]<<<
  • 다항 파라미터는 파라미터 데이터타입 뒤에 *을 붙여준다.

    1
    2
    3
    4
    5
    def sum(args: Int*) = {
    var result = 0
    for (arg <- args) result += arg
    result
    }
  • 위 메소드의 파라미터에 Seq[Int]를 다항 파라미터로 변환하여 넣고 싶을 경우 아래와 같이 한다.

    1
    2
    3
    val s = sum(1 to 5: _*)
    // 1 to 5의 반환값은 Seq[Int]
  • What is that ? is joker

Lab 2. 함수 정의 연습하기

계속…

6. Array, Maps and Tuples

  • Array.sum
  • ArrayBuffer.max
  • ArrayBuffer.sorted
  • Array.reverse
  • Array.mkString() // Array의 내용물을 스트링으로 출력 (예쁘게)
1
2
3
4
5
6
7
8
9
10
11
12
13
val c = Array(2,3,5,7,8,11) //> c : Array[Int] = Array(2, 3, 5, 7, 8, 11)
val result = for (elem <- c if elem % 2 != 0) yield 2 % elem
//> result : Array[Int] = Array(2, 2, 2, 2)
val mySong = ArrayBuffer("Mary", "had", "a", "little", "lamb")
//> mySong : scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer(Mary, h
//| ad, a, little, lamb)
mySong.max //> res11: String = little
mySong.sorted //> res12: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer(Mary, a, h
//| ad, lamb, little)
Array(1,7,4,5).sorted //> res13: Array[Int] = Array(1, 4, 5, 7)
Array(1,7,4,5).reverse //> res14: Array[Int] = Array(5, 4, 7, 1)
Array(1,2,3).mkString("[", ", ", "]") //> res15: String = [1, 2, 3]

6.1 Maps

  • Map.getOrElse 메소드는 key에 해당하는 value를 못찾을 경우 기본 값을 반환한다.
  • updating map을 하고 싶을 땐 그냥 Map(“Bob”) = 20
  • append와 remove는 연산자 메소드를 통해서 가능 (+=, -=)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
val scores = Map("Alice" -> 20, "Bob" -> 10, "Cindy" -> 8)
//> scores : scala.collection.immutable.Map[String,Int] = Map(Alice -> 20, Bob -
//| > 10, Cindy -> 8)
scores("Alice") = 291 // immutable map이라서 value 값을 바꿀 수 없음
val mscores = scala.collection.mutable.Map("Alice" -> 10)
//> mscores : scala.collection.mutable.Map[String,Int] = Map(Alice -> 10)
val bobsScore = scores("Bob") //> bobsScore : Int = 10
val bogsScore = scores("Bog") //> java.util.NoSuchElementException: key not found: Bog
//| at scala.collection.MapLike$class.default(MapLike.scala:228)
//| at scala.collection.AbstractMap.default(Map.scala:59)
//| at scala.collection.MapLike$class.apply(MapLike.scala:141)
//| at scala.collection.AbstractMap.apply(Map.scala:59)
//| at Lesson5$$anonfun$main$1.apply$mcV$sp(Lesson5.scala:6)
//| at org.scalaide.worksheet.runtime.library.WorksheetSupport$$anonfun$$exe
//| cute$1.apply$mcV$sp(WorksheetSupport.scala:76)
//| at org.scalaide.worksheet.runtime.library.WorksheetSupport$.redirected(W
//| orksheetSupport.scala:65)
//| at org.scalaide.worksheet.runtime.library.WorksheetSupport$.$execute(Wor
//| ksheetSupport.scala:75)
//| at Lesson5$.main(Lesson5.scala:1)
//| at Lesson5.main(Lesson5.scala)
val bogsDefaultScore = scores.getOrElse("Bog", 0)
//> bogsDefaultScore : Int = 0
val mbogScore = mscores.contains("Bog") //> mbogScore : Boolean = false
mscores("Alice") = 100
mscores //> res0: scala.collection.mutable.Map[String,Int] = Map(Alice -> 100)
val newScores2 = scores + ("Fred" -> 91) //> newScores2 : scala.collection.immutable.Map[String,Int] = Map(Alice -> 20,
//| Bob -> 10, Cindy -> 8, Fred -> 91)
val newScores3 = newScores2 - "Alice" //> newScores3 : scala.collection.immutable.Map[String,Int] = Map(Bob -> 10, Ci
//| ndy -> 8, Fred -> 91)
for ((k, v) <- scores) println(k + " has score " + v)
//> Alice has score 20
//| Bob has score 10
//| Cindy has score 8
for ((k, v) <- scores) yield (v, k) //> res1: scala.collection.immutable.Map[Int,String] = Map(20 -> Alice, 10 -> Bo
//| b, 8 -> Cindy)
scores.keySet //> res2: scala.collection.immutable.Set[String] = Set(Alice, Bob, Cindy)
scores.values //> res3: Iterable[Int] = MapLike(20, 10, 8)

6.2 Tuples

  • Map과 비슷하지만 서로다른 자료형의 데이터를 묶을 수 있음
    1
    2
    3
    4
    val myTuples = (1, 3.14, "Fred")
    val pie = myTuples._2
    val (_, second, third) = t

Lab 3.1 Remove First Negative

  • ArrayBuffer가 주어졌을 때 양수 값은 모두 출력 & 첫 번째 음수만 출력하고 나머지 음수들은 제거하는 함수 작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
val myArray = ArrayBuffer(3,6,7,3,-4,8,-3,-5,6,7,-2,9,-1)
def myRmNeg(targetArray: ArrayBuffer[Int]): ArrayBuffer[Int] = {
var index = 0
var limit = myArray.length
var isNegativeFound: Boolean = false
val newMyArray = ArrayBuffer[Int]()
while(index < limit) {
val num = targetArray(index)
if (num < 0) {
if (!isNegativeFound) {
isNegativeFound = true
newMyArray += num
}
}
else newMyArray += num
index += 1
}
newMyArray
} //> myRmNeg: (targetArray: scala.collection.mutable.ArrayBuffer[Int])scala.colle
//| ction.mutable.ArrayBuffer[Int]
def myRmNeg2(targetArray: ArrayBuffer[Int]): ArrayBuffer[Int] = {
val targetArraySize: Int = targetArray.length
// 음수를 가진 인덱스 번호만 끄집어 낸다.
var negativeIndexArray = for (index <- 0 until targetArraySize if targetArray(index) < 0) yield index
negativeIndexArray = negativeIndexArray.drop(1).reverse
val filterdArray = for (index <- 0 until targetArray.length if !negativeIndexArray.contains(index)) yield targetArray(index)
ArrayBuffer[Int]() ++= filterdArray
} //> myRmNeg2: (targetArray: scala.collection.mutable.ArrayBuffer[Int])scala.col
//| lection.mutable.ArrayBuffer[Int]

Lab 3.2 Word Count

  • 파일을 읽어서 특정 단어가 몇 번 나왔는지 세보는 코드 작성하기
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    def wordCount(filename: String): scala.collection.mutable.Map[String, Int] = {
    import scala.io.Source
    val count = scala.collection.mutable.Map[String, Int]()
    for (line <- Source.fromFile(filename).getLines) {
    for (word <- line.split(" ")) {
    count(word) = count.getOrElse(word, 0) + 1
    }
    }
    count
    } //> wordCount: (filename: String)scala.collection.mutable.Map[String,Int]
    def wordCountWithImmutableMap(filename: String): scala.collection.immutable.Map[String, Int] = {
    import scala.io.Source
    var countMap = scala.collection.immutable.Map[String, Int]()
    for (line <- Source.fromFile(filename).getLines) {
    for (word <- line.split(" ")) {
    val wordCount = countMap.getOrElse(word, 0) + 1
    countMap += (word -> wordCount)
    }
    }
    countMap
    } //> wordCountWithImmutableMap: (filename: String)scala.collection.immutable.Map[
    //| String,Int]
    val filename = "C:\\Users\\Zotac023\\Desktop\\SCALA\\alice.txt"
    //> filename : String = C:\Users\Zotac023\Desktop\SCALA\alice.txt
    val countMap = wordCount(filename) //> countMap : scala.collection.mutable.Map[String,Int] = Map(talk: -> 1, passi
    //| on, -> 2, Visit -> 1, etext92 -> 1, follow -> 1, OTHERWISE -> 1, provisions
    //| -> 1, machines, -> 1, sighing -> 2, is--"Oh, -> 1, sister -> 5, curled -> 2,
    //| digging -> 2, absurd, -> 1, mouths. -> 1, `When -> 3, rose-tree, -> 2, plea
    //| sant -> 1, can--' -> 1, flapper -> 1, dream. -> 1, morning, -> 2, requires -
    //| > 1, worm. -> 1, `Well! -> 2, lessons -> 4, Files -> 1, `Boots -> 1, `we ->
    //| 3, expense -> 1, Shakespeare, -> 1, officers, -> 1, Long -> 1, RED -> 1, shr
    //| ink -> 1, Caterpillar, -> 2, nasty, -> 1, intellectual -> 1, hard -> 8, [Ori
    //| ginally -> 1, welcome -> 1, off.' -> 1, RIGHT -> 1, Rabbit's -> 3, carry ->
    //| 1, opening -> 3, matter,' -> 1, (luckily -> 1, chimney!' -> 1, `As -> 3, jur
    //| or -> 1, [get -> 1, grand, -> 1, did!' -> 1, medium -> 3, asking! -> 1, gay
    //| -> 1, bawled -> 1, advance -> 2, `YOU'D -> 1, THAT -> 6, Free -> 1, thunders
    //| torm. -> 1, mustard-mine
    //| Output exceeds cutoff limit.
    val countMap2 = wordCountWithImmutableMap(filename)
    //> countMap2 : scala.collection.immutable.Map[String,Int] = Map(herself. -> 8,
    //| Hatter -> 24, sneezed -> 1, forgotten -> 6, of. -> 1, Rabbit-Hole -> 1, rat
    //| e -> 4, pepper -> 5, submitted -> 1, NOT!' -> 1, `Fifteenth,' -> 1, like!' -
    //| > 1, remarked -> 1, lost: -> 1, croquet.' -> 2, est -> 1, room!' -> 2, sighi
    //| ng. -> 1, Bill!' -> 1, prizes.' -> 1, (a -> 1, accident -> 1, Cat,' -> 1, be
    //| !' -> 1, camomile -> 1, ftp -> 2, bats, -> 1, conversations -> 1, Down, -> 2
    //| , `Why -> 7, way? -> 1, cakes,' -> 1, `Nonsense!' -> 1, used -> 14, eye -> 4
    //| , whisper.) -> 1, PUNITIVE -> 1, Owl -> 2, pleased. -> 1, up.' -> 2, then?'
    //| -> 1, `Keep -> 1, instance, -> 4, READ -> 1, `Dear, -> 1, $4 -> 1, IN -> 2,
    //| way?', -> 1, II -> 1, Still -> 1, conversion -> 1, Please -> 2, At -> 8, dee
    //| ply. -> 1, "Let -> 1, shelves -> 1, locked; -> 1, beautiful -> 8, mustard-mi
    //| ne -> 1, leaders, -> 1, `Don't -> 4, does. -> 1, timidly -> 2, altogether ->
    //| 1, writing -> 4, Duches
    //| Output exceeds cutoff limit.

Lab 3.3 Grouping

  • 문자열 배열에서 길이가 같은 문자열끼리 묶는 코드 작성하기
1
2
3
4
5
6
7
8
9
val words = Array("Mary", "had", "a", "little", "lamb", "its", "fleece", "was", "white", "as", "snow", "and", "everywhere", "that", "Mary", "went", "the", "lamb", "was", "sure", "to", "go")
//> words : Array[String] = Array(Mary, had, a, little, lamb, its, fleece, was,
//| white, as, snow, and, everywhere, that, Mary, went, the, lamb, was, sure, t
//| o, go)
words.groupBy(_.length()) //> res0: scala.collection.immutable.Map[Int,Array[String]] = Map(5 -> Array(whi
//| te), 10 -> Array(everywhere), 1 -> Array(a), 6 -> Array(little, fleece), 2 -
//| > Array(as, to, go), 3 -> Array(had, its, was, and, the, was), 4 -> Array(Ma
//| ry, lamb, snow, that, Mary, went, lamb, sure))

Lab 3.4 Partitions and Zips

  • Lab 3.1 문제를 Array의 partition 메소드를 사용해서 풀기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
"New York".partition(_.isUpper) //> res0: (String, String) = (NY,ew ork)
val symbols = Array("<", "-", ">") //> symbols : Array[String] = Array(<, -, >)
val counts = Array(2, 10, 2) //> counts : Array[Int] = Array(2, 10, 2)
val pairs = symbols.zip(counts) //> pairs : Array[(String, Int)] = Array((<,2), (-,10), (>,2))
import scala.collection.mutable.ArrayBuffer
val myArray = ArrayBuffer(3,6,7,3,-4,8,-3,-5,6,7,-2,9,-1)
//> myArray : scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(3, 6, 7,
//| 3, -4, 8, -3, -5, 6, 7, -2, 9, -1)
val (positiveArray, negativeArray) = myArray.partition(_ > 0)
//> positiveArray : scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(3,
//| 6, 7, 3, 8, 6, 7, 9)
//| negativeArray : scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(-4,
//| -3, -5, -2, -1)
val answer = positiveArray //> answer : scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(3, 6, 7, 3
//| , 8, 6, 7, 9)
answer += negativeArray(0) //> res1: Lesson9.answer.type = ArrayBuffer(3, 6, 7, 3, 8, 6, 7, 9, -4)

7. Class, Object, Companion Object

  • Class의 멤버 변수를 val로 선언하면 immutable, var로 선언하면 mutable
  • val or var로 했느냐에 따라 getter, setter를 쓸 수 있음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class myString(val jString: String) {
private var extraData = ""
override def toString = f"${jString}${extraData}"
}
object myString {
def apply(base: String, extras: String) = {
val s = new myString(base)
s.extraData = extras
s
}
def apply(base: String) = new myString(base)
}
println(myString("hello", "world")) //> helloworld
println(myString("Goodbye")) //> Goodbye
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Pizza(var crustSize: Int, var crustType: String) {
def this(crustSize: Int) {
this(crustSize, Pizza.DEFAULT_CRUST_TYPE)
}
def this(crustType: String) {
this(Pizza.DEFAULT_CRUST_SIZE, crustType)
}
def this() {
this(Pizza.DEFAULT_CRUST_SIZE, Pizza.DEFAULT_CRUST_TYPE)
}
override def toString = s"A $crustSize inch pizza with a $crustType crust"
}
object Pizza {
val DEFAULT_CRUST_SIZE = 12
val DEFAULT_CRUST_TYPE = "THIN"
}
val pz1 = new Pizza(14, "THICK") //> pz1 : Lesson10.Pizza = A 14 inch pizza with a THICK crust
val pz2 = new Pizza(16) //> pz2 : Lesson10.Pizza = A 16 inch pizza with a THIN crust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
object Accounts {
private var lastNumber = 0
def newUniqueNumber() = {
lastNumber += 1
lastNumber
}
}
Accounts //> res0: Lesson10.Accounts.type = Lesson10$$anonfun$main$1$Accounts$2$@68837a7
//| 7
Accounts.newUniqueNumber() //> res1: Int = 1
Accounts.newUniqueNumber() //> res2: Int = 2
Accounts.newUniqueNumber() //> res3: Int = 3
Accounts.newUniqueNumber() //> res4: Int = 4
Accounts.newUniqueNumber() //> res5: Int = 5
  • Scala App 만들기

    1
    2
    3
    object HelloWorld extends App {
    println("Hello, world!")
    }
  • Uniform Access Method

    • 클래스에 멤버변수로 선언했지만 해당 멤버 변수와 동일한 메소드를 선언했을 경우 해당 메소드가 호출됨
Uniform Access Method Example
1
2
3
4
5
6
7
class Point(...) {
private val r = ...
private val theta = ...
def x = r * cos(theta)
// 여기서 x를 유니폼 엑세스 메소드 라고 부르며, 이 메소드는 인자가 없다.
}
  • scala에서 object 키워드가 붙은 코드는 싱글톤 객체를 말하며, 멤버 메소드는 static method이다.
  • Companion Object란 class와 object의 이름이 같은 것을 말하며, 서로간에 private feature에 접근이 가능하다.
    • apply 메소드를 Companion Object에 선언하기도 한다.

Lab 4.1 Time

  • 시간을 나타내는 클래스 작성하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Time(val hours: Int = 0, var minutes: Int = 0) {
if (hours < 0 || hours >= 24 || minutes < 0 || minutes >= 60) {
throw new IllegalArgumentException("hour or minute format incorrect")
}
def before(other: Time): Boolean = {
val thisTimeMinutes = this.toMinutes()
val otherTimeMinutes = other.toMinutes()
thisTimeMinutes < otherTimeMinutes
}
override def toString = f"${hours}:${minutes}"
def toMinutes(): Int = this.hours * Time.ONE_HOUR_MINUTES + this.minutes
}
object Time {
val ONE_HOUR_MINUTES = 60
def apply(hours: Int, minutes: Int) = new Time(hours, minutes)
def apply(hours: Int) = new Time(hours)
def apply(hours: Unit, minutes: Int) = new Time(0, minutes)
}
println(Time(12))
println(Time(11, 24).before(Time(16, 11)))
val t1 = Time(12, 30)
t1.minutes = -100

Lab 4.2 Uniform Access

  • 시간 클래스의 멤버를 val로 선언한 뒤 해당 멤버 변수는 uniform access method로 접근하기 (class의 멤버 변수에 아무것도 안쓰면 자동으로 val로 선언됨)
  • 이렇게 하면 getter 메소드를 정의한 것과 비슷한 효과를 볼 수 있다.
  • 아래 코드와 같이 멤버 변수 uniform access method를 통해 값을 변경하는 것은 컴파일 오류가 발생한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Time2(h: Int, m: Int) {
private val minutesSinceMidnight = h * 60 + m
def hours = minutesSinceMidnight / 60
def minutes = minutesSinceMidnight % 60
}
object Time2 {
}
val t2 = new Time2(12, 45) //> t2 : Lesson12.Time2 = Lesson12$Time2@61e4705b
// t2.minutes = 100
t2.hours //> res0: Int = 12
t2.minutes //> res1: Int = 45

Lab 4.3 Uniform Access (con’t)

  • 위 문제에서 uniform access method를 통해서 setter 구현해보기
  • 아래 코드를 보면 메소드명 바로 뒤에 ‘_=’가 붙는 것을 볼 수가 있는데 이것은 ‘변수명 =’과 같은 효과를 발휘한다.
  • 또한 setter uniform access method를 구현하려면 앞에서 작성했던 getter에 해당하는 uniform access method가 먼저 선언되어 있어야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Time3(h: Int, m: Int) {
private var minutesSinceMidnight = h * 60 + m
def hours = minutesSinceMidnight / 60
def minutes = minutesSinceMidnight % 60
def hours_=(newHours: Int) {
if (newHours < 0 || newHours >= 24) {
throw new IllegalArgumentException("hours is invalid")
}
this.minutesSinceMidnight = newHours * 60 + this.minutes
}
def minutes_=(newMinutes: Int) {
if (newMinutes < 0 || newMinutes >= 60) {
throw new IllegalArgumentException("minutes is invalid")
}
this.minutesSinceMidnight = this.hours * 60 + newMinutes
}
}
val t3 = new Time3(12, 45)
t3.hours = 19
t3.minutes = 13
t3.hours
t3.minutes

Lab 4.4 Operators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
object Time4 {
def apply(hours: Int = 0, minutes: Int = 0) = new Time4(hours, minutes)
}
class Time4(h: Int = 0, m: Int = 0) {
private var minutesSinceMidnight = h * 60 + m
if (h < 0 || h > 23) throw new IllegalArgumentException("hours is invalid")
if (m < 0 || m > 60) throw new IllegalArgumentException("minutes is invalid")
def hours = this.minutesSinceMidnight / 60
def minutes = this.minutesSinceMidnight % 60
def hours_=(newHours: Int) {
if (newHours < 0 || newHours > 23) throw new IllegalArgumentException("hours is invalid")
else this.minutesSinceMidnight = newHours * 60 + this.minutes
}
def minutes_=(newMinutes: Int) {
if (newMinutes < 0 || newMinutes > 59) throw new IllegalArgumentException("minutes is invalid")
else this.minutesSinceMidnight = this.hours * 60 + newMinutes
}
def <(other: Time4): Boolean = this.minutesSinceMidnight < other.minutesSinceMidnight
override def toString() = f"${this.hours}:${this.minutes}"
}
val t4 = new Time4(12, 45) //> t4 : Lesson14.Time4 = 12:45
t4.hours = 17
t4.minutes = 23
t4 //> res0: Lesson14.Time4 = 17:23
t4.hours //> res1: Int = 17
t4.minutes //> res2: Int = 23
println(Time4(11, 24) < Time4(16, 11)) //> true

9. Functional Programming

9.1 Function

  • Functions as value
  • Anonymous Functions
  • Functions with Function Parameters
  • Functions that Produce Functions
  • Parameter Inference
  • Map, Filter, Reduce

9.2 Closures

  • 양자가 얽힌 것 처럼 함수도 얽힐 수 있다.
  • 클로저를 사용하면 함수가 필요한 정보를 어딘가에서 정의해서 받아 사용할 수 있다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package otherscope {
    class Foo {
    def exec(f: (String) => Unit, name: String) = {
    f(name)
    }
    }
    }
    object HelloWorld extends App {
    var hello = "Hello"
    def sayHello(name: String) = {
    println(s"$hello, $name")
    }
    val foo = new otherscope.Foo()
    foo.exec(sayHello, "Lee")
    hello = "안녕하세요"
    foo.exec(sayHello, "Lee")
    }
  • 위 코드를 보면 hello라는 변수가 다른 스코프에서 실행되더라도 hello 값을 참조하여 쓰고 있다. (런타임 시 클로저 변수가 참조됨)

9.3 Currying

  • 함수의 반환값이 함수일 때 해당 함수 호출의 호출을 바로 하는 것
    1
    2
    3
    4
    5
    def mul(x: Int, y: Int) = x * y //> mul: (x: Int, y: Int)Int
    def mulOneAtATime(x: Int) = (y: Int) => x * y //> mulOneAtATime: (x: Int)Int => Int
    //def mulOneAtATime(x: Int)(y: Int) = x * y
    mulOneAtATime(10)(20) //> res0: Int = 200

10. Pattern Matching

  • var.isInstanceOf[Type] 보단 패턴 매칭이 더 best!
  • 주의: case의 변수명을 안쓰면 companion object를 지정하는 것처럼 보일 수 있음
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    val ch = '-' //> ch : Char = -
    val sign = ch match {
    case '+' => 1
    case '-' => -1
    case _ => 0
    } //> sign : Int = -1
    val pair = (0, 1) //> pair : (Int, Int) = (0,1)
    val st = pair match {
    case (0, _) => "0 ..."
    case (y, 0) => y + "0"
    case _ => "neither is 0"
    } //> st : String = 0 ...
    val arr = Array(9, 1, 1) //> arr : Array[Int] = Array(9, 1, 1)
    val out = arr match {
    case Array(0) => "0"
    case Array(x, y) => x + " " + y
    case Array(0, _*) => "0 ..."
    case _ => "something else"
    } //> out : String = something else

11. Case Class

  • case class는 패턴매칭에 유용하게 쓰임
  • case class는 자동으로 companion object가 만들어지며, 멤버 변수들은 자동으로 val로 선언됨
Tags: scala