본문 바로가기
language/Scala

[Scala] 스칼라 배우기 3. 스칼라 기본 문법2(함수)

by 스파이디웹 2023. 5. 22.
728x90

함수(function)

  • def으로 선언
  • 함수를 선언할 때 리턴문과 리턴 타입은 생략이 가능하고, 매개변수의 파라미터 타입은 생략할 수 없음
  • 리턴값이 없는 함수를 선언할 때는 Unit을 이용
  • 함수의 매개변수는 불변 변수이기 때문에 재할당 할 수 없음
  • 리턴 타입을 생략하면 컴파일러가 반환값을 이용하여 자동으로 추론
  • 리턴문이 생략되고, 리턴 타입이 Unit이 아니면 함수의 마지막 값을 리턴
// 함수 선언 
def add(x: Int, y: Int): Int = {
  return x + y
}

// x는 val 이기 때문에 변경 불가 
def add(x: Int): Int = {
  x = 10 
}

// 리턴 타입 생략 가능 
def add(x: Int, y: Double) = {
  x + y
}

// 리턴 타입이 Unit 타입도 생략 가능 
def add(x: Int, y: Int) = {
  println(x + y)
}

// 리턴 데이터가 없는 경우 Unit을 선언  
def add(x: Int, y: Int): Unit = {
  println(x + y)
}

함수의 축약

함수가 1라인으로 처리 가능한 경우에는 중괄호({}) 없이 선언할 수도 있음

// 중괄호 없이 선언 
def printUpper(message:String):Unit = println(message.toUpperCase())

// 반환타입도 생략 
def printLower(message:String) = println(message.toLowerCase())

파라미터의 기본값

파라미터를 선언하는 시점에 기본값을 지정하고, 파라미터의 개수대로 전달하지 않으면 기본값을 이용

// y에 기본값 선언 
def add(x: Int, y: Int = 10): Unit = {
  println(x + y)
}

// 기본값 이용
scala> add(1)
11

// 전달값 이용 
scala> add(10, 3)
13

가변 길이 파라미터

같은 타입의 개수가 다른 가변 길이의 파라미터를 입력 받는 경우 *를 이용하면 Seq형으로 변환되어 입력

// 여러개의 Int 형을 입력받아서 합계 계산 
def sum(num:Int*) = num.reduce(_ + _)

scala> sum(1, 2, 3)
res22: Int = 6

scala> sum(1)
res23: Int = 1

함수에 변수 결과 할당

함수를 def로 선언하지 않고 var, val로 선언할 수도 있음 →
함수를 실행하여 그 반환값이 변수에 입력되므로 함수의 결과(정해져 있는 같은 값)를 여러곳에서 이용할 때만 사용하는 것이 좋음

val random1 = Math.random()
var random2 = Math.random()
def random3 = Math.random()

// random1, random2는 호출할 때마다 같은 값 반환
// 선언 시점에 값이 확정됨 
scala> random1
res19: Double = 0.1896318278308372
scala> random1
res20: Double = 0.1896318278308372

scala> random2
res21: Double = 0.817421180386978
scala> random2
res22: Double = 0.817421180386978

// random3은 실행시점에 값이 확정됨 
scala> random3
res24: Double = 0.4491518929189594
scala> random3
res25: Double = 0.7644113222566244

중첩 함수(nested function)

함수를 중첩하여 사용 가능

  def run() {
    def middle() {
      println("middle")
    }

    println("start")
    middle()
    println("end")
  }

scala> run
start
middle
end

람다 함수(Lambda function)

  • 익명의 람다 함수를 선언할 수 있음
  • 언더바 _를 이용하여 묵시적인 파라미터를 지정할 수 있음
  • 묵시적인 파라미터를 이용할 때는 언더바의 위치에 따라 파라미터가 선택 됨

ex)

[exec 함수]

두개의 Int 파라미터를 받고, Int를 반환하는 고차함수 f 와 Int 형 파라미터 x, y를 입력받아서 f 함수를 호출 하면서 파라미터로 x, y를 전달하는 함수

