수정입니다

Name, Bindings, and Scope(2) - Scope 본문

전공/프로그래밍언어론

Name, Bindings, and Scope(2) - Scope

nongdamgom 2023. 12. 28. 12:43

Scope

  • the range of statements over which it is visible
  • local variable -> 어떤 프로그램 or 블록 내에서 정의된 변수
  • nonlocal variable -> 그 지역 내에서 정의되지 않았는데 visible한 변수
  • global -> non-local의 special category

Static Scope

  • 두가지 유형 - nested subprogram(함수안에 함수 정의) 허용 or 비허용
  • 작성 위치에 따라 scope가 달라짐
  • Search process : 변수가 정의된 부분을 찾아야함. 지금 내 지역부터, 더 큰 지역.. 계속 찾을 때까지 탐색
  • static ancestor : subprogram을 포함하고 있는 모든 program 전부
  • static parent : 나(subprogram)을 선언한 subprogram (ancestor 중 가장 nearest 한 것)
  • nested subprogram 허용언어 - Ada, JavaScript, Common Lisp, Scheme, Fortran 2003+, F#, Python)
  • C 기반 언어들은 허용하지 않는다.
  • 일단 여기선 허용된 언어 기반으로 생각
void func1(){ void func2(){ int x = 3; // hidden func3(); } void func3(){ int y = x; } int x = 5; // real use func2(); }

func2와 func3의 ancestor는 func1이다

func2에서 x를 정의하고, func3을 호출했지만, 실제로 둘은 아무 관계도 아님

이때 func2의 int x = 3은 int x = 5 에 의해서 은폐된다(hidden)

Blocks

  • a method of creating static scopes inside program units(from ALGOL 60)
  • C, C++ 에서는 허용되지만, C#, java 에선 허용 x
  • let 이라고 명시된 키워드로 보통 사용
  • let construct 2가지로 구성됨

--> 1. binds name to value

--> 2. uses the names defined in the first part

 

/* In Scheme */ (LET ( (top (+ a b)) //binding (bottom (- c d)) // binding (/ top bottom) //using )

 

/* In ML */ let val top = a + b //binding val bottom = c - d // binding in top / bottom //using end;

이 구조는 (a + b) / (c + d)의 식을 계산하고 그 값을 반환한다.

+ In F#

let left_side = expression // left_side is either a name or a tuple pattern

Declaration order

  • C99, C++, Java, C# 은 변수 선언이 프로그램 어디든지 나타나는 것을 허용

C99, C++, Java

--> 변수 사용하기 전에 반드시 선언 먼저 해야됨

--> scope는 선언된 순간부터 그 block 끝까지

C#

--> 변수 사용전 반드시 선언 먼저 해야하는 것은 맞음

--> scope가 선언된 순간부터가 아니라 선언된 block 전체임

using System; class HelloWorld { static void Main() { int a = 5; int b = 0; if(a == 5){ b = a; } } }

--> main.cs(13,9): warning CS0219: The variable `b' is assigned but its value is never used

--> 변수의 scope가 block 전체라서 if문 내에서 변수를 선언을 안하면 밖에다 선언해도 못씀

C++, Java, C#

  • for문 state안에 선언할 수 있음

Global scope

  • C, C++, PHP, Python 은 일련의 함수 정의들로 구성된 프로그램 구조를 허용함

--> 이러한 구조에서 변수 정의들이 함수 외부에서 올 수 있음

  • C, C++ 은 전역 데이터의 선언과 정의를 가짐

--> declaration(선언) : 타입이나 다른 속성들을 특정하지만, 기억공간 할당x

--> definition(정의) : 속성들을 특정하고 기억공간을 할당 o

--> 함수 정의 외부에 위치한 변수 선언은 그 변수가 다른 파일에 정의되어 있다는 것을 말함

/*file 2.c*/ extern int b; b = b+2;
/*file 1.c*/ int b = 1; int sum(){ int a; }

file1 에서 정의된 b를 file1 에서 extern 키워드를 쓰며 선언

-> file2에서 visible 해진다

  • PHP

--> 전역 변수 A를 함수 내부에서 쓰려면 내부에서 $global A 라고 해줘야함

--> 만약에 A와 같은 이름이 함수 내부에 존재한다면 $GLOBALS 배열을 이용해서 새로운 변수 지정

==> PHP에서 전역 변수는 선언 후 부터 프로그램의 끝까지 scope를 가지지만, 중간에 함수 정의들이 끼어 있다면, 그 함수 정의는 scope에서 빠지게 된다.

  • Python
day = "Monday" def tester(): global day print ("The global day is : ", day) day = "Tuesday" print ("The new value of day is : ", day) tester()

--> day = "Tuesday" 문장이 없다면, 전역변수를 함수 내에서 그냥 참조 가능

--> day = "Tuesday" 문장이 생겨서, 함수 내에서 변수를 선언하기 전에 print ("The global day is : ", day)를 출력 하라고 시킨 꼴이 되는거라 에러가 발생

The global day is : Monday

The new value of day is : Tuesday

출력결과

Evaluation of static scoping

  • works well in many situations
  • Problems :

--> too much access is possible

--> 프로그램이 계속 develop될 때, 전역 변수 때문에 원치 않는 설계를 하게 될 수가 있음

Dynamic Scope

  • based on calling sequences of program units not their textual layout
  • temproal(dynamic) vs spatial(static)
  • searching back through the chain of subprogram calls that forced execution to this points
function big(){ function sub1(){ var x = 7; } function sub2(){ var y = x; } var x = 3; }

함수의 호출부분을 제외한 코드임

만약에 big -> sub1 -> sub2 로 calling sequence가 이루어졌다면

function big(){ function sub1(){ var x = 7; sub2(); } function sub2(){ var y = x; } var x = 3; sub1(); }

지역프로시저 sub2부터 그 호출자 sub1으로 진행되고, sub1에서 x에 대한 선언이 발견되었으므로

이때 dynamic scoping : sub1의 x는 sub1의 x를 참조한다( x = 7 )

이때 static scoping : sub2의 parent가 big이므로, big의 x를 참조한다( x = 3)

만약 그냥 big -> sub2라면 dynamic과 static scope는 같을 것이다.

function big(){ function sub1(){ var x = 7; } function sub2(){ var y = x; } var x = 3; sub2(); }

Evaluation of Dynamic Scoping

  • 장점 : 편하다
  • 단점

- 내가 호출한 모든 subprogram이 실행되고 있으면 계~속 지역변수가 노출된다(reliability가 낮아짐)

- Impossible to statically type check

- readability가 낮아짐 (calling sequence를 인간이 눈으로 찾기 힘들다)

Scope and Lifetime

  • 둘은 관련 있어 보이지만 사실은 다른 컨셉
void printheader(){ // 이 함수 내에서 sum은 visible 하진 않지만, lifetime은 살아있음 } void compute(){ int sum; printheader(); } compute();

scope -> visibility

lifetime -> storage binding

  • sum 은 printheader 함수 내에서 사용(참조?)할 수는 없지만 binding은 어딘가의 공간에 계속 살아있다...

Referencing environments

  • statement에서 볼 수 있는 모든 name들의 집합
  • static scope

지역변수 + 모든 닫힌({} 이런거) scope내의 모든 visible한 변수

  • dynamic scope

지역변수 + 모든 active(활성화)되어 있는 subprogram 내의 모든 visible한 변수

ex) static scope

g = 3; def sub1(): a = 5 b = 7 # ---- (1) reference environment : Local(a, b) , for referencing global g(not assignment) def sub2(): global g c = 9 # ------- (2) reference environment : Local c, galbal g (referening + assignment) def sub3(): nonlocal c g = 11 # --------- (3) reference environment : nonlocal c, Local g

** 이게 교재 코든데 뭐가 이상함 sub1 scope가 sub3의 ancestor가 아니라는데 sub1 밑에 sub2 밑에 sub3가 정의가 돼 있는데 왜 아닌지 모르겠고 설명이 sub1 영역이 sub 3보다 높은 수준이긴한데 덜 깊숙하게 중첩되어 있다고 그래서 아니라는데 아니애초에 파이썬에서 indentation을 저렇게 애매하게 설정할 수가 없는데 뭐지 밑에 설명 보면 sub1이 실행중이 아닐 때 sub3이 실행중 일 수 있어서 뭐 sub3이 sub1변수에 접근하는게 안된다는데 이게 sub1 밑에 sub3가 중첩됐을 경우에 이렇게 될 수가 있나? 이상함 아무튼 근데 말하고자 하는 바는 어쨌든 sub3랑 sub1이랑 관계없다는거 같아서 걍 indentation빼고 생각함

g = 3; def sub1(): a = 5 b = 7 // -- (1) referencing environment : Local(a, b) , for referencing global g(not assignment) def sub2(): global g c = 9 // ----------(2) referencing environment : Local c, galbal g (referening + assignment) def sub3(): nonlocal c g = 11 // --------- (3) referencing environment : nonlocal c, Local g

아무튼 이렇게 생각하면 깔끔함,,

함수를 호출하지 않아도 알 수 있는 참조 환경 == static scope

ex) dynamic scope

void sub1() { int a, b; // ----------(1) referencing environment : local a,b + main d (main c, sub2 b is hidden) } void sub2() { int b, c; // ----------(2) referencing environment : local b,c + main d (main c is hidden) sub1(); } void main() { int c, d; // ----------(3) referencing environment : Local c, d sub2(); }

call stack 그대로 따라가면서 영역 결정, 은폐시키는 변수에 대한 선언 가능

Named constant

- 딱 한번만 binding 되는 변수

- 장점 : readability and modifiability

--> 프로그램 매개변수로써 사용할 수 있음

만약에 myArray[10] 일 때, 이 array 사이즈를 그냥 처음부터 len = 10; 이렇게 박아버리면 나중에 이 10 이라는 숫자가 바뀌게 됐을 때 코드 쫓아가면서 하나하나 바꿀 필요 없이 len만 바꾸면 돼서 편하다

- Language

C++, Java : 어느 타입이든 가능, dynamic binding

C# : 두가지 존재

  • const : compile time에
  • readonly : dynamical binding

// end of chapter 5