프로그래밍 언어론 7-3-1강. 일반화된 서브루틴과 클래스(Generic Subroutine and Classes)

[프로그래밍 언어론]

2019. 12. 15. 17:24

 

[다형성의 단점]

: 앞에서 알아보았던 다형성(Scheme, Ruby등)은 런타임 검사가 필요한데, 이는 높은 비용과 오류 알림의 딜레이를 유발한다..

: 명시적 파라미터의 다형성( = 일반화)는 프로그래머가 서브루틴이나 클래스를 선언할때 타입 파라미터들을 명시할 수 있게 한다.

 => 컴파일러가 이 파라미터를 서브루틴이나 클래스 선언시에 사용(컴파일 시점 검사)

 

 

 

[Generic의 발전]

: Ada기준. Generic. 즉 일반화는 기존의 다형성 방식은 받는 타입에 따라 같은이름의 메소드를 여러개 생성해야 하지만 이를 템플릿화(일반화) 하고자 하는 아이디어에서 나타났다.

 

# Ada에서의 일반화

// 기존 다형성 방식

function min(x,y : integer) return integer is ...

function min(x,y : long_float) return long_float is ...   // 두개가 같은 name이지만 받는 타입에 따라 여러개 메소드 생성

 

// 일반화 방식

...

function min(x, y : T) return T is ...     // 위에서 T를 generic 타입으로 선언 해줌

: 그리고 일반화된 메소드 사용시 Ada는 사용할 타입 명시 필요 "min(integer, "<") 하지만 C는 컴파일러가 대신 만드어준다.

 

# 객체지향 언어에서의 일반화

: Generic은 (C에서는 templete) 전체 클래스를 파라미터화 하기위해 사용되는 가장 일반적인 방식이다. ( <-> Duck typing)

: container형태로 전달되는 클래스들에게 주로 사용된다

: 스택, 큐, 힙, 세트, 사전형(매핑) / 리스트, 배열, tress, 해시테이블 등에서 주로 사용된다.

: 일반화 없으면 앞에서 보았듯이 universal 클래스를 사용해야하는데 이는 type cast 필요하고 컴파일 시점 검사를 못한다.

 

# 객체지향 언어에서의 일반화 예시

ArrayList<A> a = new ArrayList<A>();   // A나 A의 상속되는 타입만 해당 a 배열에 들어갈수있다. (컴파일 시점)

 

# C++에서의 일반화

template<class item, int max_items = 100>    // C, C++에서는 templete을 일반화 하는데 사용한다.

: 여기서 중요한건 JAVA와 C#에서는 오로진 타입들만 일반화 파라미터로써 사용가능하지만, 

: Ada와 C++은 값들을 일반화의 파라미터로 사용할 수 있다.

: 단, C++에서 파라미터로 사용되는 값은 컴파일-시점 상수 여야하고, Ada는 동적크기 배열 지원할때, 변수를 사용해도된다(동적시점까지 연기가능)

 

 

 

[실행 옵션Implementation Options)]

: 일반화는 여러가지 방법으로 실행될 수 있다.

 

# Ada와 C++에서의 일반화 실행

  : 순수한 정적 매커니즘

  : 모든 작업이 컴파일 시점에 일반화 코드의 다중 인스턴스들이 생성되고 사용되어야 한다.

  : 일반적인 케이스에서, 컴파일러는 코드의 모든 인스턴스에 대해 separate copy를 생성한다.

  : 같은 종류의 타입을 가지고 있는 클래스는 enqueue와 dequeue 루틴 코드를 공유한다.

class q;

q q1;

q q2;   //에서 q1의 q와 q2의 q는 같은 코드를 공유한다.

 

# JAVA에서의 일반화 실행

: 위와 반대로, 일반화에 주어진 모든 인스턴스들이 실행 시점에 같은 코드를 공유한다.

: 기준을 베이스 클래스인 Object로 잡는다.

: 즉, 일반화가 어떤 종류의 파라미터를 받던 간에, 같은 코드를 공유한다.

q<int>, q<process>   // 받는 타입 파라미터 달라도 같은 q 코드 공유한다.

 

# C#에서의 일반화 실행

: C++ 방식과 JAVA의 방식을 섞은 형태

