Chapter 2 - 2. The Basics, Error Handling

2.4.3 Error Handling ~ 2.5 postscript

- 본 문은 노트 정리 차원에서 기술한 내용입니다.


에러 핸들링을 위한 편의 시설 - 시스템 그 자체. 빌트인 타입과 if 문 등의 statement 만이 아닌 어플리케이션에 적합한 타입 ( string / map .. ) 이용하고 알고리즘들을 이용할수있다. 이런 고수준 구조가 존재 할 수록 버그를 만들 여지가 줄고 에러를 더 잡기 쉬움.

가장 중요한 것은 추상화.


2.4.3.1 Exceptions

vector 구현자에게 있어 out of range 에러를 사용자에게 알려주고 싶다.

예를 들면 다음과 같이 연산자 오버로딩을 통해 에러를 던질 수 있다.


double& Vector::operator[](int i)

{

if (i<0 || size()<=i) throw out_of_rang e{"Vector::operator[]"};

return elem[i];

}


throw 는 out-of-range 타입의 예외를 연산자 호출부로 전달한다.

그러기 위해 콜스택 풀기가 필요. 


try - 블럭으로 예외가 발생할 것 같은 곳에 두고 catch 로 발생한 예외를 잡아낸다.

이 메커니즘은 에러 핸들링을 간단/시스템적으로/가독성있게 만들어준다.



2.4.3.2 Invariants

vector 의 멤버가 어떤 타당한 값을 가지고 있지 않으면 아무 의미도없다. 주석을 통해 elem 은 double 형의 sz 개의 배열을 가지고 있어야 하지만 이를 강제하는 것은 없다. 이렇게 어떤 클래스가 유효함을 보장하기 위해 사실이어야 하는 진술이 class invariant 라고 불림.

생성자를 통해 초기화 되어 소멸자가 호출되기 전까지 지켜져야함


invariant 보장을 위해 다음과 같이 정의 

Vector::Vector(int s)

{

if (s<0) throw length_error{};

elem = new double[s];

sz = s;

}

standard-library 의 length-error 사용.

보통 에러를 '핸들링' 한다는 것은 최소한의 로컬 클린업과 rethrowing exception 을 의미.


클래스의 invariant , 함수의 preconditions 는 비슷하다. 

invariant 는

- 우리가 뭘 원하는지 이해하는데 도움을 준다

- 특정한 것에 초점을 맞추게 한다. ( 디버깅 / 테스트 후에 우리 코드를 올바르게 만들 수 있는 더 많은 기회)


2.4.3.3  static assertions

컴파일 타임 체크. - 다라서 constexpr 로 표현되는 predicate 여야한다.

static_assert(<predicate>, <message>);




Scala.1 기본 문법. Method / if - expression / for - expression

Programming/Scala 2014. 6. 14. 00:41

Scala.1 기본 문법


사족

지금 와서 스칼라 포스팅이라니.

Functional Programming Language 를 사용할 일이 있어서 어떤 언어로 쓸까 하다가 한번이라도 접해봤던 Scala 를 선택했다. 

작년에 공부했던거 같은데 별로 사용을 안하니 엄청난 속도로 pop 이 되서 .. ㅡㅡ;;;; 

정리해뒀으면 좋았겠지... 라고 같은 후회를 이번에도 하며, 

오랜만에 ... 프로그래밍 관련해서 제대로 글을 쓴게 몇년 만인지 회상하며 포스팅!


어쨌거나 기념비적인 새로운 언어와 첫번째 만남이 되는 글인 만큼 외쳐본다.





Method 의 선언

명색이 Functional Programming Language 다. Method 부터! 

- 다음에 다룰테지만, 스칼라는 Method 와 Function 의 (메서드,함수) 개념이 분리되어 있다.  본문에서는 두 개념을 메서드로 통칭한다.


다음과 같이 정의한다. 

def <functionName> (<argument>: <type> ... ) : <returnType> = { <body> }

단, <returnType> 의 경우 deduction 이 가능하므로 생략 가능하다. 


예컨데

def sum (a: Int, b: Int): Int = {

a + b

}


return keyword 는 사용하지 않는 것이 일반적.


인자 / 리턴 타입으로 사용할 수 있는 타입들

Unit(void), Int, Boolean, String, Method ( a : Int => Int , 인트를 받아 인트를 리턴하는 메서드 )


상수 / 변수 

상수는 그나마 나은데, 변수는 가능한한 어쨌거나 사용하지 않는 것이 스칼라의 기본 스타일이었던 것 같다. 

가장 기본이라고 생각할 수 있는 이 문법을 두번째로 소개하는 이유라 하겠다.

선언할 때, 타입명은 적을 수 없다.


val a = 3

var b = 3


if-expression 

우리의 마음속에 있는 그대로 친숙하다.


if (<predicate>) { <then-clauses> } else { <else-clauses> }


brace ( { , } ) 는 다른 언어와 마찬가지로 생략 가능하다. 



for-expressions

일반적인 for

