-
Notifications
You must be signed in to change notification settings - Fork 36
7장 built in control structures
JeongHoon Byun (aka Outsider) edited this page Jun 9, 2013
·
15 revisions
- 스칼라의 제어구문(syntax) :
if
,while
,for
,try
,match
, 함수호출 - Scala의 대부분의 제어 구문은 값을 반환함. <- functional language들이 이렇게 동작한다고 함.
- Java의 ternary operator인 ? 의 경우, Scala의
if
로 대체 가능함. 이는if
나else
블럭의 마지막 평가값이 return되기 때문으로 이해했음. 이런 형태를 취함으로써 임시 변수의 필요성을 줄일 수 있다고 함.
int a = 1 > 10 ? 1 : 0; //java
val a:Int = if( 1 > 10 ) 1 else 0 //scala
scala> val k = try { val a = 1; }
k: Unit = ()
scala> val a = 1 match { case 1 => 1; case 2 => 2; }
a: Int = 1
- 일반적인 if문의 기능을 수행한다.
val filename =
if (!args.isEmpty) args(0)
else "default.txt"
- var보다 val을 사용하면 좋은점.
- 자바의 final변수처럼 사용할 수 있다.
- 등식추론에 더 도움이 된다. "equational reasoning"
- 읽기도 쉽고 리팩토링하기도 쉽다.
- 일반적인 while문의 기능을 수행한다.
def gcdLoop(x: Long, y: Long): Long = {
var a = x
var b = y
while (a != 0) {
val temp = a
a = b % a
b = temp
}
b
}
- do-while문도 있다.
var line = ""
do {
line = readLine()
println("Read: " + line)
} while (line != "")
-
while
과do-while
은 반환형이 Unit이기 때문에 표현식이 아니라 루프라고 한다.
scala> val a = while( false) { }
a: Unit = ()
-
Unit
타입이 반환하는 값은 unit value라고 하며()
로 표기한다.- 자바와는 다르다.
- 자바의 void와는 다르게 함수의 값을 비교할 수 있다.
- 대입식을 비교하는 구문으로 사용하면 루프를 쓰지 말아야 한다.
(대입하면 스칼라에서는 결과가 unit value,()
라서 ""이 될 수 없다. > 무한루프로 빠진다. pg.118)
-
while
문 대신 함수형으로 재귀호출을 쓸 수 있다. - java의 할당문은 할당된 값을 반환하지만, Scala의 할당문은 Unit 값을 반환함.
//java
public class A {
public static void main(String[] args)
{
String a = null;
System.out.println( a= "a");
}
}
// a //실행결과
//scala
scala> val a = { val b = 3; }
a: Unit = ()
- scala의
for
문은 상당히 강력하다. - collection iteration
val filesHere = (new java.io.File(".")).istFiles
for (file <- filesHere)
println(file)
* `file <- filesHere`부분을 generator라보 부르고 콜렉션 요소로 반복문을 수행한다.
* `Range`를 사용해서 `for`문을 사용할 수 있다.
scala> for (i <- 1 to 4) println("Iteration " + i)
Iteration 1
Iteration 2
Iteration 3
Iteration 4
scala> for (i <- 1 until 4) println("Iteration " + i)
Iteration 1
Iteration 2
Iteration 3
- 스칼라에서 정수를 반복하는 일반적인 방법.
// Not common in Scala....
for (i <- 0 to filesHere.length - 1)
println(filesHere(i))
-
스칼라에서는 켈럭션에서 직접 반복을 할 수 있기 때문에 거의 사용되지 않는 방법
-
Filtering
- for문 안에 if절을 넣어 필터링을 할 수 있다.
- for문은 표현식.(타입을 가진 콜렉션이 결과값으로 나오기 때문)
val filesHere = (new java.io.File(".")).istFiles
for (file <- filesHere if file.getName.endsWith(".scala"))
println(file)
// 다음과 동일하다.
for (file <- filesHere)
if (file.getName.endsWith(".scala"))
println(file)
* 필터링이 여러개 필요하면 필요한 만큼 쭉 if 를 붙이면 됨. && 로 덧붙이는 게 아님.
for (
file <- filesHere
if file.isFile
if file.getName.endsWith(".scala")
) println(file)
* _질문: && 로 덧붙이는 것이랑 if 를 여러개 붙이는 것이랑 차이가 있을까?_ -> 별 차이 없는 듯 한데.
scala> val a = for( i <- 1 to 10 ; if i %2 == 0 ; if i %3 == 0 ) yield i
a: scala.collection.immutable.IndexedSeq[Int] = Vector(6)
scala> val a = for( i <- 1 to 10 ; if i %2 == 0 && i %3 == 0 ) yield i
a: scala.collection.immutable.IndexedSeq[Int] = Vector(6)
- Nested iteration
-
<-
절을 추가하여 중첩루프로 쓸 수 있다.
-
def fileLines(file: java.io.File) = scala.io.Source.fromFile(file).getLines().toList
def grep(pattern: String) =
for (
file <- filesHere
if file.getName.endsWith(".scala");;
line <- fileLines(file)
if line.trim.matches(pattern)
) println(file + ": " + line.trim)
grep(".*gcd.*")
* `{}`를 `()`대신 쓸 수도 있다.(`{}`를 쓰면 `;`을 안써도 된다.)
- Mid-stream variable bindings
- 반복되는 표현식이의 계산을 한번만 하고 싶다면
=
를 사용해서 새로운 변수에 바인딩할 수 있다. 여기서 바인딩된 변수는val
은 없지만val
처럼 사용한다.
- 반복되는 표현식이의 계산을 한번만 하고 싶다면
def fileLines(file: java.io.File) = scala.io.Source.fromFile(file).getLines().toList
def grep(pattern: String) =
for (
file <- filesHere
if file.getName.endsWith(".scala");;
line <- fileLines(file)
trimmed = line.trim
if trimmed.matches(pattern)
) println(file + ": " + trimmed)
grep(".*gcd.*")
- Producing a new collection
- 지금까지는 반복문만 순회했지만 각 반복에서 생성된 값을
yield
를 사용해서 생성할 수 있다.
- 지금까지는 반복문만 순회했지만 각 반복에서 생성된 값을
for clauses yield body
def scalaFiles =
for {
file <- filesHere
if file.getName.endsWith(".scala")
} yield file
* [yield에 대한 토론](http://stackoverflow.com/questions/1052476/can-someone-explain-scalas-yield)
- 예외를 던지는 것은 자바와 동일하다.
throw new IllegalArgumentException
-
throw
도 결과값을 가지는 표현식이고 기술적으로는throw
는Nothing
타입이 된다. 어떤 타입도 될 수 있으며 이 반환값을 사용할 수는 없다. --> 이건 126pg 에서 else 문에 throw가 들어가도 문법에 어긋나지 않음을 설명하기 위함이라고 봄. -
catch
절은 패턴매칭을 이용해서 예외를 처리한다.
import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException
try {
val f = new FileReader("imput.txt")
} catch {
case ex: FileNotFoundException => // handle missing file
case ex: IOException => //Handle other I/O error
}
* 스칼라는 throws 절에서 checked exception를 잡거나 선언하도록 하지 않는다. 필요하다면 `@throws` 어노테이션으러 선언할 수 있다.
-
filnally
로 구문이 종료되는 것과 상관없이 항상 수행되어야 하는 코드를 처리한다. 자바랑 다를 바 없는 듯. 9.4절에 나오는 loan pattern을 고려할 수 있다고 함. - 예외가 던져졌는데 catch되지 앟으면 표현식의 결과값은 없다.
-
try
,catch
,finally
도 value를 반환함. (java랑 다름!) 다만 finally의 평가 결과는 무시됨. 따라서 finally에서 명시적으로 return을 하지 않는 이상 finally의 평가 값은 버려짐.
- switch문처럼 여러 대안을 선택할 수 있다.(int, enum만 쓸 수 있는 java의 switch 보다 훨씬 강력)
-
default
는_
- 자바의 switch문과는 다르다.
- 다양한 종류의 타입을 쓸 수 있다.
- break가 없다.(case 뒤에 break;를 붙일 필요 없음. 무조건 break.)
- 값이 도출된다.
대상 match {
case 1 => expr1
case 2 => expr2 ...
case _ => default
}
- match 표현식도 값을 반환함.
###7.6 Living without break and continue
- 스칼라에는
break
와continue
가 없다. -
boolean
타입의 필드를 사용하면break
,continue
를 쓰지 않을 수 있음. - 모든 continue는 if문으로 break문은 boolean값으로 바꿀 수 있다.
// This is Java
int i = 0;
boolean foundIt = false;
while (i < args.length) {
if (args[i].startsWith("-")) {
i = i + 1;
continue;
}
if (args[i].endsWith(".scala"")) {
foundIt = true;
break;
}
i = i + 1;
}
// looping without break or continue
var i = 0
var foundIt = false
while (i < args.length && !foundIt) {
if (!args(i).startsWith("-")) {
if (args(i).endsWith(".scala"))
foundIt = true
}
i = i + 1
}
// 재귀로 다시 구현
def searchFrom(i: Int): Int =
if (i >= args.length) -1
else if (args(i).startsWith("-")) searchFrom(i + 1)
else if (args(i).endsWith(".scala")) i
else searchFrom(i + 1)
val i = searchFrom(0)
- 필요하다면
scala.util.control
패키지에 있는Breaks
클래스에서break
메소드를 제공한다. -
Breaks
클래스는,breakable
메소드에서 처리하는 예외를 던져서 break를 구현한다.
import scala.util.control.Breaks._
breakable {
while (true) {
if () break
}
}
###7.7 Variable scope
- 변수를 선언하면 변수는 scope를 가지며 일반적으로
{}
가 새로운 범위를 갖는다. - 같은 scope에서는 동일한 이름의 변수를 쓸 수 없다. 단, 하위 scope에 상위 scope에 이미 선언된 변수를 선언할 수 있음(java에선 안됨).
- repl 에서 중복 선언이 가능한 것도 이런 특성에 기인함. repl에선 매 문장마다 nested scope를 만들기 때문이라 함
val a = 1;
{
val a = 2;
{
// ...
}
}
- 외부의 변수와 같은 이름인 내부변수를 shadow라고 부른다.
- 변수는 새 이름으로 의미있게!
- imperative 코드를 functional하게 바꿔보자.
// imperative style
def printMultiTable() {
var i = 1
while (i <= 10) {
var j = 1
while (j <= 10) {
val prod = (i * j).toString
var k = prod.length
while (k < 4) {
println(" ")
k += 1
}
print(prod)
j += 1
}
println()
i += 1
}
}
// funtinal style
def makeRowSeq(row: Int) =
for (col <- 1 to 10) yield {
val prod = (row * col).toString
val padding = " " * (4 - prod.length)
padding + prod
}
def makeRow(row: Int) = makeRowSeq(row).mkString
def multiTable() = {
val tableSeq =
for (row <- 1 to 10)
yield makeRow(row)
tableSeq.mkString("\n")
}