3.5 The Meaning of Names within a Scope
:이전까지 다뤘던 내용은 이름과 객체간의 관계는 일대일 관계였음
Aliases
두 개 또는 그 이상의 이름이 같은 오브젝트를 가리킴 (별칭)
한 개 object에 이름 여러 개, 같은 공간에 접근함
typedef union {
int mem1;
int mem2;
double mem3;
} UBox;
double sum, sumOfSquares;
...
void accumulate (double& x){
sum += x;
sumOfSquares += (x * x);
}
accumlatue(sum);
Overloading
한 개의 이름이 여러 개의 오브젝트를 나타냄
- 다형성과 연관 있음
- C : + 연산자가 여러 가지 다양한 함수로 사용됨 (double, int 타입 등등)
- 위의 경우 month, print_base 중 어디 것을 의미하는지 모호함
- Symbol table 에서는 Lookup 함수를 사용해서 특정 이름 찾음
- 가능성이 있는 것을 리스트로 나타내고, context에 맞춰서 선택 결정한다.
- 위의 상황처럼 모호하면 오류 발생
- Modula-3 & C#: type name을 앞에 명시해줘야함
- explicit하게 month'(oct) 명시
- C 언어: 오버로딩 매우 제한됨
- C++ → 스코프를 새로 만듦: 감춰져서 안보이게 됨
- enum constant는 구별이 가능해야 한다 → enum class 를 사용하면 사용 가능
- month::OCT와 같이 연산 씀
- 갯수, 타입이 다른 것을 가질 때 오버로딩 가능
- Built-in Operators는 오버로딩 가능
- Ada 에서 prefix를 사용해야하는데 Infix사용할 수 있도록 함
- C++,C#: operator+(A,B) or A.operator+(B)
- Syntax sugar: 문법적으로 사용하기 쉽도록 (A+B)
- Haskell : 연산자 정의 가능
- Overloadig: 같은 이름을 가진 여러 개 의 오브젝트
Coercion
컴파일러가 자동으로 형 변환
(void f(double a), f(2) → (정수)다른 타입 넘김)
Polymorphism
다형성, 하나의 서브 루틴에 다양한 인자 타입을 허락함
void f(Object o)
: 모든 클래스 전달 가능 (부모 클래스로부터 상속받은 자식 클래스)
1. Overloading은 같은 이름의 여러 개의 객체를 프로그래머에게 허용, 문맥에 근거해서 명확히 구분 짓는다. 인자의 갯수, 타입이 다른 것을 가질 때 오버로딩이 가능하다.
2. Coecion은 **컴파일러**가 예측되어지는 타입으로 자동 변환하는 것을 허용한다
3. Polymorphism은 한 개의 서브 루틴에 여러 개의 타입의 argument을 가지는 것을 허용한다. (interface를 구현하거나 상속하는 object)
- print: 화면에다 출력함, 다양한 타입을 받을 수 있음
- overloading: 여러 개 함수 만들면 됨 (타입별)
- print(my_object)타입이 있는지 확인 → 없으면 오류를 내거나 my_object의 부모 클래스를 찾음 or coercion으로 인해 자동 형변환
- java: toString()
- 많은 타입에서 toString() 지원
- 부모 함수 호출 처럼 보이지만 overriding 한 함수 호출
3.6 The Binding of Referencing Environments
Referencing Environments
: 프로그램을 실행시키다가 한 순간에 멈췄을 때 유효한 함수 및 변수 즉 바인딩
- 스코프 룰: 프로그램 내에서 지정된 statement의 referencing environment에 의해 결정됨 (그때 사용할 수 있는 함수, 변수)
- Static scope rules: 프로그램을 실행하지 않아도 알 수 있음
- Dynamic scope rules: 프로그램을 실행시켜봐야 알 수 있음
- 함수가 파라미터로 전달하는 경우? → 어떤 스코프를 사용해야 하는지
- 함수 자체가 호출되는 시점 (1. 전달할 때 reference is first created, 2. 그 함수를 호출할 때: routine is finally called)
- Deep binding: reference가 만들어질 때 → 구조적으로 가장 상위의 것
- Shallow binding: 서브 루틴이 호출될 때 → 가장 최근에 호출된 곳에서 값 가져옴
- 파라미터로 전달된 함수가 late binding (호출되기 직전)
- threshold → 어디선가 가져와야하는 변수
- 다이나믹 스코프: line_length를 print_selected_records 내에서 만들고 값을 지정해줘야함 → print_routine 에서 사용
- print_routine 사용 직전: shallow (다이나믹 스코프는 기본 shallow)
- threshold 라는 변수를 print_selected_record가지고 있다면
- main에서 지정하더라도 덮어씌게 되니까 deep binding이 적합함
3.6.1 Subroutine Closures
- Deep binding 은 함수 전달 직전 bundling 해서 보내야함
- closure: 배낭을 매고 있는 함수, 함수 자체와 함수에 사용할 Reference environment(그 당시의 값이 아니라 그 당시의 reference를 의미)를 포함함
- dynamic scoping의 default: shallow binding
- dynamic scoping → deep binding 필요하는 경우 있음
- 빌트인 함수를 통해서 사용 가능
- 호출할 함수를 인자로 전달 받고 클로저를 반환해줌 → 클로저를 파라미터로 전달 가능
- static scoping → deep binding(전달할 때) 만 사용
- shallow binding 일때는 지역변수로 지정하면 다른 함수에서는 안보이는 영역이기때문
- static scoping 은 shallow binding 은 사실 의미가 없음 → 덮어씌여지니까 (즉, 디폴트는 deep binding )
- 만약 오브젝트가 현재 실행 중인 서브루틴의 로컬일 경우, 서브루틴이 직접적으로 호출되었는지 혹은 클로저를 통해서 호출되었는 지는 중요한 것이 아님
- 두 경우 모두 다 로컬 오브젝트가 서브루틴이 시작할 때 생성된 것이기 때문임
- 지역 변수는 안보이는 영역이므로 영향 받지 않음
- 전역 변수 → 한 개 이상의 인스턴스가 존재할 수 없음 → 똑같은 값 유지: g_x
- Deep: 함수 넘길때 → 1
- Shallow: 함수 호출 직전 → 2
- 자바 스크립트의 클로져
- localVariable은 testClosure()의 지역 변수로, testClosure()가 종료되면 함께 사라지는 변수지만 이 경우에서 function()이 반환 될때 localVariable도 함께 묶여서 반환되도록 한다.
3.6.2 First-Class Values and Unlimited Extent
First- Class
ex) C# / 제한 존재 - C, C++, imperative 언어, Fortan, Modula-2, Modula-3, Ada95
프로그래밍 언어에서 세 가지 조건 만족
- (함수, 변수) value를 인자로 전달 가능
- 서브 루틴으로부터 반환 받기 가능
- 변수에 할당 가능
- outlive: 함수 리턴하고 나면, 함수 벗어난 영역에서 사용 로컬 오브젝트가 destroy된 후, x에 대한 내용도 가지고 있음 → 로컬 오브젝트가 unlimited extent → 라이프 타임은 더 길다. → garbage 콜렉터에서 더 이상 사용하지 않는다고 판단하면 reclaim → heap (끝날 때까지 들고 있음) limited → stack
Second-Class
ex) 대부분 imperative 언어
Third-Class
ex) Ada
3.6.3 Object Closures
- nested subroutines 없을 때는 서브루틴 context와 함께 전달하는 것을 못할 수 있음
- object-oriented
-
함수의 객체화 (static 이용 → 제한적)
-
객체에 있는 함수
-
이름이 없는 함수 사용
-
C++
-
object인데 함수 역할: functor
3.6.4 Lambda Expressions
- 람다 표현은 코드 재사용성과 일반화를 도움
- 즉석에서 새로운 함수를 만들어 냄
- call back: 메인 프로그램으로 라이브러리가 콜백 하는 것을 허용
-
먼저 만들어진 일반화된 함수에 Compare에서 불러서 씀
class AgeComparator implements Comparaotr<Person> {
public int compare(Person p1, Person p2) {
return Integer.compare(p1.age, p2.age);
}
}
Person[] people = …
…
Arrays.sort(People, new AgeComparator());
Arrays.sort(People, (p1, p2) -> Integer.compare(p1.age, p2.age))
- filter
- predicate: 2021년 노래 뽑기 등등 톨렉션의 요소를 필터링
- event handler
Lambda Expression in C++
- 50이하 원소 출력
- 벡터의 모든 요소에 대해 마지막 인자의 함수를 적용함
- k가 밖에 정의되어 있을 때
- = : 복사본 사용 → 오래 걸림, deep binding을 해도 영향을 주지 않음 (함수 호출 전)
- &: 같은 공간 사용 → 시간 빠름, 밖에서 값이 바뀌면 값이 바뀔 수도
Lambda Expression in JAVA
- Comparator를 구현한 클래스의 배열을 받아서 정렬해줌 (Arrays.sort)
- 함수 하나만 가지고 있는 인터페이스(fuctional interface)
- 를 넘길때 람다를 대체할 수 있음
- interface를 사용하는 익명 클래스 생성함 → 객체 생성(전달)
- 파라미터, 리턴 타입 같아야 함
3.7 Macro Expansion
- 어셈블리 언어 프로그래머들은 반복되는 코드를 잘 찾을 수 있도록 매크로 사용함
- 함수로 만들면 느리지만 매크로는 그대로 바꿔치기 때문에 효율적일 수도
- 그대로 치환 → 산술 연산에서 우선 순위로 인해 원하는 결과가 나오지 않을 수도
- 겹치는 문제 발생
- 수도 없이 증가
- 최근에는 지양하는 추세
- named constants 지원
- const int a = 3; : type-safe, 구현 쉬움
- inlined subroutine
- 치환이지만 타입 체킹함
- 치환에서 속도가 비슷함
- Scheme, Comon Lisp → max 문제 여전히 존재