제7장 제어구조 I – 식과 문장
7.1 식(Expression)
식이란 무엇인가? 식 식의 종류 상수, 변수 등에 연산자나 괄호를 적용하여 구성한다. 계산 결과 값이 있다. 산술식(arithmatic expression) 계산 값이 수인 식 3 + 4 * 5 부울식(Boolean expression) 계산 값이 논리값(참, 거짓) 객체식(object expression) 계산 값이 객체인 식 new C(); x.m();
식의 표기법 전위표기(Prefix form) 후위표기(Postfix form) 중위표기(Infix form) + 3 * 4 5 (+ 3 (* 4 5)) add(3, mul(4,5)) 후위표기(Postfix form) 3 4 5 * + 중위표기(Infix form) 3 + 4 * 5 연산자 적용 순서를 나타내는데 괄호가 필요하다. (3 + 4) * 5 + * 3 4 5
식의 계산 적용 순서 계산(Applicative order evaluation) 예 엄격 계산(strict evaluation)이라고도 한다. 피연산자를 먼저 계산하고 연산자를 적용한다. 예 (3 + 4) * (5 – 6) mul(add(3,4), sub(5,6)) * + - 3 4 5 6
식의 계산 정규 순서 계산(normal order evaluation) 예 지연 계산(Delayed evaluation)이라고도 한다. 피연산자(매개변수) 계산 전에 연산(함수) 계산 시작. 예 int double(int x) int square(int x) { return x + x; } { return x * x; } 정규순서 계산 square(double(2)) => double(2) * double(2) => (2 + 2)*(2 + 2) => 4 * (2 + 2) => 4 * 4 => 16 적용순서 계산 square(double(2)) => square(2 + 2) => square(4) => 4 * 4 => 16
사례 C의 배정식(assignment expression) C의 순차식(sequence operation) x = y; 는 배정할 뿐 아니라 결과 값이 있다. x = (y = z); x = y = z + w; C의 순차식(sequence operation) 몇 개의 식을 하나로 묶는다. 순차적으로 계산된다. , in C x = (y+=1, x+=y, x+1)
사례 조건식(if-expression) ML의 경우-식(case-expression) if e1 then e2 else e3 (ML) e1 ? e2 : e3 (C) ML의 경우-식(case-expression) case e1 of a => e2 | b => e3 | c => e4 ;
7.2 조건문 및 가드 (Conditional Statements & Guards)
E.W. Dijkstra 의 Guarded if 문 조건문의 이론적 형태 if B1 -> S1 | B2 -> S2 . . . | Bn -> Sn fi Bi는 부울식으로 가드 Si는 문장(들) 의미 Bi가 참이면 Si가 수행된다. 하나 이상 참이면 ?
if-문 if-문의 구문구조 <if-statement> ::= if (<expression>) <statement> [ else <statement> ] Dangling-else 문제 if (e1) if (e2) S1 else S2 최중첩 우선 규칙 (most closely nested rule ) Else는 가장 가까운 if와 짝을 짓는다. 예 if (e1) { if (e2) S1 } else S2 If (e1 ) if (e2 ) S1 else ; else S2
Dangling-else 문제 해결책 괄호 키워드 사용(Bracketing keyword in Algol68) <if-statement> ::= if <expression> then <statement> [else <statement>] fi 괄호 키워드(Ada, Fortran, Modula-2 ) end if, ENDIF, END 예 if x > 0.0 then y := 1.0/x; done := true; else x := 10.0; done := false; end if;
Case-문/Switch-문 C. A. R Hoare의 Case-문 구문구조 가드 –if 문의 특별한 종류 가드가 순서 값(ordinal value) 구문구조 case < expression> of <constant1> : <statment1>; <constant2> : <statment2>; . . . <constantn> : <statmentn>; end
Ada의 Case 문 경우 값 예 상수, 상수들의 리스트 혹은 범위 모든 값이 나열되거나 catch-all others가 사용되어야 한다. 예 case x-1 is when 0 => y := 0; z := 2; when 2 .. 5 => y := 3; z := 1; when 7 | 9 => z := 10; when others => null; end case
C의 switch-문 경우 값 예 정수 값이어야 하고 하나씩 나열되어야 한다. switch (x-1) {case 0: y = 0; z = 2; break case 2: case 3: case 4: case 5: y = 3; z = 1; break; case 7: case 9: z = 10; default: ; }
7.3 반복문/루프 (Iterative Statements/Loops)
Dijkstra의 Guarded do 문 루프의 이론적 형태 의미 do B1 -> S1 | B2 -> S2 . . . | Bn -> Sn od 의미 모든 Bi가 거짓일 때까지 반복한다. 참인 Bi가 비결정적으로 선택되고 해당 Si가 실행된다.
While 문 구문구조 C, C++, Java Algol68 Modula-2 while (expression) S while B-expr do S od Modula-2 while B-expr do S end
do-while 문 do S while (e);의 의미 구문적 양념(syntactic sugar) S; while (e) S; 다른 구조들을 이용하여 표현할 수 있는 언어 구조
for 문 구문구조 예 sum += a[i]; for (e1; e2; e3) S; for (초기화; 테스트; 갱신) S; while 문을 이용하여 작성하면 ? 예 for ( int i = 0; i < size; i++) sum += a[i];
for 문의 제약 제어(인덱스) 변수 제어 변수의 타입 및 제약 루프 내에서 변경할 수 없다. 루프가 끝난 후에 값은 미정이다. 제어 변수의 타입 및 제약 오직 정수 타입 (C) 임의의 순서 타입 (Pascal, Modula-2) 루프 내에서만 유효 (Ada)
Goto 논쟁 E.W. Dijkstra GOTO is considered to be harmful, Communications of ACM 68 GOTO 문에 대한 세 가지 의견 1. 효율과 좋은 프로그램 구조를 위해서도 필요 불가결하다. 2. 제한된 환경하에서 주의 깊게 사용 되야 한다. 3. 컴퓨터 언어에서 사라져야 한다.
7.5 예외 처리 (Exception Handling)
예외(Exception) 예외 Exception object is a first-class object 예외 (상황): 심각하지 않은 에러 혹은 비정상적 상황 예외 발생 시 계속 수행할 수 있도록 처리해야 한다 Exception object is a first-class object 일반 object처럼 클래스를 이용하여 정의되고 일반 object처럼 사용될 수 있다. 일반 object와 차이점 throw 될 수 있다.
Java에서 예외 선언 자바에서 예외 타입은 클래스로 선언 예외 선언 예외타입은 클래스이다 Exception 클래스나 서브클래스로부터 상속 받아 정의한다. 예외타입은 클래스이다 생성자, 멤버 필드, 메소드 등을 가질 수 있다.
예외 클래스 계층 구조 Object ….. Throwable Exception Error RuntimeException ClassNotFoundException IllegalAcessException InstantiationException InterruptedException NoSuchMethodException ….. ArithmeticException NagativeArraySizeException ArrayIndexOutOfBoundsException SecurityException …
예외 클래스: 예제 DivideZeroException.java 2 private String reason; 3 1 public class DivideZeroException extends Exception { 2 private String reason; 3 4 DivideZeroException() { 5 reason = "Divide by zero"; 6 } 7 8 public String toString() { 9 return reason; 10 } 11 }
throw exception-object; 예외 발생 예외는 throw문을 이용해서 발생시킨다 throw exception-object; throws와는 다름
예외 발생: 예제 Divider.java 1 public class Divider { 2 3 public Divider() { } 6 7 public int divide(int x) throws DivideZeroException { 8 switch(x) { 9 case 0: 10 throw new DivideZeroException(); 11 case 1: 12 throw new DivideZeroException(); 13 defaults: 14 ; 15 } 16 return (int)(100/(x*(x-1))); 17 } 18 }
예외 처리 예외 발생 시 처리하지 않는 경우 프로그램은 메시지를 내고 종료한다. 메시지는 call stack trace를 포함한다. main 메쏘드부터 호출과정
예외 처리: try-catch 문 구문 구조 try { … } catch (E1 x) { … } … catch (En x) { … } 예외가 발생할 수 있는 문장들을 try문 블록에 기술한다. 예외가 발생하면 해당 매칭되는 catch 문으로 제어가 넘어 간다.
예외 처리: finally 절 try { … } catch (E1 x) { … } finally 절은 옵션 catch (En x) { … } finally { … } finally문은 예외 발생 여부에 관계없이 항상 수행된다
예외 처리: 예제 TestException.java 1 public class TestException { 2 public TestException() { } 3 4 public static void main(String[] args) { 5 int a; 6 7 Divider t = new Divider(); 8 try { 9 a = Integer.parseInt(args[0]); 10 System.out.println("successful!: "+ t.divide(a)); 11 } catch(DivideZeroException e) { 12 System.out.println("error!!: " + e); 13 } catch(Exception ex) { 14 System.out.println("error!!: " + ex); 15 } …………..
예외 전파 처리되지 않는 예외 전파 호출의 역순으로 처리될 때까지 전파된다. main() 메소드까지 전파된다.
예외 전파: 예제 public class Propagation_Demo { static public void main (String[] args) { Exception_Scope demo = new Exception_Scope(); System.out.println("program beginning"); demo.level1(); System.out.println("program ending"); } // method main } // class Propagation_Demo
예외 전파: 예제 class Exception_Scope { public void level3 (int adjustment) { int current = 1; System.out.println("level3 beginning"); current = current / adjustment; System.out.println("level3 ending"); } // method level3 public void level2() { System.out.println("level2 beginning"); level3 (0); System.out.println("level2 ending"); } // method level2 public void level1() { System.out.println("level1 beginning"); try { level2(); } catch (ArithmeticException problem) { System.out.println (problem.getMessage()); problem.printStackTrace(); } System.out.println("level1 ending"); } // method level1 } // class Exception_Scope
예외 종류 검사 예외(checked exception) 비검사 예외(unchecked exception) 컴파일러가 처리 여부를 검사하는 예외 처리되지 않는 검사 예외는 메소드 헤더 부분에 throws를 이용하여 선언 비검사 예외(unchecked exception) RuntimeException로부터 상속 받는 표준 런타임 예외 처리 여부를 컴파일러가 검사하지 않는 예외
예외 명세 처리되지 않은 검사 예외를 메소드 이름 뒤에 명세 throws 절 이용 메소드_이름(…) throws A, B, C { . . . }
컴파일러의 예외 분석 컴파일-시간 예외 분석 발생된 예외가 처리되었는지 아니면 메소드 선언 시 명세 되었는지 컴파일-시간 검사 호출된 메소드에서 처리되지 않는 예외 메소드 선언에 명세된 처리되지 않는 예외 정보를 이용
Java 예외 클래스 예 ArithmeticException ArrayIndexOutofBoundsException 0으로 나누는 경우에 주로 발생하는 예외 상황 ArithmeticException은 RuntimeException으로부터 상속받은 예외 클래스 ArrayIndexOutofBoundsException 배열의 크기보다 큰 원소를 접근하려고 할 때 발생되는 예외 NegativeArraySizeException 배열의 크기가 음수로 된 경우에 발생하는 예외 NullPointerException 생성되지 않은 객체를 이용해서 객체의 멤버를 접근하는 경우에 발생하는 예외