for ( <indexing var> <- <initial value> until <escape condition> ) 

즉, 다음과 같이 사용할 수 있다.


for (i <- 0 until 10) {


}


for-each ( 여기서의 for - each 는 자바에서의 개념이다 )

컬렉션의 요소를 하나씩 방문하는 형태인 for-each 문 


for (<element> <- <collection>) { <body> }


즉 , 다음과 같이 사용할 수 있다.


val array = Array(1, 2, 3)

for ( element <- array )

{

println(element)

}


또, 스칼라에서 자체적으로 foreach 라는 컬렉션의 멤버 메서드가 built-in 으로 존재하는데, 다음과 같다

<collection>.foreach(<Method>)


컬렉션의 각 요소에 대해서 인자로 전달받은 메서드를 적용한다. 


val array = Array(1, 2, 3)

def increment(a: Int){

  a+1

}

array.foreach(increment)


요소는 하나씩 얻어지므로, foreach 의 Method 는 element => Unit 의 형태로 구성되어야 한다. 


Structure and Interprecation Of Computer Programs - Chapter 1.1

MIT 컴퓨터 공학 1학년 교재로 사용된다는 SICP. (Structure and Interprecation of Computer Programs)

번역본까지 출판되어 잘 팔리고 있는 책.

문제가 되지 않는 선에서 간략히 필자의 노트 필기 차원에서 여기에 정리함.


책에서는 Lisp 으로부터 파생된 (방언이라 표현하던데) scheme 으로 예제들이 정리가 되어있는데,

필자는 Scala 로 풀어보았음. - scala 소개는 본 블로그의 scala 문법 참고. 


초반부 - Lisp 소개와, scheme 의 기본 문법소개, 그리고 전위 연산 표기 소개등.


정의대로 계산법 (normal-order evaluation), 인자 먼저 계산법 (applicative_order evaluation)

def sum_of_square(a: Int, b: Int) = {

a*a + b*b 

}

sum_of_square(5+5, 2+3)

와 같이 작성했을때 


정의대로 계산법은 

1. (5+5)*(5+5) + (2+3)*(2+3)

2. 10*10 + 5*5 

3. 100 + 25

4. 125 

와 같이 좁혀지며 계산되며


인자 먼저 계산법은 

1. 10*10 + 5*5

2. 100 + 25

3. 125 

와 같이 계산된다.


예제 1.3

세 숫자중 2개의 큰 숫자를 제곱하여 합하라



예제 1.5

정의대로 계산법과 인자 먼저 계산법에서의 다음 연산의 차이

(define (p) (p))

(define (test x y)

 (if (= x 0)

 0 y))

Then he evaluates the expression 

(test 0 (p))







1.1.7 - Square Roots by Newton's Method


어떤 수 x 의 제곱근을 구하는 방법으로, 뉴튼법이 있다.

일단 어떤 적당한 값으로 나눠보고 (x / guess) , 이 값을 제곱했을때 값과 x 의 차이가 오차범위 이내이면 이 값을 제곱근으로 리턴하고,

그렇지 않으면 나눈 결과 값과 추측값의 평균값을 구한다. ( (x / guess + guess) / 2 ) 

이 평균값을 다시 추측값으로 하여 전 과정을 오차범위 이내일때까지 반복한다.

-> 장황하지만 간단히 얘기해서, 어떤 값으로 나눠본 결과가 터무니 없으면 그 어떤값과의 평균값으로 보정하는 과정을 반복하는 것이다.

-> 정확한 값이라면 (x/guess + guess) / 2 는 

와 같이 전개되어 결국 원하는 값이 된다.


스칼라로 표현하면

def newton = {

def sqrt(x: Int) = {

def iter_sqrt(guess: Float, x: Int): Float = {

def improve(guess: Float): Float = {

println("guess : " + (x / guess + guess)/2)

(x / guess + guess)/2

} // improve

def good_enough(guess: Float):Boolean = {

def pow(a: Float) = {

a*a

}

if (Math.abs(pow(guess) - x) < 0.001) true else false

}

if (good_enough(guess))

guess

else

iter_sqrt(improve(guess), x)

} //iter_sqrt

iter_sqrt(1, x)

}

sqrt(2)

}                                             //> newton: => Float

newton                                    //> guess : 1.5

                                                  //| guess : 1.4166667

                                                  //| guess : 1.4142157

                                                  //| res3: Float = 1.4142157

와 같이 된다.



예제 1.6  new-if 로 scheme 의 if cond 를 다음과 같이 직접 작성했다.

(define (new-if predicate then-clause else-clause)

(cond (predicate then-clause)

(else else-clause))) 

만약 이 new-if 를 newton's method 에 적용한다면 ?




예제 1.7 뉴튼법은 큰 숫자나 작은 숫자엔 적용하기 어렵다. 그 예를 들어 과정을 설명하고, 개선해보라.


예제 1.8  세제곱근을 구하는 공식은 (x/y^2 + 2y)/3 과 같다. 작성해보아라.