Fascination
article thumbnail

↓Chapter 04. 변수 및 유효 범위

프로그래밍 언어론 원리와 실제 - 창병모 교수님


4.1 변수 선언

1) 변수 선언과 유효 범위

- 사용 전 선언(declaration before use): 변수는 사용하기 전에 선언되어야 함

- 변수의 유효 범위(scope)

  • 선언된 변수가 유효한(사용될 수 있는) 프로그램 내의 범위/영역
  • 변수 이름뿐 아니라 함수 등 다른 이름도 생각해야 함

- 정적 유효 범위(Static scope)

  • 선언된 이름은 선언된 블록 내에서만 유효함
  • 대부분 언어에서 표준 규칙으로 사용됨

 

2) 블록과 변수 선언

- 구문법

<stmt> → ...
              | id = <expr>;
              | let <decls> in 
                      <stmts>
                end
<decls> → {<type> id [=<expr>];}
<stmts> → {<stmt>}
<type> → int | bool | string

- 의미

  • 변수 id는 <type>타입 변수이며 초기화가 가능함
  • 초기화하지 않은 변수는 자동으로 기본값으로 초기화함
  • 변수 id는 지역 변수로 유효 범위는 선언된 블록 내임

- 정적 타입 언어(static typed language): 한 번 선언된 변수의 타입은 변하지 않음

 

3) 블록의 중첩

- let 블록 내에는 아무 문장이나 올 수 있으므로 let 블록 내에 다시 let 블록이 중첩되어 올 수 있음

let D1; in
	let D2; in
    ...
    end;
end;

↓

let D1; D2; in
...
end

D1, D2가 변수 선언을 나타낸다고 가정할 때,

아래와 같이 선언하는 것을 일종의 설탕 구문(syntactic sugar)라고 볼 수 있음

D1와 D2의 유효범위는 해당 블록 내부임

- 똑같은 이름의 변수 x를 구분하는 방법: 정적 유효범위 규칙 이용

  • 3번 줄에서 사용된 x는 외부 블록에서 선언된 x
  • 내부 블록 내의 6번 줄에서 사용된 x는 외부 블록과 내부 블록에서 선언된 x 둘 다 유효한데, 보통 이런 경우에는 내부 x가 우선권이 있음
  • 8번 줄에서 사용된 x는 외부 블록에서 선언된 x를 의미

 

4) 전역 변수 선언

- 지역 변수: let문 내에서 선언된 변수

- 전역 변수: let문 밖에서 선언된 변수

  • <command> → <decl> | <stmt> | <function>
  • <decl> → <type> id [=<expr>];

- 예

>> int x = 1;
>> let int y = 2;
		x = x + y;
   end;
>> print x;
  • 변수 x: 전역 변수, 변수 y: 지역 변수
  • let문 내에서는 둘 다 사용 가능하고 let문이 끝나면 전역 변수 x만 사용 가능

- 전역 변수와 같은 이름의 지역 변수 선언

>> int x=1;
>> let int x=2; in
		print x;
   end
2
>> print x;
1

- 함수를 정의한 후 호출해서 사용

>> fun int f(int x) return x*x;
>> print f(10);
100

 

5) 타입 없는 변수 선언

- 동적 타입 언어(dynamically typed language)

  • 변수의 타입을 선언하지 않고 바로 사용 가능
  • 변수에 어떤 타입의 값이든지 저장 가능
  • 장점: 융통성이 있음
  • 단점: 변수를 여러 타입으로 쓰게 되면 프로그램을 이해하기 힘듦. 즉, 해당 변수가 무슨 타입을 가지는지 알기 어렵기 때문에 어떤 값을 가지는지 추적해야 함 → 오류를 범하기 쉬워짐
  • Lisp/Scheme, Javascript, Python: 대부분 interpreter 방식

 

6) Python 전역 변수

함수 내에서 전역 변수 사용은 가능하나 수정은 불가능함

→ 전역 변수에 대입하면 자동으로 지역변수가 생성되기 때문임

