프로그래밍 언어론 2nd edition Tucker and Noonan 7장 의미 구조 이스마엘(Ishmael): 분명히 이 모든 것이 의미없이 존재하지 않아 헤르만 멜빌(Herman Melville), 모비딕 (Moby Dick)
소목차 7.1 동기 7.2 계산식의 의미구조 7.3 프로그램 상태 7.4 저장문의 의미구조 7.5 제어 흐름 의미구조 7.6 입출력 의미구조 7.7 예외 처리의 의미구조
7.1 동기 프로그래밍 언어의 의미구조를 엄밀하게 정의하 면 다음 사람들에게 도움이 된다. 프로그래머 컴파일러 작성자 7.1 동기 프로그래밍 언어의 의미구조를 엄밀하게 정의하 면 다음 사람들에게 도움이 된다. 프로그래머 컴파일러 작성자 표준 제정자 프로그래밍 언어는 문법, 타입 체계, 의미구조가 모두 정의되어야만 완전하다.
의미구조(semantics)는 문법과 타입이 잘 맞는 프로그 램의 “의미”에 대한 정확한 정의이다. 의미를 주는 방법: 특정 기계에서 특정 컴파일러로 번역했을 때 실행 코드가 하는 일 (예: Fortran의 경우 IBM 709) 실행 전후의 상태를 “언어”로 기술 (12 장) 명령문을 상태 변경 함수로 정의 이 장에서는 다소 엄밀하지 못한 첫 번째 방법, 즉 실행 의미구조(operational semantics)를 사용한다.
7.2 계산식의 의미구조 중위 표기법: (a + b) - (c * d) 폴란드 전위 표기법: - + a b * c d 7.2 계산식의 의미구조 중위 표기법: (a + b) - (c * d) 폴란드 전위 표기법: - + a b * c d 폴란드 후위 표기법: a b + c d * - 캠브리지 전위 표기법: (- (+ a b) (* c d)) 중위 표기법에서는 모호함을 해소하기 위해 우선순위 (precedence)와 결합성(associativity)을 사용
연산자의 결합성 언어 + - * / 단항 - ** == != < ... C 부류 좌 우 좌 Ada 좌 없음 없음 없음 Fortran 좌 우 우 좌 이 계산식의 의미는? a < b < c
연산자의 우선 순위 연산자 C 부류 Ada Fortran 단항 - 7 3 3 ** 5 5 * / 6 4 4 + - 5 3 3 == != 4 2 2 < <= ... 3 2 2 not 7 2 2
단축 계산 (Short Circuit Evaluation) 논리곱 a and b 에 대한 정의는: if a then b else false 논리합 a or b 에 대한 정의는: if a then true else b
예제 Node p = head; while (p != null && p.info != key) p = p.next; if (p == null) // 리스트에 없는 경우 ... else // 리스트에서 찾은 경우
단축계산이 없다면 boolean found = false; while (p != null && ! found) { if (p.info == key) found = true; else p = p.next; }
부작용 부작용(side effect)은 계산 도중 지역이 아닌 변수 를 변경시키거나, 입출력이 발생하는 것을 말한 다. 다음을 계산하면 결과가 무엇일까? i = 2; b = 2; c = 5; a = b * i++ + c * i;
7.3 프로그램 상태 프로그램 상태(program state)는 사용하고 있는 개 체에 대해 어떤 값을 가지고 있는지 모아 놓은 것을 말한다. 두 개의 맵(map): 사용 중인 개체에 대해 메모리 주소 메모리 주소에 대해 갖고 있는 값
현재 명령문의 실행 결과는 현재 상태에 따라 다르 다. 프로그램의 실행은 상태를 계속 변경하는 과정으 로 볼 수 있다 현재 명령문의 실행 결과는 현재 상태에 따라 다르 다. 프로그램의 실행은 상태를 계속 변경하는 과정으 로 볼 수 있다. 이 장에서는 간결하게, 프로그램 상태를 변수에서 변수에 저장된 값으로의 맵으로 보겠다.
// n의 계승 계산 1 void main ( ) { 2 int n, i, f; 3 n = 3; 4 i = 1; 5 f = 1; 6 while (i < n) { 7 i = i + 1; 8 f = f * i; 9 } 10 }
// n의 계승 계산 1 void main ( ) { 2 int n, i, f; 3 n = 3; 4 i = 1; 5 f = 1; 6 while (i < n) { 7 i = i + 1; 8 f = f * i; 9 } 10 } n i f undef undef undef 3 undef undef
// n의 계승 계산 1 void main ( ) { 2 int n, i, f; 3 n = 3; 4 i = 1; 5 f = 1; 6 while (i < n) { 7 i = i + 1; 8 f = f * i; 9 } 10 } n i f 3 undef undef 3 1 undef
// n의 계승 계산 1 void main ( ) { 2 int n, i, f; 3 n = 3; 4 i = 1; 5 f = 1; 6 while (i < n) { 7 i = i + 1; 8 f = f * i; 9 } 10 } n i f 3 1 undef 3 1 1
// n의 계승 계산 1 void main ( ) { 2 int n, i, f; 3 n = 3; 4 i = 1; 5 f = 1; 6 while (i < n) { 7 i = i + 1; 8 f = f * i; 9 } 10 } n i f 3 1 1
// n의 계승 계산 1 void main ( ) { 2 int n, i, f; 3 n = 3; 4 i = 1; 5 f = 1; 6 while (i < n) { 7 i = i + 1; 8 f = f * i; 9 } 10 } n i f 3 1 1 3 2 1
// n의 계승 계산 1 void main ( ) { 2 int n, i, f; 3 n = 3; 4 i = 1; 5 f = 1; 6 while (i < n) { 7 i = i + 1; 8 f = f * i; 9 } 10 } n i f 3 2 2 3 2 1
// n의 계승 계산 1 void main ( ) { 2 int n, i, f; 3 n = 3; 4 i = 1; 5 f = 1; 6 while (i < n) { 7 i = i + 1; 8 f = f * i; 9 } 10 } n i f 3 2 2 3 3 2 3 3 6
// n의 계승 계산 1 void main ( ) { 2 int n, i, f; 3 n = 3; 4 i = 1; 5 f = 1; 6 while (i < n) { 7 i = i + 1; 8 f = f * i; 9 } 10 } n i f 3 3 6
7.4 저장문의 의미구조 주제 복식 저장 (multiple assignment) 저장 명령문(assignment statement)과 저장 계산 식 (assignment expression) 저장의 복사 의미구조(copy semantics)와 참조 의미구조 (reference semantics)
복식 저장 예: a = b = c = 0; 모든 변수를 0으로 초기화
저장 명령문과 저장 계산식 대부분의 언어에서 저장은 명령문이다. 계산식 도중에 나올 수 없다. 대부분의 언어에서 저장은 명령문이다. 계산식 도중에 나올 수 없다. C 부류의 언어에서는 저장은 계산식이다. 예: if (a = 0) ... // 오류 while (*p++ = *q++) ; // strcpy while (ch = getc(fp)) ... // ??? while (p = p->next) ... // ???
복사 의미구조와 참조 의미구조 복사: a = b; 참조: a, b 는 같은 값을 갖는다. 한 쪽이 변경되어도 다른 쪽에 영향 없다. 명령 중심 언어에서 사용된다. 참조: a, b 는 같은 객체를 가리킨다. 한 쪽이 변경되면 다른 쪽도 변경된다. 객체지향 언어에서 사용된다.
public void add (Object word, Object number) { Vector set = (Vector) dict.get(word); if (set == null) { // not in Concordance set = new Vector( ); dict.put(word, set); } if (allowDupl || !set.contains(number)) set.addElement(number);
7.5 제어 흐름 의미구조 명령 중심 언어가 완전해 지기 위해 문장 나열 (statement sequence) 조건문 (conditional statement) 루프 (looping statement) 가 필요하다.
문장 나열 s1 s2 의미구조: 중간에 분기가 없다면, s1 실행 후, s2 실행. s1의 결과 상태가 s2의 입력 상태
조건문 IfStatement if ( Expresion ) Statement [ else Statement ] 예: if (a > b) z = a; else z = b; 조건식이 참이면, 조건문의 결과 상태는 then 부분의 결과 상태, 아니면, 조건문의 결과 상태는 else 부 분의 결과 상태.
루프 WhileStatement while ( Expression ) Statement 괄호 안의 조건식을 계산한다. 참이면, 뒤의 명령문을 실행하고 다시 루프를 반복 한다. 거짓이면, 루프를 종료한다.
7.6 입출력 의미구조 open/close에 의해 입출력이 시작되고 끝난다. 접근 방법: 순차(sequential)와 임의(random) 스트림(stream)과 고정 길이 레코드 문자 방식과 바이너리 방식 형식 (format)
표준 파일 Unix: stdin, stdout, stderr C: stdin, stdout, stderr C++: cin, cout, cerr Java: System.in, System.out, System.err
입출력 스트림 Fortran Java integer :: i, a(8) write(8,*) “Enter 8 integers: “ read(*,*) a write(*,*) a Java 파일, 파이프, 메모리, 웹 주소 필터 읽기용, 쓰기용
형식 기술자 (Format String) C Fortran 코드: d, e, f, c, s (int, float, float, char, 문자열) 형식: % 길이 (생략가능) 코드 예: %s %5d %20s %8.2f Fortran 코드: i, f, a (integer, float, string) 형식: 반복회수(생략가능) 코드 길이 예: 8i4, f8.2, a20
목적 프로그램을 단순 명료하게 응용 프로그램을 보다 견고하게 (robust) 견고하다는 것이 뭐지?
(. Pascal – 문제가 무엇이지. ) (. open. ) sum := 0 (* Pascal – 문제가 무엇이지? *) (* open *) sum := 0.0; count := 0; count := count + 1; end; ave := sum / count;
예외 처리 모델 그림 7.9 예외 발생 재개 종료
#include <iostream #include <iostream.h> int main () { char A[10]; cin >> n; try { for (int i=0; i<n; i++){ if (i>9) throw "array index error"; A[i]=getchar(); } catch (char* s) { cout << "Exception: " << s << endl; } return 0;
Java 예외의 클래스 상속관계 그림 7.10
새로운 예외 클래스 정의
Java에서의 안전한 입력 처리 그림 7.11
잘못된 입력에 대한 예외 처리 (재개 모델) 그림 7.12
StackUnderflowException 클래스 그림 7.13
예외 발생 그림 7.13
AssertException 클래스
Assert 클래스 그림 7.15
단언문의 사용