프로그래밍언어

[프로그래밍언어]Subprograms

추상화에는 두 가지 종류가 있습니다.

Process abstraction

Data abstraction(이는 여기서는 다루지 않을 내용입니다.)

우리가 메인함수에서 프로세스를 구성해서 f1(a,b)라는 subprogram을 호출했을 때 f1()이라는 실제 실행 함수는 호출한메인함수 프로세스를 추상화하게 됩니다. 그리고 다시 메인함수에서 f1()을 호출해서 재사용하는 것 또한 추상화입니다.

모든 subprogram은 entry point을 가집니다.

subprogram을 호출하는 애를 calling program(caller)라고 하고 subprogram이 실행되는 동안에는 멈춰있습니다.

 

C와 C++에서 함수 정의를 prototype이라고 합니다.

caller에서 subprogram을 호출하는 f1(a,b)에서 a와 b는 actual parameter라고 합니다. 이는 value나 address를 나타내줍니다. 그리고 subprogram 실제함수 f1()안에서의 a와 b는 formal parameter라고 합니다. 이는 dummy variable입니다.

이 둘은 서로 상응해서 동작합니다.

이때 Parameter에는 Positional parameter과 Keyword parameter가 존재하는데, positional은 actual parameter가 formal parameter로 포지션에 의해 바인딩이 이루어집니다. Keyword는 순서에 상관없이 parameter가 등장할 수 있어서 상응 에러가 없다는 장점이 있지만 formal parameter의 이름을 기억해야한다는 단점이 있습니다.

 

Default parameter

C++, Python, Ruby, PHP 등에서 formal parameter는 default values를 가질 수 있습니다. 즉, 아무 actual parameter가 패싱되지 않는다는 뜻이죠.

다음은 C++코드로 매개변수 score3=60이 default parameter입니다.

이때 중요한것은 항상 마지막에 위치해야한다는 것입니다

예를 들어 sub(int score1, int score3=60, int score2)라고 쓰면 에러입니다! 

#include<iostream>
void sub(int score1, int score2, int score3=60){
	cout<<score1<<endl;
    cout<<score2<<endl;
    cout<<score3<<endl;
}
    
int main(void){
	sub(80, 70, 90);
    sub(70, 50); //sub(70,50,60)과 동일!! 60이 들어간 것과 마찬가지.
    
    return 0;
}

그리고 가변개수의 parameter를 허용합니다. 아래의 파이썬 코드처럼 가변개수의 매개변수를 total()이 가집니다. 

def total(*numbers):
	tot=0
    for n in numbers:
    	tot+=n
    return tot
t=total(1,2)//가변개수
print(t)
t=total(1,5,2,6)//가변개수
print(t)

 

Procedures and Functions

subprogram은 두 가지 카테고리가 있습니다.

Procedures와 Functins. Functions는 리턴값이 있지만 procedures는 없습니다. functions는 매개변수 또는 함수 외부에서 정의된 변수를 수정하지않아서 side effects가 없습니다. 순수한 함수 모델은 함수외부에서 정의한 변수를 수정하지 않습니다. 

 

Local Referencing Environments

지역변수는 stack-dynamic일 수 있습니다.

장점: 재귀허용, subprogram들이 메모리 공유

단점: 할당, 해제, 초기화시간, indirect addressing한 것, historysensitive하지않음

C++, Java, Python ,C#은 stack-dynamic만 유일하게 있습니다.

 

또한 지역변수는 static할 수 있습니다. 

변수가 static하면 subprogram종료후에도 유지하여 프로그램종료까지 메모리 유지합니다. 

위의 stack-dynamic과 장점과 단점을 반대로 생각하시면 됩니다.

C언어는 stack-dynamic하지만 static으로도 선언이 가능합니다.

C언어를 사용해보신 분들은 모두 static변수가 어떻게 작동하는지 보셨을 겁니다.

#include<stdio.h>
void func(void);
int main(void){
	for(int i=0,i<3,i++){
		func();
    }
return 0;
}
void func(void){
	static int n1=0;//static local variable
    int n2=0;
    n1++, n2++;
    printf("static:%d, local:%d\n",n1,n2);
}

결과값은 이와 같이 로컬변수n2는 int n2=0에서 계속 초기화되지만

static로컬변수 n1은 프로그램 종료까지 계속 그 메모리가 유지됩니다.

static:1, local:1
static:2, local:1
static:3, local:1

변수들에 대한 stack-dynamic과 static을 포함한 4가지 종류는 이 포스트를 자세히 참고하시길 바랍니다.

https://hidemasa.tistory.com/31

 

[프로그래밍언어] Names, Bindings, and Scopes