함수 내에서 전역 변수를 사용하려면 global로 선언해야 함

 

 

4.2 블록 구조 언어

1) 선언

- 무엇을 선언하는가?

  • 상수, 변수, 함수 등을 선언할 수 있음

- 어디에 선언하는가?

  • 블록 내 혹은 밖에 변수 등을 선언할 수 있음
  • 구조체(struct) 내에 필드 변수를 선언할 수 있음
  • 클래스(class) 내에 필드 변수 혹은 함수를 선언할 수 있음

- 어떻게 선언하는가?

  • 명시적 선언: Pascal, C, C++, Java
  • 묵시적 선언을 허용: Lisp, Python, ML

 

2) 블록 구조 언어

- 블록

  • 서로 연관된 선언문과 실행문들을 묶어놓은 프로그래밍 단위
  • 블록은 변수나 함수를 선언하는 선언문들과 실행문들로 구성

- 블록 구조 언어(block structed language): 블록의 중첩을 허용하는 언어

  • 대부분의 블록 구조 언어는 블록 내에 블록의 중첩을 허용
  • 정적 유효 범위 규칙에 따라 블록에서 선언된 변수는 선언된 블록 내에서만 유효

블록 구조

- 블록 구조 언어의 특징 및 장점

  • 대형 프로그램을 여러 블록으로 나누어 작성하면 복잡한 수행 내용을 단순화하며 프로그램의 해독성을 높여줌
  • 프로그램 오류가 발생하여도 그 범위가 블록 단위로 한정되므로 수정이 쉬워지며 블록의 첨가, 삭제, 수정 등이 용이
  • 블록 내에 선언된 변수들은 그 안에서만 유효하며 실행이 종료되면 기존에 선언되었던 변수들은 모두 무효화됨
  • 사용자로 하여금 변수의 사용과 기억 장소의 할당에 관한 경계를 명확하게 할 수 있음

 

 

4.3 변수의 상태와 의미

1) 변수의 상태

- 변수: 메모리 위치(주소)를 나타내는 이름

x = x + 1;

오른쪽 변수 x의 의미: 메모리 위치에 저장된 값(r-value)

왼쪽 변수 x의 의미: 메모리 위치(l-value)

- 수식의 의미(값)를 말할 때는 반드시 변수들의 현재 값과 함께 이야기해야 함

- 현재 값을 이론적으로 표현하는데 다음과 같은 기본적인 수학 개념을 사용

  • 함수 집합: 정의역이 A이고 공변역이 B인 모든 함수들의 집합

  • 함수 f: A → B: 정의역이 A이고 공변역이 B인 하나의 함수

  • 함수 수정 f [a |→ b]: 함수 f에서 a에 대응한 값만 b로 수정하는 새로운 함수

- 상태(state): 프로그램이 실행 중일 때 변수들이 현재 갖고 있는 값

- 상태 s는 다음과 같이 변수 이름 집합인 Identifier에서 값 집합인 Value로 가는 하나의 함수로 정의할 수 있음

s: Identifier → Value

 

3) 수식의 의미

- 수식 E: 상태에서 수식의 값

V: (State, Expr) → Value

- 상태 s에서 간단한 수식의 의미

  • E → true | false | n | str | id
  • V(s, true) = T
  • V(s, false) = F
  • V(s, n) = n
  • V(s, str) = str
  • V(s, id) = s(id) 

- 산술 수식

  • E → E + E | E - E | E * E | E / E
  • V(s, E1 + E2) = V(s, E1) + V(s, E2)
  • V(s, E1 - E2) = V(s, E1) - V(s, E2)
  • V(s, E1 * E2) = V(s, E1) * V(s, E2)
  • V(s, E1 / E2) = V(s, E1) / V(s, E2)

- 비교 수식

  • E → E > E | E < E | E == E | E != E
V(s, E1 > E2) = T if V(s, E1) > V(s, E2)
				F otherwise
                
V(s, E1 < E2) = T if V(s, E1) < V(s, E2)
				F otherwise
                
