가시성 규칙(Visibility Rules)
- 비공식적인 개념이다.
- 이름의 가시성을 결정하고 특정 블록에서 어떤 바인딩을 사용할 수 있는지 결정하는 규칙.
- 블록의 로컬 선언은 해당 블록과 해당 블록 내의 다른 모든 블록에 표시된다.
- 블록에 동일한 이름의 새 선언이 있는 경우 이 새 선언은 이전 선언을 숨긴다.
0: { int a = 1; 1: { int b = 2; 2: { int b = 3; int c = a + b; printf("%d\n", c); } 3: { int d = a + b; printf("%d\n", d); } } } |
- 블록 0~3이 있다.
- 현재 블록이 변경되면 환경도 변경된다.
- 따라서 동일한 이름이 다른 개체에 연결될 수 있다.
- 변수 c의 값 : 4
- 변수 d의 값 : 3
- 먼저 블록 0에 a가 선언되어 있으므로 블록 0~3 모두에서 볼 수 있다.
- 첫 번째 b는 블록 1에서 선언되므로 블록 1, 2, 3에서 볼 수 있다.
- 두 번째 b는 블록 2에서 선언되며 블록 2에서만 볼 수 있다.
- 블록 2의 첫 번째 b를 숨기므로 블록 2에서 b는 항상 두 번째 b를 나타낸다.
- 반면, 블록 3에서는 두 번째 b(블록 2의)가 표시되지 않는다.
- 블록 1의 첫 번째 b는 블록 3에서 볼 수 있으므로 d를 계산하는 데 사용된다.
- 따라서 c는 4이고 d는 3이다.
환경
- 위의 예시에서 변수 a가 글로벌 변수라고 가정해보자.
- 그러면 a는 모든 블록에 표시되며 글로벌 환경의 일부가 된다.
- 블록 1의 경우 a의 바인딩은 글로벌 환경이자 비로컬 환경이다.
- 로컬 환경의 이름은 내부 블록에 표시된다.
- 로컬 환경의 이름은 외부 블록에 표시되지 않는다.
- 로컬이 아닌 환경의 이름은 로컬 환경의 동일한 이름으로 숨겨진다.
- 예) 블록 2의 로컬 환경에서 첫 번째 b의 바인딩이 비활성화된다.
그래도 이것만으로는 부족하다.
- 우리가 논의한 가시성 규칙은 대략적인 큰 그림을 설명한다.
- 구체적이고 세부적인 규칙은 언어마다 다를 수 있다.
- 예를 들어 방금 설명한 경우는 Java에서는 유효하지 않다.
- 이전 예제는 C로 작성되었으며 오류 없이 작동한다.
- Java에서는 로컬변수 중복이 허용되지 않는다.
- 반면에 전역 변수를 재정의할 수 있다.
- 그러므로 우리는 각 프로그래밍 언어에 대한 구체적인 규칙을 이해해야 한다.
0: { int a = 1; 1: { int b = 2; 2: { int b = 3; // 오류가 발생 int c = a + b; printf("%d\n", c); } 3: { int d = a + b; printf("%d\n", d); } } } |
범위 규칙(Scope Rules)
- 블록과 관련된 다양한 환경에서 이름이 표시되는 방식을 대략적으로 설명한다.
- 본 포스트에서는 정적과 동적 관점에 대해 다룬다.
정적 vs. 동적
- 정적 범위 : 프로그램 자체의 구문 구조에만 의존
- 따라서 환경은 컴파일러에 의해 완전히 결정될 수 있다.
- 동적 범위 : 프로그램의 역방향 실행을 사용하여 바인딩을 결정
- 따라서 런타임 중에 결정될 수 있다.
정적 범위 규칙
- 정적 범위 규칙은 가장 가까운 중첩 범위의 규칙으로 간주될 수 있다.
- 세 가지 규칙
- 규칙 1. 블록에 대한 로컬 선언은 해당 블록의 로컬 환경을 정의
- 규칙 2. 이름이 블록 내에서 사용되는 경우 이 이름의 유효한 바인딩은 로컬 환경에 존재.
존재하지 않는 경우 가장 가까운 외부 블록에 존재 - 규칙 3. 블록 자체는 이름과 연관될 수 있으며 이러한 이름은 블록의 로컬 환경의 일부이다.
규칙 1. 지역 선언
- 지역적으로 선언된 변수는 지역 환경을 정의한다.
- 블록 1의 경우 이 블록에는 변수 b만 선언된다.
- 다른 변수는 표시되지 않거나 표시되지만 로컬 환경에는 포함되지 않는다.

규칙 2. 가장 가까운 중첩 범위
- 변수 a는 블록 3에서 참조된다.
- 단, 이 블록에는 a가 선언되지 않는다.
- 규칙 2에 따라 블록 1을 먼저 검색한다.
- 아직 찾을 수 없으므로 블록 0을 시도한다. → a가 여기에 선언되어 있다.
- 블록 2는 "중첩된" 블록만 검색하므로 건너뛰었다.

규칙 3: 블록에 할당된 이름
- Java 코드에서 메소드 이름 put, 매개변수 list, str는 실제로 블록 내부에 없다.
- 단, 로컬 환경에서는 사용 가능하다.
- 또한 로컬 환경의 일부이므로 외부 블록에는 표시되지 않는다.
- put()은 선언이 포함된 블록에 표시되는 프로시저이기 때문에 예외이다.
public static void put(List<String> list, String str) {
list.set(list.size()/2, str);
}
정적 범위의 장점
- 이러한 모든 정적 범위 규칙은 미리 정의되어 있으며 코드의 구문 구조에만 의존한다.
- 컴파일러는 사용된 이름의 모든 바인딩을 추론할 수 있다.
- 이 사실은 큰 이점을 제공한다.
- 프로그램에 대해 더 잘 이해할 수 있다.
- 컴파일러는 정확성 테스트를 수행할 수 있다.
- 컴파일러는 상당한 최적화를 수행할 수 있다.
동적 범위
- 프로그램의 특정 지점 P에서 이름 X의 유효한 바인딩,
- 가장 최근에 생성된 바인딩 X 이다.
- X는 P 지점에서 계속 활성화되어 있어야 한다.
Shell Script 1: x=1 2: function foo() { 3: echo $x; 4: x=2; 5: } 6: function bar() { 7: local x=3; 8: foo; 9: } 10: bar 11: echo $x |
- 정적 범위를 사용하는 경우
- 1행의 x는 전역 변수
- 함수 bar은 라인 10에서 호출
- 내부에서 foo를 호출
- foo 함수는 3행에서 1을 인쇄 → 1행의 x를 사용
- 그런 다음 x는 11번 라인에서 다시 인쇄 → x는 4번 라인에서 변경
- 따라서 2를 인쇄
- 동적 범위를 사용하는 경우 이 스크립트의 실제 출력
- 3(3번째 줄에 인쇄)
1(11번째 줄에 인쇄)
- 3(3번째 줄에 인쇄)
- 3행에서 이름 x의 가장 최근 바인딩은 7행에 있다.
- 따라서 3이 인쇄
- 11번째 줄에서 x의 가장 최근 바인딩(4번째 줄의 2)이 이미 사라졌다.
- 따라서 1이 인쇄
동적 범위의 장점
- 프로그램을 다시 작성하지 않고 프로그램의 동작을 더 쉽게 수정할 수 있다.
- 실제로 정적 범위 규칙 OOP를 사용하여 언어로 비슷한 작업을 수행할 수 있다.
- 데코레이터 패턴
- 그러나 코드를 쉽게 이해하기 어렵다.
코드 작성 function pizza() { echo "A pizza $toppings" toppings="" } function cheese() { toppings="$toppings+cheese" } function peperoni() { toppings="$toppings+peperoni" } |
코드 실행, 런타임 명령 #cheese pizza cheese pizza #peperoni pizza peperoni pizza #double cheese pizza cheese cheese pizza |
실행 결과 A pizza +cheese A pizza +peperoni A pizza +cheese+cheese |
'프로그래밍언어론' 카테고리의 다른 글
[프로그래밍언어론] 15. 정적 관리(Static Management) (0) | 2023.10.11 |
---|---|
[프로그래밍언어론] 14. 스택(Stack), 힙(Heap) (0) | 2023.10.11 |
[프로그래밍언어론] 12. 블록(Block), 환경(Environment) (0) | 2023.10.10 |
[프로그래밍언어론] 11. 이름(Name), 바인딩(Binding) (1) | 2023.10.10 |
[프로그래밍언어론] 10. 추상 구문 트리(AST)를 EBNF로 변환 (1) | 2023.10.10 |
가시성 규칙(Visibility Rules)
- 비공식적인 개념이다.
- 이름의 가시성을 결정하고 특정 블록에서 어떤 바인딩을 사용할 수 있는지 결정하는 규칙.
- 블록의 로컬 선언은 해당 블록과 해당 블록 내의 다른 모든 블록에 표시된다.
- 블록에 동일한 이름의 새 선언이 있는 경우 이 새 선언은 이전 선언을 숨긴다.
0: { int a = 1; 1: { int b = 2; 2: { int b = 3; int c = a + b; printf("%d\n", c); } 3: { int d = a + b; printf("%d\n", d); } } } |
- 블록 0~3이 있다.
- 현재 블록이 변경되면 환경도 변경된다.
- 따라서 동일한 이름이 다른 개체에 연결될 수 있다.
- 변수 c의 값 : 4
- 변수 d의 값 : 3
- 먼저 블록 0에 a가 선언되어 있으므로 블록 0~3 모두에서 볼 수 있다.
- 첫 번째 b는 블록 1에서 선언되므로 블록 1, 2, 3에서 볼 수 있다.
- 두 번째 b는 블록 2에서 선언되며 블록 2에서만 볼 수 있다.
- 블록 2의 첫 번째 b를 숨기므로 블록 2에서 b는 항상 두 번째 b를 나타낸다.
- 반면, 블록 3에서는 두 번째 b(블록 2의)가 표시되지 않는다.
- 블록 1의 첫 번째 b는 블록 3에서 볼 수 있으므로 d를 계산하는 데 사용된다.
- 따라서 c는 4이고 d는 3이다.
환경
- 위의 예시에서 변수 a가 글로벌 변수라고 가정해보자.
- 그러면 a는 모든 블록에 표시되며 글로벌 환경의 일부가 된다.
- 블록 1의 경우 a의 바인딩은 글로벌 환경이자 비로컬 환경이다.
- 로컬 환경의 이름은 내부 블록에 표시된다.
- 로컬 환경의 이름은 외부 블록에 표시되지 않는다.
- 로컬이 아닌 환경의 이름은 로컬 환경의 동일한 이름으로 숨겨진다.
- 예) 블록 2의 로컬 환경에서 첫 번째 b의 바인딩이 비활성화된다.
그래도 이것만으로는 부족하다.
- 우리가 논의한 가시성 규칙은 대략적인 큰 그림을 설명한다.
- 구체적이고 세부적인 규칙은 언어마다 다를 수 있다.
- 예를 들어 방금 설명한 경우는 Java에서는 유효하지 않다.
- 이전 예제는 C로 작성되었으며 오류 없이 작동한다.
- Java에서는 로컬변수 중복이 허용되지 않는다.
- 반면에 전역 변수를 재정의할 수 있다.
- 그러므로 우리는 각 프로그래밍 언어에 대한 구체적인 규칙을 이해해야 한다.
0: { int a = 1; 1: { int b = 2; 2: { int b = 3; // 오류가 발생 int c = a + b; printf("%d\n", c); } 3: { int d = a + b; printf("%d\n", d); } } } |
범위 규칙(Scope Rules)
- 블록과 관련된 다양한 환경에서 이름이 표시되는 방식을 대략적으로 설명한다.
- 본 포스트에서는 정적과 동적 관점에 대해 다룬다.
정적 vs. 동적
- 정적 범위 : 프로그램 자체의 구문 구조에만 의존
- 따라서 환경은 컴파일러에 의해 완전히 결정될 수 있다.
- 동적 범위 : 프로그램의 역방향 실행을 사용하여 바인딩을 결정
- 따라서 런타임 중에 결정될 수 있다.
정적 범위 규칙
- 정적 범위 규칙은 가장 가까운 중첩 범위의 규칙으로 간주될 수 있다.
- 세 가지 규칙
- 규칙 1. 블록에 대한 로컬 선언은 해당 블록의 로컬 환경을 정의
- 규칙 2. 이름이 블록 내에서 사용되는 경우 이 이름의 유효한 바인딩은 로컬 환경에 존재.
존재하지 않는 경우 가장 가까운 외부 블록에 존재 - 규칙 3. 블록 자체는 이름과 연관될 수 있으며 이러한 이름은 블록의 로컬 환경의 일부이다.
규칙 1. 지역 선언
- 지역적으로 선언된 변수는 지역 환경을 정의한다.
- 블록 1의 경우 이 블록에는 변수 b만 선언된다.
- 다른 변수는 표시되지 않거나 표시되지만 로컬 환경에는 포함되지 않는다.

규칙 2. 가장 가까운 중첩 범위
- 변수 a는 블록 3에서 참조된다.
- 단, 이 블록에는 a가 선언되지 않는다.
- 규칙 2에 따라 블록 1을 먼저 검색한다.
- 아직 찾을 수 없으므로 블록 0을 시도한다. → a가 여기에 선언되어 있다.
- 블록 2는 "중첩된" 블록만 검색하므로 건너뛰었다.

규칙 3: 블록에 할당된 이름
- Java 코드에서 메소드 이름 put, 매개변수 list, str는 실제로 블록 내부에 없다.
- 단, 로컬 환경에서는 사용 가능하다.
- 또한 로컬 환경의 일부이므로 외부 블록에는 표시되지 않는다.
- put()은 선언이 포함된 블록에 표시되는 프로시저이기 때문에 예외이다.
public static void put(List<String> list, String str) {
list.set(list.size()/2, str);
}
정적 범위의 장점
- 이러한 모든 정적 범위 규칙은 미리 정의되어 있으며 코드의 구문 구조에만 의존한다.
- 컴파일러는 사용된 이름의 모든 바인딩을 추론할 수 있다.
- 이 사실은 큰 이점을 제공한다.
- 프로그램에 대해 더 잘 이해할 수 있다.
- 컴파일러는 정확성 테스트를 수행할 수 있다.
- 컴파일러는 상당한 최적화를 수행할 수 있다.
동적 범위
- 프로그램의 특정 지점 P에서 이름 X의 유효한 바인딩,
- 가장 최근에 생성된 바인딩 X 이다.
- X는 P 지점에서 계속 활성화되어 있어야 한다.
Shell Script 1: x=1 2: function foo() { 3: echo $x; 4: x=2; 5: } 6: function bar() { 7: local x=3; 8: foo; 9: } 10: bar 11: echo $x |
- 정적 범위를 사용하는 경우
- 1행의 x는 전역 변수
- 함수 bar은 라인 10에서 호출
- 내부에서 foo를 호출
- foo 함수는 3행에서 1을 인쇄 → 1행의 x를 사용
- 그런 다음 x는 11번 라인에서 다시 인쇄 → x는 4번 라인에서 변경
- 따라서 2를 인쇄
- 동적 범위를 사용하는 경우 이 스크립트의 실제 출력
- 3(3번째 줄에 인쇄)
1(11번째 줄에 인쇄)
- 3(3번째 줄에 인쇄)
- 3행에서 이름 x의 가장 최근 바인딩은 7행에 있다.
- 따라서 3이 인쇄
- 11번째 줄에서 x의 가장 최근 바인딩(4번째 줄의 2)이 이미 사라졌다.
- 따라서 1이 인쇄
동적 범위의 장점
- 프로그램을 다시 작성하지 않고 프로그램의 동작을 더 쉽게 수정할 수 있다.
- 실제로 정적 범위 규칙 OOP를 사용하여 언어로 비슷한 작업을 수행할 수 있다.
- 데코레이터 패턴
- 그러나 코드를 쉽게 이해하기 어렵다.
코드 작성 function pizza() { echo "A pizza $toppings" toppings="" } function cheese() { toppings="$toppings+cheese" } function peperoni() { toppings="$toppings+peperoni" } |
코드 실행, 런타임 명령 #cheese pizza cheese pizza #peperoni pizza peperoni pizza #double cheese pizza cheese cheese pizza |
실행 결과 A pizza +cheese A pizza +peperoni A pizza +cheese+cheese |
'프로그래밍언어론' 카테고리의 다른 글
[프로그래밍언어론] 15. 정적 관리(Static Management) (0) | 2023.10.11 |
---|---|
[프로그래밍언어론] 14. 스택(Stack), 힙(Heap) (0) | 2023.10.11 |
[프로그래밍언어론] 12. 블록(Block), 환경(Environment) (0) | 2023.10.10 |
[프로그래밍언어론] 11. 이름(Name), 바인딩(Binding) (1) | 2023.10.10 |
[프로그래밍언어론] 10. 추상 구문 트리(AST)를 EBNF로 변환 (1) | 2023.10.10 |