// exec는 3개의 파라미터(함수 f, x, y)를 받음
def exec(f: (Int, Int) => Int, x: Int, y: Int) = f(x, y)

// 람다 함수를 전달하여 처리. x+y 작업을 하는 함수(f) 전달 
scala> exec((x: Int, y: Int) => x + y, 2, 3)
res12: Int = 5

// 선언시에 타입을 입력해서 추가적인 설정 없이 처리 가능 
scala> exec((x, y) => x + y, 7, 3)
res13: Int = 10

// 함수에 따라 다른 처리 
scala> exec((x, y) => x - y, 7, 3)
res14: Int = 4

// 언더바를 이용하여 묵시적인 처리도 가능 
scala> exec(_ + _, 3, 1)
res15: Int = 4

커링(currying)

  • 여러 개의 인수 목록을 여러 개의 괄호로 정의할 수 있음
  • 함수를 정해진 인수의 수보다 적은 인수로 호출하면 그 리턴 값은 나머지 인수를 받는 함수임
// x를 n으로 나누어 나머지가 0인지 확인하는 함수
//함수의 파라미터를 표현하는 방법은 다르지만 같은 함수
def modN(n:Int, x:Int) = ((x % n) == 0)     // 1번 n, x 인수를 2번에서는 여러개의 괄호로 받은 것
def modN(n: Int)(x: Int) = ((x % n) == 0)   // 2번 커링을 이용해서 n 값을 미리 바인딩 하는 다른 함수로 선언하거나, 다른 함수의 파라미터로 전달할 수 있음


def modN(n: Int)(x: Int) = ((x % n) == 0)

// modN함수를 커링을 이용하여 n 값이 정해진 변수로 호출 
def modOne:Int => Boolean = modN(1)
def modTwo = modN(2) _ // _가 modN(2)메소드를 함수로 만들어줌 없으면 에러 남, eta-expansion이라고 함

println(modOne(4))  // true
println(modTwo(4))  // true
println(modTwo(5))  // false

클로저(Closure)

  • 함수형 언어에서 클로저는 내부에 참조되는 모든 인수에 대한 묵시적 바인딩을 지닌 함수
  • 자신이 참조하는 것들의 문맥을 포함
  • 지연 실행의 좋은 예(클로저 블록에 코드를 바인딩함으로써 그 블록의 실행을 나중으로 연기할 수 있음)

타입(Type)

타입을 이용해서 클래스와 함수를 제네릭하게 생성할 수 있음

제네릭(Generic)한 생성이란?
제네릭(generic)이란 데이터의 타입(data type)을 일반화한다(generalize)는 것을 의미하고,

제네릭하게 생성하는 것은
클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법(컴파일 시에 미리 타입 검사(type check)를 수행)

1. 클래스나 메소드 내부에서 사용되는 객체의 타입 안정성을 높일 수 있음
2. 반환값에 대한 타입 변환 및 타입 검사에 들어가는 노력을 줄일 수 있음

 

클래스

import scala.collection.mutable //스칼라는 기본적으로 불변(immutable) 데이터를 이용하기 때문에 변경가능한 스택처리를 위해 import 문을 추가해야 함

trait TestStack[T] {
  def pop():T
  def push(value:T)
}

class StackSample[T] extends TestStack[T] {//[T] 부분

  val stack = new scala.collection.mutable.Stack[T]

  override def pop(): T = {
    stack.pop()
  }

  override def push(value:T) = {
    stack.push(value)
  }
}

val s = new StackSample[String]

s.push("1")
s.push("2")
s.push("3")

scala> println(s.pop())
3
scala> println(s.pop())
2
scala> println(s.pop())
1

메소드

def sample[K](key:K) {//[K] 부분
  println(key)
}

def sample2 = sample[String] _// [String] 부분

scala> sample2("Hello")
Hello

 

 

 

참조:

https://wikidocs.net/26140

728x90

댓글