V(s, E1 == E2) = T if V(s, E1) == V(s, E2)
				 F otherwise
                
V(s, E1 != E2) = T if V(s, E1) != V(s, E2)
				 F otherwise

 

4) 문장의 의미

- 상태 전이(State transition): 문장 실행 전 상태 s를 문장 실행 후 상태 s'으로 변경

(s, S) → s'

- 작동 의미론(operational semantics): 문장은 문장 실행 전 상태를 문장을 실행함으로써 실행 후 상태로 변경하는 일을 하는 것

  • 각 문장마다 하는 일을 상태 전이 규칙으로 정의하고 이를 이용하여 문장의 의미를 설명
  • 일련의 문장들로 구성된 프로그램의 의미도 문장들의 상태 전이 과정으로 설명

- 상태 변환 함수(state transformation fuction): 문장 S의 실행 전 상태에서 실행 후 상태 s'를 결정하며 Eval로 정의됨

Eval: (State, Statement) → State
Eval(s, S) = s' for each statement S

- 대입문 id = E

Eval(s, id = E) → s[id |→ V(s, E)]

- 복합문 S; S

Eval(s, S1; S2) = Eval(Eval(s, S1), S2)

- 예시

let int x; in
	x = 1;	(1)
    let int y; in
    	y = 2;	(2)
        x = x + y	(3)
    end
end

(1) 실행

(2) 실행

(3) 실행

 

 

4.4 변수의 유효 범위 관리

(1) 블록 구조를 위한 상태 관리

- 블록 시작을 만났을 때

  • 블록 내에 선언된 변수는 유효해 짐
  • 선언된 변수에 대한 상태 정보를 새로 생성

- 블록 내 문장을 만났을 때

  • 유용한 변수들의 상태 정보를 이용해서 문장들을 해석(실행)함

- 블록 끝을 만났을 때

  • 블록 내의 선언된 변수들은 더 이상 유효하지 않음
  • 블록 내의 선언된 변수들의 상태 정보를 제거

- 상태(state)를 스택(stack) 형태로 유지 관리

 

(2) 예제

let int x; in
	x = 1;
    let int y; in
    	y = x + 2;
    end
end

1: 변수 x의 값을 나타내는 상태 정보를 push 함

2: 유효한 변수 x의 상태 정보를 이용하여 2번 줄을 해석

3: 변수 y의 값을 나타내는 상태 정보를 스택에 push

4: 유효한 변수 x와 y의 상태 정보를 이용하여 4번 줄을 해석

5: 변수 y의 상태 정보를 pop

6: 변수 x의 상태 정보를 pop

let int x = 1; in
	let int y; in
    	y = x + 2;
    end
    let int x; in
    	x = 10;
    end
    x = x * 5;
end

1. 변수 x의 값을 나타내는 상태 정보를 스택에 push

2. 변수 y의 값을 나타내는 상태 정보를 스택에 push

3. 유효한 변수 x와 y의 상태 정보를 이용하여 3번 줄을 해석

4. 변수 y의 상태 정보를 pop

5. 새로운 변수 x의 값을 나타내는 상태 정보를 push. 스택에는 2개의 x가 존재

6. 변수 x를 만나면 심볼 테이블 탑에서부터 x를 찾음. 따라서 이 변수 x는 5번 줄에서 선언되어 최근에 추가된 x를 나타냄

7. 이 변수 x의 상태 정보를 pop. 이제 심볼 테이블에서는 1번 줄에서 선언된 변수 x만 남아있음

8. 사용한 변수 x는 1번 줄에서 선언된 변수를 나타냄

9. 1번 줄에서 선언된 변수 x의 상태 정보를 pop

 

'Study > Programming Language' 카테고리의 다른 글

[PL] Chapter 03. 언어 설계와 파서 구현  (0) 2022.04.24
[PL] Chapter 02. 구문법(Syntax)  (0) 2022.04.24
[PL] Chapter 01. 서론  (0) 2022.04.23
profile

Fascination

@euna-319

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!