: C++와 같이, 서로 다른 타입들에 대한 일반화의 특화된 실행을 생성한다.

: JAVA와 같이, 일반화 코드 그 자체가 분명히 안전할 것을 요구한다.

: Object와 같이 위에서 상속 받는 클래스 존재.

 

# 결론적으로 언어별로 일반화 구현되는 방식이 다르다는 것을 알수 있다.

 

 

 

[일반화 파라미터의 제약]

: 일반화는 추상화인 만큼, 일반화의 인터페이스(선언의 header부분)가 추상적인 사용자가 알고 있어야 하는 것들에 대한 모든 정보를 제공하는것이 중요하다.

 

# JAVA와 C#에서의 일반화 파라미터의 제약

: 부모클래스 타입이나 인터페이스로부터 일반화를 계승할 수 있는 객체 지향 형식의 능력을 이용하는 제약조건에 대해깨끗한 접근방식을 채택한다.

 

# JAVA 일반화 파라미터 상속 예시

public static <T extends Comparable<T>>  // T는 Comapareble를 상속한 객체

  void sort(T A[]) {

    ...

    if (A[i].compareTo(A[j]) >= 0 )   // compareTo를 구현한 애들만 T에 들어올 수 있다.

...

Integer[] myArray = new Integer[50];

sort(myArray);

 

# C# 일반화 파라미터 상속 예시

static void sort<T>(T A[]) where T : 

  Icomparable {   // Java처럼 Compareable 상속 의미

    ..

    if (A[i].compareTo(A[j] >= 0)

...

int[] myArray = new int[50];

sort(myArray);

: JAVA와 사용방법 다르지만 비슷하다.

 

: C# 컴파일러는 int타입이 primitive 타입인걸 충분히 인지할 정도로 똑똑하기에, 정렬에 대한 커스텀된 실행을 제공한다.

: Java의 Integer wrapper class 사용을 제거하게 해준다.

 

 

# C++ 일반화 파라미터의 제약

: 몇몇 언어들은 제약 명시를 포기하지만, 여전히 파라미터가 어떻게 사용되는지 검사한다. ex) C++

 

# C++ 일반화 파라미터 예시(일반화된 정렬 루틴의 header)

template<typename T>

void sort(T A[], int A_size) {...

: 앞과 같이 T중 Comparable 있는 것만 구별할 수 있는 방법이 없다.

: 그래서 그냥 T 타입이면 우선 사용할 순 있는데, 컴파일러가 T중 compareTo 함수 없는 거 사용시 오류를 식별 해준다.

: 그래서 프로그래머가 어느 부분에서 오류 나타날지 예측하기가 어렵다.

: 또한, 특정 케이스에선 원하던 결과와 다른 결과가 나올 수도 있다. ex) int와 double 타입에 대해 <는 메모리내 lower 주소만 참조

: 그래서 프로그래머는 JAVA와 C#을 에뮬레이트 하는데 주로 사용한다.

 

 

 

[암시적 인스턴스화(implicit instantiation)]

: 클래스는 타입이기에, 일반화 클래스(Object)는 사용되기 전에 인스턴스를 생성해줘야 한다.

 

# 일반화 클래스에 대한 인스턴스 생성

queue<int,50>* myQueue = new Queue<int, 50();  // C++,  여기선 new를 이용해 인스턴스 생성

 

# 몇몇 언어 (Ada등)들은 사용되기 이전에 암시적 인스턴스화 하기위해 일반화 서브루틴을 필요로 한다.

procedure int_sort is new sort(integer, int_array, "<");

 

# C++, JAVA, C#에서의 암시적 인스턴스화

: 위와 같은 작업 필요 없다.

sort(ints, 10);

sort(reals, 50);

// 아래 와 같이 사용하지 않고 위처럼 사용 가능

int ints[10];

double reals[50];

: 그냥 이렇게 쓰면 컴파일러가 알아서 내부적으로 코드를 생성해준다.

 

 

 

[C++, JAVA, C#에서의 일반화]

1. C++에서의 일반화

  : C++에서는 일반화가 좀더 다양화 되어 사용된다.

2. JAVA, C#에서의 일반화

  : container 생성까지만 일반화 사용하게끔 순수하게만 제공된다.