명령형 언어는 폰노이만 아키텍처의 추상화입니다. 메모리에서 명령어와 데이터를 저장해서 기계메모리셀의 언어에서의 추상화(변수)가 이루어지죠. RAM에서 CPU로 load(fetch), CPU에서 RAM으로 store�

hidemasa.tistory.com

Parameter Passing Methods(중요!!)

In mode

formal parameter가 상응하는 actual parameter로 부터 data를 받는다.

이를 pass by value라고 합니다.

Out mode

formal parameter가 상응하는 actual parameter로 data를 보냅니다.

이를 pass by result라고 합니다.

Inout mode

formal parameter가 위의 두 가지를 모두 하는 경우입니다.

이를 pass by reference라고 합니다.

이 세가지 경우를 자세히 살펴보도록 하겠습니다.

매개변수전달로 데이터값이 전달되는 경우의 모델은 두 가지입니다.

1. actual value가 복사되는 것. (caller로든, callee로든, 둘다로든) 

실제 값이 복사되는 것으로, a->a 했을 때 여분 공간이 더 필요합니다.

 

2. access path가 전달되는 것 (simple pointer나 reference) 

a<-참조 

a의 주솟값을 전달하여 참조하는 것으로 여분공간이 필요하지않습니다.

 

 Pass-by-Value(In Mode)

1번의 실제값이 복사되는 경우입니다. 

장점: linkage cost와 access time 모두에 있어 빠릅니다.

단점: physical move로 여분 공간이 필요합니다. (두 번 저장이 됨) 

 

actual parameter의 사본이 생성되는 것으로, actual parameter의 수가 많을 경우 비용 문제가 있습니다.

actual parameter는 절대로 변경되지 않습니다. 왜냐하면 값이 write-protected cell안에 있어 read-only합니다.

이를 예시로 보여드리겠습니다.

call-by-value로 swap함수를 실행한 모습입니다.

세번째 줄 출력값인 actual parameter_ num1 num2은 swap()호출 전과 값이 똑같습니다. 

swap 함수 내에서만 값이 변경되어 외부에는 영향을 주지 않은 것입니다. 

swap을 제대로 하고 싶다면 call-by-reference를 해주어야 합니다.

call-by-reference한 모습입니다. swap함수 내에서 함수외부의 actual parameter값도 변경(교환)이 이루어졌습니다. 

main의 actual parameter를 참조하여 값변경하였기 때문입니다.

 

Pass-by-Result (Out Mode)

formal parameter가 상응하는 actual parameter로 data를 보냅니다. 

parameter가 passed-by-result되면 어떤 값도 subprogram으로 전달이 되지 않습니다. 

상응하는 formal parameter가 지역변수로서, 

physical move로 control이 caller에 리턴된 후에 

value가 caller의 actual parameter로 전달됩니다. 

이는 여부공간과 복사작업을 필요로합니다.

 

그런데 이는 어느 시점부터 바인딩 되느냐에 따라 값이 달라지는 문제가 발생할 수 있습니다. 

actual parameter가 subprogram메소드 시작과 끝 사이에 변합니다. 

그러므로 매개변수를 리턴하기 위해 주소 사용 시점을 명시해야 합니다.

 

Pass-by-Value-Result (inout Mode)

formal parameter가 상응하는 actual parameter로부터 data를 받고 보내는 작업을 둘 다 합니다. 

지금은 거의 사용되지 않고 있습니다. 그냥 이런게 있다 정도로 넘어가면 될 것 같습니다.

 

Pass-by-Reference(Inout Mode)

caller는 actual parameter의 access path(접근경로)를 callee에게 pass합니다.(sharing actual parameters)  

장점: 패싱과정이 효율적입니다. 복사시간도 없고, 복사메모리공간도 필요없습니다.

단점: slower accesses. Indirect addressing이기 떄문에 

포인터를 사용하므로 collisions이나 원치 않던 aliases(두 변수가 같은 메모리 접근) 과 같은 부작용이 있습니다.

그래서 aliasing이나 같은 데이터위치를 다양한 이름으로 사용하는 것을 자제해야합니다.

Pass-by-Name (Inout Mode)

이름 전달 방식을 사용하는 subprogram동작은 formal parameter의 이름을 actual parameter의 이름으로 대치하여 실행한 결과와 같습니다. 프로그램을 작성하기 어렵고 구현이 어려워 최근 대부분 언어에선 사용하지 않습니다.

그냥 이런 개념이 있다 정도로 넘어가면 될 듯합니다.

 

Implementing Parameter-Passing Methods

자, 이제부터는 위의 매개변수패싱방식들을 실행해보겠습니다.

보통 대부분 parameter communitcation은 run-time stack에서 이루어집니다.

