본문 바로가기
Programming/Scala

[9강] 제어 문

by 유주원 2015. 4. 21.

스칼라에서 제어 구문은 if, while, for, try, match 가 전부이다.


if


스칼라에서 if 표현식은 단지 표현식이 아니라 값을 리턴한다. 그래서 아래와 같은 표현이 가능하다.


val filename = if(!args.isEmpty) args(0) else "default.txt"


위와 같이 표현을 하면 좋은 게 변수 선언과 동시에 값을 배정할 수가 있기 때문에 val 변수 사용이 가능하다는 것이다.

이렇게 val로 한번 선언한 변수는 final 효과를 지니기 때문에 효과적인 코딩 스타일을 구사할 수 있다.


while


while은 Unit 타입의 결과를 리턴한다. Unit은 빈괄호 ()를 리턴하기 때문에 void와는 다르다고 할 수 있다.

변수에 재할당 하는 코드 역시 Unit을 리턴한다. 그래서 아래의 코드는 문맥 오류를 일으킬 수 있다.


var line = ""

while (( line = readLine()) != "")

    println(line)


위의 line = readLine()의 결과는 항상 () 이기 때문에 while 조건문이 항상 True이다. 그래서 문맥 오류를 일으킬 수가 있다.

스칼라에서는 while 구문과 var 선언의 사용을 최소화 하는 것이 좋은 코딩 습관이라고 한다. 

while 문은 값을 리턴하지 않기 때문에 var 변수를 써야 할 것이며, var 변수는 코드에 다른 부가적인 영향을 끼칠 수 있기 때문이다. while, var의 사용 최소화와 함께 break와 continue문도 스칼라에서는 장려하고 있지 않다.


break 문 같은 경우에는 scala.util.control.Breaks._ 패키지를 통해 처리가 가능하다.


import scala.util.control.Breaks._


breakable {

    while(true) {

        if (in.readLine() == "") break

    }

}


readline이 빈 문자를 만날 경우 해당 루프는 breakable의 블록을 빠져나감으로써 다른 언어의 break 효과를 볼 수가 있다.


for


스칼라에서 for 구문은 아래와 같이 사용하곤 한다.


for (i <- 1 to 4)                // 1 ~ 4 까지 출력

    println(i)


for (i <- 1 until 4)            // 1 ~ 3까지만 출력

    println(i)   


또한 아래와 같이 쓸 수도 있다.

for (arg <- args)

    println(arg)


여기서 arg는 변수로 보일 수 있으나 val이다. 그래서 for문 안에서 arg 값을 변경시킬 수가 없다. arg 값은 바뀌는 것이 아니라 계속 새 값으로 다시 선언되는 것이다.


filter 라는 매서드를 통해 전체 item을 순회하지 않을 수도 있다.


val filesHere = (new java.io.File(".")).listFiles

for (file <- filesHere if file.getName.endsWith(".scala"))

   println(file)


위의 코드를 보면 현재 위치(".")에 있는 모든 파일 리스트를 가져와서 filesHere에 저장한다. for문을 통해 filesHere 리스트에 있는 file들을 하나씩 가져오는데 file이름이 .scala로 끝나는 파일만 출력하도록 짜여져 있다. 아래와 같이 여러 개의 filter로 적용도 가능하다.


for ( 

    file <- filesHere

    if file.isFile

    if file.getName.endsWith(".scala")

) println(file)


scala에서의 이중 for문은 아래와 같이 작성이 가능하다.


for (

    file <- filesHere

    if file.getName.endsWith(".scala");

    line <- fileLines(file)

    if line.trim.matches(pattern)

) println( file + ":" + line.trim)


filesHere의 file들에 대해 루프를 처음 돈 다음에 file 이름이 .scala인 파일에 대해서만 file의 라인들을 읽고 해당 라인들에 대해 for문을 돌려서 라인이 pattern과 일치할 경우에만 print 해주도록 짜여져 있다.


for 문에서도 yield라는 키워드를 통해 if문과 같이 값을 가져올 수 있다.


def scalaFiles = 

        for {

           file <- fileHere

           if file.getName.endsWith(".scala")

         } yield file


위의 결과는 file 이름이 .scala를 가진 file 리스트이다. 

yield 사용시 주의 사항이 있는데 바로 [for 절 yield 본문] 이 위치에 맞게 사용해야 한다. yield는 여는 중괄호 앞에 위치해야 한다.

아래와 같이 사용하면 에러가 발생한다.


for (col <- 1 to 10){

    yield col            // 에러

}


for (col <- 1 to 10) yield {     // 맞는 표현

    val prod = col * 3

    col + prod          

}


try


스칼라에서도 try문이 존재한다. 또한 throw를 통해 예외를 전달할 수도 있다. throw문을 아래처럼 값으로도 이용할 수 있다.


var divide = if (n % 2 == 0)

                            n/2

                      else

                           throw new RuntimeException("Error")


위에 예에서 만약 예외가 발생하면 divide란 변수에는 어떠한 값도 들어가지 않는다. 예외 발생 시 어떤 값으로도 초기화 되지 않고 예외를 발생시키기 때문이다. 그렇기 때문에 throw를 값으로 처리하는데 무방하다.


아래의 예제는 exception을 잡는 예제를 나타내고 있다.


try{

    ...

}catch {

     case ex: FileNotFoundException =>

     case ex: IOException =>


자바와 마찬가지로 스칼라에서도 finally 구문이 존재한다. finally 구문에서는 값을 반환하기 보다는 리소스 정리 작업 등의 구문 처리를 하는게 바람직하다. 왜냐하면 스칼라에서는 명시적으로 return이라는 키워드를 사용하지 않는 이상 finally의 리턴 값을 무시하기 때문이다.



위의 interpreter 결과를 보면 finally에서 return으로 직접적인 명시가 없는 이상 finally 값은 무시하는 것을 확인할 수가 있다.


match


match 구문은 switch 문과 유사하다고 할 수 있다. 사용법은 아래와 같다.



match 구문도 값을 반환하기 때문에 위와 같이 변수 할당을 할 수가 있다. 

자바의 switch 문과 차이가 있는데, 첫 번째로 자바의 switch에서는 enum이나 정수만 case문에 사용 가능하지만 scala의 경우에는 어떤 종류의 상수도 사용할 수가 있다. 두 번째는 scala에서는 break문이 따로 명시되어 있지 않다. 하지만 암묵적으로 break문이 존재하기 때문에 다음 로직으로 넘어가지는 않는다. 마지막으로 scala에서는 match의 결과가 값이라는 점이 큰 차이점으로 남는다.