[정리] Scala 정리(1) - 기초
스칼라 학교!을 개인적으로 정리한 것입니다.
인터프리터 시작하기
$ sbt console
값과 변수
val two = 1 + 1 // value, cannot be modified
var two = 1 + 1 // variable, can be modified
함수
/* functions */
def sum(x:Int, y:Int): Int = x + y
def print_hello() = println("Hello World")
/* lambda functions */
(x:Int) => x * x
이름 없는 함수를 다른 함수나 식에 넘기거나 val에 저장할 수도 있다.
부분적용(Partial application)
{ _ + 2 }
이라는 문맥에서 밑줄은 이름이 없는 매개변수를 가리킨다.
def adder(m: Int, n: Int) = m + n
// adder: (m: Int,n: Int)Int
val add2 = adder(2, _:Int)
// add2: (Int) => Int = <function1>
add2(3)
// res50: Int = 5
Curried Function
떄로 함수의 인자중 일부를 적용하고, 나머지는 나중에 적용하게 남겨두는 것이 더 쓸모있는 경우가 있다.
scala> def multiply(m: Int)(n: Int): Int = m * n
// multiply: (m: Int)(n: Int)Int
/* 물론 두 인자를 한꺼번에 적용할 수도 있다.*/
scala> multiply(2)(3)
// res0: Int = 6
언더스코어를 이용해본다.
scala> val timesTwo = multiply(2) _
// timesTwo: (Int) => Int = <function1>
scala> timesTwo(3)
// res1: Int = 6
scala> val multiply2 = multiply(_:Int)(_:Int)
scala> multiply2(3,5) // returns 15
가변길이 인자
동일한 타입의 매개변수가 반복되는 경우
// sum function
def sum(args: Float*): Float ={
args.reduce {
(m:Float, n:Float) => m+n
}
}
sum(1,2,3,4,5) // returns 15.0
클래스
/* Class Defining */
class Calculator {
val brand: String = "HP"
def sum(m:Int, n:Int):Int = m+n
}
/* instantiate */
val calc = new Calculator
/* call class method */
calc.sum(1,2)
/* access class variable */
calc.brand
Constructor
특별히 생성자 문법을 제공하지는 않으며 단순히 초기화 파라미터만 받는다. 인스턴스 메소드 외의 모든 부분이 생성자가 된다.
// (brand: String) is parameter of constructor
class Calculator(brand: String) {
// constructor
val color: String = if (brand == "TI"){
"blue"
} else if (brand=="HP"){
"black"
} else{
"white"
}
// instance method
def sum(m:Int, n:Int):Int = m+n
}
scala> val calc = new Calculator("HP")
// calc: Calculator = Calculator@1e64cc4d
scala> calc.color
// res0: String = black
위 예제 클래스에서 클래스가 인스턴스화되면 초기화 변수인 brand
에 따라서 color
의 value가 정해지는 것을 볼 수 있다.
함수 vs 메소드
아래 예시를 참고해보자 ```scala class C { var acc = 0 def minc = { acc += 1 } val finc = { () => acc += 1 } }
scala> val c = new C // c: C = C@1af1bd6
scala> c.minc // c.minc() 호출함
scala> c.finc // 함수 값(반환값이 아니라 함수 자체)을 반환함
res2: () => Unit =
### 상속
```scala
class ScientificCalculator(brand: String) extends Calculator(brand) {
def log(m: Double, base: Double) = math.log(m) / math.log(base)
}
오버라이딩
class EvenMoreScientificCalculator(brand: String) extends ScientificCalculator(brand) {
def log(m: Int): Double = log(m, math.exp(1))
}
abstract class
abstract class Shape{
def getArea():Int
}
class Circle(r: Int) extends Shape {
def getArea():Int = {r * r * 3}
}
val s = new Shape
// <console>:8: error: class Shape is abstract; cannot be instantiated
// val s = new Shape
^
val c = new Circle(2)
// c: Circle = Circle@65c0035b
Traits
트레잇(trait)은 다른 클래스가 확장(즉, 상속)하거나 섞어 넣을 수 있는(이를 믹스인Mix in 이라 한다) 필드와 동작의 모음이다.
abstract class Car{
val brand: String
}
trait Shiny{
val shineRefraction: Int
}
class BMW extends Car with Shiny{
val brand="BMW"
val shineRefration=12
}
Traits vs. Abstract Class
- 공통점: 이를 상속(또는 확장)하는 쪽에서 구현을 해야 한다.
- 차이점:
- 클래스의 다중상속은 안된다. 다중상속이 필요하면 트레잇을 사용해야 한다.
- 생성자의 매개변수가 필요한 경우 추상클래스를 사용해야 한다. 트레잇의 매개변수는 허용되지 않는다.
타입
제네릭 함수를 만들 떄는 제네릭을 만들 변수를 각괄호([], squared bracket)으로 감싼다.
trait Cache[K, V]{
def get(key: K): V
def put(key: K, value: V)
def delete(key: K)
}
def remove[K](key: K) // 메소드에도 타입매개변수를 추가할 수 있다.
apply 메소드
apply 메소드를 사용하면 클래스나 객체의 용도가 주로 하나만 있는 경우에 유용하다.
scala> class Foo {}
// defined class Foo
scala> object FooMaker {
| def apply() = new Foo
| }
// defined module FooMaker
scala> val newFoo = FooMaker()
// newFoo: Foo = Foo@5b83f762
scala> class Bar {
| def apply() = 0
| }
// defined class Bar
scala> val bar = new Bar
// bar: Bar = Bar@47711479
scala> bar()
// res8: Int = 0
apply를 정의하면 메소드를 호출하듯 객체 인스턴스를 호출할 수 있다. 객체 인스턴스를 호출하면 그 객체(클래스)에 정의된 apply()가 호출된다.
객체(object)
object키워드로 선언된 객체를 말한다. 클래스와 객체가 같은 이름을 가질 수도 있다. 클래스의 유일한 인스턴스를 넣기 위해 사용한다.
object Timer{
var count = 0
def currentCount(): Long = {
count += 1
count
}
}
scala> Timer.currentCount
// res6: Long = 1
함수는 객체이다.
또는 트레이트의 집합이다.
인자를 하나만 받는 함수 = Function1
의 인스턴스, 인자를 두 개 받는 함수 = Function2
의 인스턴스, …
각 트레잇은 apply()
메소드가 정의되어 있다. 그래서 실제로 함수를 호출하는 것은 Function*
의 인스턴스의 apply()
메소드를 호출하는 것과 같다.
scala> class AddOne extends Function1[Int, Int] {
| def apply(m: Int): Int = m + 1
| }
// defined class AddOne
scala> val plusOne = new AddOne()
// plusOne: AddOne = <function1>
scala> plusOne(1)
// res0: Int = 2
/** extends Function1[Int, Int]는 extends (Int => Int)라고
* 더 알아보기 쉽게 쓸 수 있다.
*/
class AddOne extends (Int => Int) {
def apply(m: Int): Int = m + 1
}
패키지
package com.twitter.example
이를 파일 맨 앞에 선언하면 해당 파일 내의 모든 것들이 패키지에 포함된다. 값이나 함수는 클래스나 객체 밖에 존재할 수 없다. 객체(자바의 static함수와 동일)를 이용해 정적 함수를 관리할 수 있다.
패턴매칭(스위치 문)
times match {
case 1 => "one"
case 2 => "two"
case _ => "some other number"
}
// using conditional statement
times match {
case i if i == 1 => "one"
case i if i == 2 => "two"
case _ => "some other number"
}
// withoug _ ==> runtime error
타입 매칭
타입으로도 경우를 나눌 수 있다.
def bigger(o: Any): Any = {
o match {
case i: Int if i < 0 => i - 1
case i: Int => i + 1
case d: Double if d < 0.0 => d - 0.1
case d: Double => d + 0.1
case text: String => text + "s"
}
}
클래스 멤버 따라 매칭
def calcType(calc: Calculator) = calc match {
case calc.brand == "HP" && calc.model == "20B" => "financial"
case calc.brand == "HP" && calc.model == "48G" => "scientific"
case calc.brand == "HP" && calc.model == "30B" => "business"
case _ => "unknown"
}
Case Class
case class Calculator(brand: String, model: String)
val hp20b = Calculator("HP", "20b")
val hp20B = Calculator("HP", "20b")
hp20b == hp20B // this operation is generated automatically
// toString 함수도 기본적으로 지원된다.
케이스 클래스로 패턴 매칭하기
val hp20b = Calculator("HP", "20B")
val hp30b = Calculator("HP", "30B")
def calcType(calc: Calculator) = calc match {
case Calculator("HP", "20B") => "financial"
case Calculator("HP", "48G") => "scientific"
case Calculator("HP", "30B") => "business"
case Calculator(ourBrand, ourModel) => "Calculator: %s %s is of unknown type".format(ourBrand, ourModel)
}
// alternative for last case
case Calculator(_, _) => "Calculator of unknown type"
case _ => "Calculator of unknown type"
예외
예외 처리시 try-catch-finally 문법에 패천 매칭 사용 가능하다.
try {
remoteCalculatorService.add(1, 2)
} catch {
case e: ServerIsDownException => log.error(e, "the remote calculator service is unavailble. should have kept your trustry HP.")
} finally {
remoteCalculatorService.close()
}
// another example
val result: Int = try {
remoteCalculatorService.add(1, 2)
} catch {
case e: ServerIsDownException => {
log.error(e, "the remote calculator service is unavailble. should have kept your trustry HP.")
0
}
} finally {
remoteCalculatorService.close()
}
댓글남기기