Pass-by-reference가 가장 단순한 실행입니다. 그냥 주소만 스택에 위치하면 되니까요.

4가지의 파라미터패싱방식을 그림으로 나타낸 것입니다. 

 

TIP) JAVA의 pass-by reference를 좀 주의해야합니다.

아래는 아까 pass-by-value의 C언어 swap예제를 JAVA에서 해 본 예제입니다.

JAVA에서 모든 매개변수는 pass-by-value방식이지만, 객체매개변수는 pass-by-reference입니다.

중요한 것은 JAVA에서는 포인터를 사용하지 않고 인스턴스(객체)를 사용합니다. 예제2에서 subprogram에 인스턴스를 전달하면 새로운 메모리를 할당합니다. (pass-by-value처럼) 

그래서 자바의 pass-by-reference는 주소공간이 서로 다르다는 것입니다. 객체 주솟값 직접 전달되는 것이 아니라, 객체를 가리키는 또 다른 주솟값을 만들어서 전달합니다. 그래서 pass-by-reference를 한 예제2는 앞서 봤던 C언어였다면 swap이 이루어졌겠지만 JAVA에서는 swap이 되지 않습니다.

그래서 예제3처럼 새로운 레퍼런스를 참조해서 객체 값을 변경해야 swap이 정상적으로 이루어집니다. 

 

 

Type Checking Parameters

 

Reliability(신뢰성)을 위해서 중요합니다.

Perl, PHP, Javascript와 같은 비교적 새로운 언어들은 타입체킹을 필요로 하지 않는다고 합니다.

그리고 Python과 Ruby는 변수가 타입을 가지지 않습니다.(LIST, TUPLE, DICTIONARY) _(객체는 타입을 가짐) 

 

Multidimensional Arrays as Parameters

C나 C++에서 배열을 매개변수로 전달할 때, 배열이 크기가 너무 크다면 배열포인터로 전달하는 것이 좋습니다. 

Java나 C#에서는 배열을 객체로 생성해서 객체의 element를 또 객체(배열)로 사용 가능합니다.(다차원배열) 

 

Parameters that are Subprogram Names: Referencing Environment

1. Shallow Binding: dynamic-scoped languages에 적합

2. Deep Binding: static-scoped languages에 적합 

3. Ad hoc Binding: subprogram을 actual parameter로 패싱 

적갈색이 1.Shallow Binding_ sub3()에서 sub4(sub2);를 호출합니다. sub2의 x참조는 sub4의 지역변수 4. 

초록색이 2.Deep Binding_ sub2에서 alert(x)를 호출했을 때 static-scoped방식 따라 바깥블록의 x=1;을 참조합니다.

보라색이 3.Ad hoc binding_ sub3에서 sub4(sub2);에서 sub2를 actual parameter로 전달한 호출문을 참조환경으로 합니다.

 

Calling Subprograms Indirectly

event handling, 예를 들어 signal함수로 signal handler로 subprogram 간접적 호출을 할 수 있습니다. 

 

C#에서는 method pointer를 객체로 실행하는 delegates라는 것이 있습니다.

-Delegate declaration(중요!) 

delegate를 이용한 경우 간접호출로 indirect addressing과 비슷한 방법으로 이루어집니다.

장점은 다양하게 control, handling해서 원하는대로 변경하여도 사용할 수 있다는 것이지만

단점은 overhead가 발생할 수 있다는 것입니다. 

 

Overloaded Subprograms

중복된 subprogram으로 이는 C에서는 에러지만 C++에서는 다형성 개념으로 generic program 성질 중의 하나입니다. 

C에서는 함수의 이름이 같으므로 번역 시 오류가 발생합니다.

