-
[스칼라] 내장 제어 구문스칼라 2022. 1. 31. 00:31
Programming in scala 4th edition을 읽고 정리한 내용입니다.
스칼라의 내장 제어 구문은 if, while, for, try, match, 함수 호출이 전부다. 대부분의 스칼라 제어 구문은 결과 값을 내놓는 형태로 구현되어있다.
1. if 표현식
예를들어서 입력이 있으면 그 입력값을 리턴하고 입력값이 없으면 "default" 라는 값을 할당하는 코드를 구현한다고 하자. 명령형 스타일로 작성한다면 아래와 같을 것이다.
def ifStatementFunction(input: String): String = { var name = "default" if (!input.isEmpty) name = input name } println(ifStatementFunction("")) println(ifStatementFunction("this is input")) // 출력 결과 default this is input
스칼라의 if 표현식을 활용하여 구현하면 위와 같은 기능을 하는 코드를 아래처럼 작성할 수 있다. 아래처럼 구현하면 코드가 짧아지는것도 장점이지만 val 키워드를 이용하여 변수를 선언할 수 있다는 점이 진짜 장점이다.
def ifStatementFunction2(input: String):String = { val name = if(input.isEmpty) "default" else input name }
2. while 루프
while문은 다음과 같다.
// while var counter = 0 while(counter < 10) { counter += 1 } println(counter) do { counter += 1 } while(counter < 20) println(counter)
스칼라에서 while문의 결과는 Unit 타입이다. Unit타입은 유닛값(unit value)만 있고 ()로 표기한다. ()값이 존재한다는 점에서 자바의 void와는 다르다.
def returnUnitFunction() = println("unit created") //returnUnitFunction: ()Unit var a = returnUnitFunction() //unit created a //a: Unit = () a = 10 //a: Unit = () 보다시피 var에 재할당을 하면 이때의 반환도 유닛이다.
주의 해야할 점: 스칼라에서 var 변수에 재할당을 할 때의 결과값도 Unit이 반환되는데 이 점 때문에 스칼라에서는 아래처럼 코드를 사용할 수가 없다.
var line = "" while ((line = readLine()) != "") println("Read: " + line)
line에 readLine의 결과값을 재할당하면 그 결과는 Unit이 반환되고, while의 조건문이 항상 참이되기 때문에 while은 제대로 동작하지 않는다.
3. For 표현식
대부분의 반복 처리를 위해서는 for 표현식이 활용된다.
컬렉션 이터레이션
val numbers = List(1, 2, 3, 4, 5) for(number <- numbers) { println(number) } //출력결과 1 2 3 4 5
위의 코드는 리스트를 이터레이션하는 간단한 예시이다. number <- numbers 는 제너레이터(generator)라고 부르고 각 반복때마다 number라는 새로운 val 을 해당 순서의 원소의 값으로 초기화한다.
필터링
for표현식의 제너레이터를 작성하는 부분에는 필터를 추가해서 원하는 값만 반복을 처리하도록 할 수도 있다. 예를들어 2와 3을 제외하고 싶다면 아래처럼 할 수 있다.
for(number <- numbers if number != 2 if number != 3) { println(number) } // 출력 결과 1 4 5
중첩이터레이션
// 중첩 포문 val numberLists = List(List(2, 4, 6), List(3, 6), List(4, 8, 12, 16)) for(numberList <- numberLists if(numberList.length > 3); number <- numberList if(number > 10)) { println(number) } // 결과 12 16
위와같이 중첩하여 이터레이션을 사용할 수도 있으며 각각의 제너레이터에 필터를 적용할 수도 있다.
반복 중 변수 바인딩
반복 중 중첩되는 부분이 많거나 값의 변형이 필요하다면 아래처럼 반복중에 변수를 새로 바인딩하여 활용할 수도 있다.
// 중첩 포문 for{numberList <- numberLists if(numberList.length > 3) number <- numberList fiveTimesBig = number * 5 if(fiveTimesBig > 10)} { println(fiveTimesBig) } //출력결과 20 40 60 80
새로운 컬렉션 만들어내기
yield키워드를 사용해서 반복의 결과를 새로운 컬렉션으로 만들 수도 있다. 아래 코드를 수행하면 lists에 for 문의 결과가 담긴다.
val numberLists = List(List(2, 4, 6), List(3, 6), List(4, 8, 12, 16)) val lists = for{ numberList <- numberLists if(numberList.length > 3) number <- numberList fiveTimesBig = number * 5 if(fiveTimesBig > 10) } yield fiveTimesBig lists // 출력 결과 List(20, 40, 60, 80)
4. try 표현식
예외 발생시키기
스칼라에서 예외를 발생시키는 방법은 아래와 같다.
throw new Exception
예외 발생 구문 또한 결과 타입이 있는 표현식이다. 예외는 Nothing 타입을 갖는다. 실질적으로 예외의 결과값을 할당받아서 쓰는 일은 없을것 같다
발생 예외 잡기
자바와 다르게 스칼라에서는 메서드에 throws로 명시해줄 필요는 없다.
// 예외 def errorFunction(n: Int): Int = { try { if(n > 10) throw new IllegalArgumentException if(n < 0 ) throw new RuntimeException n * 10 } catch { case ex: IllegalArgumentException => println("IllegalArgument"); 0 case ex: RuntimeException => println("RuntimeException"); 0 } } errorFunction(11) errorFunction(-1) errorFunction(10) // 결과 IllegalArgument res13: Int = 0 RuntimeException res14: Int = 0 res15: Int = 100
스칼라에서 예외를 잡는 방법은 catch 절을 사용하면 된다. 스칼라의 예외에서도 finally절을 선언해서 예외가 발생하든 발생하지않든 처리되어야할 작업을 정의할 수 있다. 한가지 알아둬야할 점은 스칼라의 try catch 또한 결과값을 발생시킨다는 점이다.
// 예외 값 반환 def f(): Int = try return 1 finally return 2 def g(): Int = try 1 finally 2 println(f()) println(g()) //출력 결과 2 1
return 을 명시적으로 써주었는지 아닌지에 따라서 결과값이 달라진다.
5. match 표현식
여러가지 선택지중 하나를 선택하는 처리를 위해서 다른언어에는 switch 문을 제공한다. 스칼라에는 match 표현식이 있다.
def switch(n: Int) = { n match { case 3 => println("Three") case 2 => println("Two") case _ => println("default") } } switch(3) switch(2) switch(-1) // 출력결과 Three Two default
match 구문의 어디에도 속하지 않는 경우는 _로 표현한다. match와 자바 switch 문과 차이점은 다음과 같다.
- 자바의 case문에는 enum, 정수, 문자열만 쓸 수 있음 vs 스칼라는 어떤 종류의 상수라도 사용 가능
- 스칼라 match는 결과가 값임
val n = 10 val nToString = n match { case 10 => "Ten" } println(nToString) // Ten
위와같이 스칼라 match는 결과를 값으로 반환한다는 점을 이용해 바로 변수에 할당해 사용할 수 있다.
'스칼라' 카테고리의 다른 글
[스칼라] 흐름 제어 추상화 (0) 2022.02.01 [스칼라] 함수와 클로저 (0) 2022.01.31 [스칼라] 함수형 객체 (0) 2022.01.30 [스칼라] 기본 타입과 연산 (0) 2022.01.30 [스칼라] 클래스와 객체 (0) 2022.01.30