int add(int x, int y){
return x+y;
}
float add(float x, float y(P
return x+y;
}
int main(void){
add(10,20); //error!
return 0;
}

 

하지만 C++에서는 함수의 이름이 같아도 매개변수의 내용이 다르면 다른 함수로 인식합니다. 

int add(int x, int y){
return x+y;
}
float add(float x, float y(P
return x+y;
}
int main(void){
add(10,20); 
add(1.2,3.4);
return 0;
}

-Default Parameters

앞서서 디폴트 매개변수에 대해 알아봤었습니다. 아래 예제의 경우 overloaded subprogram으로 에러발생합니다. 

int func(int x=0){//디폴트매개변수 사용
return x+10;
}
int func(){//매개변수 없음
return 0;
}
int main(void){
func(); //errror! 1행과 5행모두 해당하므로
return 0;
}

 

Generic Subprograms

매개변수의 다형성 개념 관련있습니다. Polymorphic subprogram이라고도 부르며 매개변수를 다른 타입과 다른 실행을 취합니다.

Subtype poltmorphism: 오브젝트로 타입 T 사용. (OOP) 

subprogram은 parametic polymorphism을 제공하며 타입을 T라고 써놓은 후 나중에 타입을 결정하는 것ㅇ입니다.

 

이러한 포괄형 subprogram은

소프트웨어의 재사용성을 증가시키고, 다른 데이터 타입에 대해 동일한 알고리즘으로 구현된 subprogram을 구현할 수 있습니다. 특히 매개변수적 다형성(Parametic polymorphism) 

 

C++에서는 template함수에 해당됩니다.

template <typename T> //함수템플릿정의
void func(T a, T b){
cout<<a<<endl;
cout<<b<<endl;
}
int main(void){
func(1, 2);
func(3, 2.5); //error! 동일 타입이어야함
return 0;
}

주의해야 할 것은 함수 템플릿 정의시 type을 하나만 정의했으면 매개변수들은 동일한 type이어야합니다.

template <typename T1, typename T2> //함수템플릿정의
void func(T a, T b){
cout<<a<<endl;
cout<<b<<endl;
}
int main(void){
func(1, 2);
func(3, 2.5); 
return 0;
}

이렇게 템플릿 정의로 type 2개를 하면 type2개가 사용 가능해집니다.

 

템플릿함수 중 특정한 경우에 대하여 다른 처리 및 결과값을 원하는 경우에 Explicit Instantiation(특수화)를 할 수 있습니다.

template<typename T> //일반적인 함수템플릿정의
int Sizeof(T a){
return sizeof(a);
}

template<> //특수화
int Sizeof(char* a){
return strlen(a);
}

int main(void){
int i=10;
double e =7.7;
char *str="Hello";

cout<<Sizeof(i)<<endl;
cout<<Sizeof(e)<<endl;
cout<<Sizeof(str)<<endl;//특수화

return 0;
}

이는 template type을 char로 구체화(특수화)한 것입니다.  char타입일 때는 sizeof이 아닌 strlen을 사용하고 싶을 때 저렇게 특수화할 수 있습니다. 

 

이제 Java에서의 제너릭subprogram을 알아보겠습니다.

자바의 generic parameter는 classes취급됩니다. 그리고 generic method는 한번만 instantiate 됩니다.

generic parameter에 대해 Bound(한계)를 줄 수 있습니다.

public static <T extends Comparable> T

그리고 Wildcard types을 가질 수 있습니다.

void printCollection(Collection <?> c)

Collection<?>는 collection classes을 위한 wildcard type입니다. 이는 결정되지 않은 타입의 객체를 담고있는 컬렉션입니다. 그래서 컬렉션 c의 타입은 Collection of unknown입니다. 나중에 타입이 결정됩니다. 

 

자바에서의 제너릭은 String타입의 컬렉션이라면 타입캐스팅이 필요 없습니다.

 

 

Design Issues for Functions

부작용을 줄이려면 parameter는 in-mode를 사용하는 것이 좋다. 값만 전달(메모리는 추가로 필요) 

 

Overloaded Operators

이번에는 중복 연산자에 대해 알아보겠습니다. 연산자 함수를 구현하면 기존에 알고 있던 연산자의 의미와 다른 의미의 연산자를 구현할 수 있습니다. 

Class Unit{
Unit operator+(Unit right);
};

Unit Unit::operator+(Unit right){
//연산자함수의 구현...
}

 

Closures

closure는 지역변수의 참조를 가진 subprogram(함수블록)입니다. 

원래는 local 변수를 밖에서는 참조할 수 없지만

closure는 unlimited extent를 제공하므로 nonlocal variable에 접근이 가능합니다. 

그래서 이는 heap dynamic합니다.

본 함수가 소멸되더라도 내부함수로 접근이 가능하도록 합니다. 

 

이는 자바스크립트 예제입니다. 

function makeAdder(x){
return function(y){return x+y;}
}
...
var  add10 = makeAdder(10);
var  add10 = makeAdder(5);

C#에서는 function을 delegate로 고쳐주면 됩니다. 

function makeAdder(x){
return delegate(y){return x+y;}
}

 

Coroutines

multiple entries를 가진 subprogram으로 동시 실행되는 것처럼 보이는 것입니다.

이는 symmetric control, 즉 대칭적 구조를 가지고 

coroutine call은 resume이라고 부릅니다. (coroutine 시작)

Coroutine 은 quasi-concurrent(의사 동시성)을 제공하여 번갈아가며 실행하지만 동시에 병렬로 실행하는 것처럼 보입니다.

 

루프를 사용한 모습입니다.

카드들이 왔다갔다하는 카드게임 시뮬레이션과 같다고 비유합니다.