전공과목 정리/컴퓨터과학의이해

[컴퓨터과학의이해🧮] 프로그래밍 언어 (6장)

최연재 2022. 8. 30. 00:12

교재 : 컴퓨터 과학 총론 (13th Edition)

(배운 내용을 책으로 복습하며 작성한 글입니다. )

1. 역사적 고찰

1) 초창기 언어

- 컴퓨터를 위한 프로그램은 숫자들로 인코딩된 일련의 명령으로 이루어지는데, 이러한 인코딩 체계를 기계어 (machine language)라 부른다.

- 디버깅 (debuging) : 프로그램을 완성하기 위해 오류를 찾아 고치는 과정

 

(1) 어셈브러

  • 어셈블러 (asembler) : 기호 표현을 기계어 명령으로 변환하는 프로그램
  • 어셈블리어 (assembly language) : 프로그램 표현에 사용되는 기호 체계
  • 어셈블리 언어는 기계어에 비해 여러 가지 이점을 갖고 있지만, 궁극적인 프로그래밍 환경을 제공하기에는 여전히 많이 부족하다. 
  • 어셈블러 언어로 작성된 프로그램은 본질적으로 기계 종속적이다. 즉, 프로그램 안의 명령들은 특정 컴퓨터의 속성을 이용하여 표현된다. 
  • 프로그래머가 숫자 형식을 사용해야 하는 것은 아니지만 여전히 기계어의 단순한 단계들로 주어지는 틀에 맞추어 사고하도록 강요된다. 

(2) 3세대 프로그래밍 언어

  • 큰 단위의 명령을 표현할 수 있는 고급 수준의 프리미티브를 가지고 있다.
  • 특정 컴퓨터의 특성에 의존하지 않는 기계 독립적(machine independent)이다.
  • 소프트웨어 개발에 이용될 수 있는 고급 수준의 프리미티브 집합을 정한다. 
  • 고급 수준의 프리미티브가 정해지면, 고급 수준의 프리미티브들을 기계어 프로그램으로 번역해주는 번역기(translator)프로그램을 개발하여 사용할 수 있다. 
  • 3세대 언어들을 위한 번역 프로그램은 흔히 컴파일러(compiler)라 부른다.
  • 3세대 언어를 구현하기 위한 수단으로 컴파일러 대신 인터프리터(interpreter)라 불리는 프로그램을 사용한다. 
  • 인터프리터는 컴파일러와 유사한 특징이 있지만 컴파일러가 번역된 버전을 나중에 사용하기 위해 파일로 만드는 반면, 인터프리터는 번역하면서 실행된다는 점에서 차이가 있다. 
  • 인터프리터는 나중에 실행된 기계어 버전을 만드는 것이 아니라 고급 수준 형태의 프로그램을 실제로 실행시킨다.

- 일상 언어  : 자연어(natural language)

- 프로그래밍 언어 : 정형어(formal language)

 

2) 기계 독립성

- 3세대 언어의 발전과 함께 기계 독립성이라는 목표는 대체로 달성되었다. (특정 컴퓨터의 속성을 참조하지 않으므로 어느 컴퓨터에서나 쉽게 컴파일된다.)

- 하지만 컴파일러가 설계될 때, 때로 그 기반이 되는 컴퓨터의 특성이 번역되는 언어에 대한 조건으로 반영되기도 한다. 

- 3세대 언어가 진정한 기계독립성에는 미치지는 못하지만 이는 크게 중요하지 않다. 

  • 소프트웨어를 한 컴퓨터에서 다른 컴퓨터로 그리 어렵지 않게 옮길 수 있을 정도로 3세대 언어의 기계 독립성 수준은 충분하다. 
  • 기계 독립성이라는 목표는 보다 중요한 목표를 위한 씨앗에 불과하다. 

- 새로 나타나는 고급 프로그래밍 언어들에는 더욱 강력한 추상화 요소들이 내장되어 프로그래머들이 소프트웨어 설계를 개념화하는 수준을 더욱 높였으며, 점차 더욱 복잡한 소프트웨어 시스템의 구축이 가능해졌다. 

 

3) 프로그래밍 패러다임

- 프로그래밍언어는 프로그래밍 패러다임이라 불린느 다양한 프로그래밍 방법론이 나타남에 따라 다양한 방향으로 발전해왔다. 

- 패러다임의의 종류 : 함수형(functional), 객체지향(object-oriented), 명령형(imperative), 선언형(declarative) 등

- 텍스트 가빈 프로그래밍 언어들이나 시각적 프로그래밍언어들은 컴퓨터 프로세서에서 자동적으로 실행시킬 수 있을 정도의 정확성을 제공하면서 알고리즘을 명료하게 기술할 수 있도록 설계된다. 

- 광범위한 영역에 걸쳐 문제 해결 유형이 다양해짐에 따라 특정 유형의 알고리즘들을 보다 잘 지원하 수 있는 많은 전용 프로그래밍 언어가 개발되었다. 

 

(1) 명령형 패러다임 (impertive paradigm) == 절차형 패러다임 (procedural paradigm)

  • 프로그래밍 과정에 대한 전통적 접근 방법
  • 기계어, 파이썬, 의사코드
  • 데이터를 가공하여 원하는 결과를 만들어내도록 실행될 수 있는 일련의 명령을 개발하는 것으로 프로그래밍 과정을 정의한다. 
  • 프로그래밍 과정은 당면한 문제를 해결하기 위한 알고리즘을 찾고 알고리즘을 일련의 명령으로 표현하는 접근 방식을 취한다.

(2)  선언형 패러다임 (declarative paradigm)

  • 프로그래머가 구체적인 알고리즘이 아닌 해결해야 할 문제 자체를 기술한다. 
  • 선언형 프로그래밍 시스템은 주어지는 문제에 미리 정의되어 있는 범용 문제 해결 알고리즘을 적용한다.
  • 바탕이 되는 문제 해결 알고리즘이 필요하다.
  • 수학에서 정형논리(formal logic)가 범용 선언형 프로그래밍 시스템에서 사용하기에 적합한 간단한 문제 해결 알고리즘을 제공한다는 사실이 밝혀지며 선연형 패러다임이 크게 활성되었다. 
  • 논리 프로그래밍(logic programming)이 등장하게 되었다. 

(3) 함수형 패러다임 (fuctional paradigm)

  • 프로그램은 입력을 받아 출력을 만드는 개체로 간주된다. 
  • 프로그램은 사전 정의된 작은 함수라는 프로그램 단위들을 연결하여 구축되는데, 각 함수의 출력은 다른 함수의 입력으로 사용되며 전체적으로 원하는 입력-출력 함수 관계를 형성한다. 
  • 복잡한 함수 호출 구조를 구축한다. 

(4) 객체지향 패러다임 (object-oriented paradigm) 

  • 객체지향 프로그래밍(object-oriented programming ; OOP) 프로그래밍 과정에 관련된다.
  • 소프트웨어 시스템은 객체(object)라는 단위들의 집합으로 간주된다. 
  • 메서드 (method)라 불리는 함수들의 집합을 포함한다. 
  • 전체 객체지향 시스템은 자신에 관련된 사건들에 어떻게 반응할지를 알고 있는 객체들의 집합으로 구축된다. 
  • 객체는 데이터 부분과 함께 활동 수행을 위한 메서드집합으로 이루어진다. 
  • 객체의 특성을 기술하는 클래스(class)가 구축되면 하나의 클래스에 기초해 여러 개의 객체를 만들 수 있다.
  • 특정 클래스를 사용하여 구축된 객체를 해당 클래스의 인스턴스(instance)라 부른다. 
  • 객체들은 재사용이 가능한 클래스를 통해 기술된 잘 정의된 정의이다. 
  • 객체 안의 메서드들은 기본적으로 작은 명령형 프로그램 단위이다.  

2. 전통적 프로그래밍 개념

  • 명령형 패러다임 하에서 프로그램을 작성하는 접근 방식을 취한다.
  • 프로그램은 문장의 집합으로 이루어지며, 프로그램 문장은 선언문, 명령문, 주석 등의 세 개의 범주로 분류된다. 
  • 선언문(declarative statement)은 데이터 항목에 대한 참조에 사용되는 이름들과 같이 프로그램에서 나중에 사용할 용어들을 정의한다.
  • 명령문(impertative statement)은 프로그램의 바탕이 되는 알고리즘의 단계들을 기술한다. 
  • 주석(comment)은 프로그램이 수행하는 기능을 사람이 이해하기 쉬운 형식으로 설명함으로써 프로그램을 읽기 쉽게 해준다.  
  • 명령형 프로그램은 프로그램에서 다룰 데이터를 기술하는 선언문으로 시작한다. 그 뒤에 명령문이 온다. 

1) 변수와 데이터 타입

- 변수(variabe)

;주기억장치의 위치들을 숫자 형태의 주소 대신 의미 있는 이름을 사용하여 표시할 있는데, 이러한 이름이 나타내는 주소 위치 값을 프로그램 실행 중에 변경가능하다.

- 데이터 타입 (data type)

;변수들을 사용하기 전에 미리 선언문에서 메모리 위치에 저장할 값의 유형을 표시하도록 요구한다.

  • 정수(integer) : 2의 보수 표기법을 사용해 저장되는 정수
  • 실수(float, real) : 부동소수점 표기법으로 저장되며, 정수 이외의 숫자들도 포함할 수 있는 숫자 데이터
  • 문자 (character) : ASCII나 유니코드로 저장되는 기호들로 이루어진 데이터
  • 부울 (bool) : 참이나 거짓 값만을 가지는 데이터

- 프리미티브 데이터 타입 (primitve data type) : 프로그래밍 언어에 포함된 데이터 타입

 

2) 데이터 구조

데이터 구조(data strcuture) : 데이터에 대한 개념적 형태나 배치를 의미한다.

  • 배열(array) : 동일한 데이터 타입의 원소들로 이루어지는 블록
  • 구조체(structure) : 원소들마다 데이터 타입이 달라질 수 있는 데이터 블록 (==집합체 타입(aggregate type), 이질성 배열(heterogeneous array))

 

3) 상수와 리터럴

  • 리터럴 (literal) : 값 자체를 표시하는것
  • 상수 (constant) : 프로그래밍언어에서는 변경되지 않는 특정 값에 서술적 이름(상수)을 부여하는 것을 허용한다. 

 

4) 배정문

  • 배정문 (assignment statement) : 변수에 식의 값을 배정하도록 요청
  • 중복 정의 (overloading) : 하나의 연산 기호를 여러 용도로 사용하는 것

 

5) 제어문

  • 제어문 (control statement) : 프로그램의 실행 순서를 변경

ex) goto 문

 

- goto문으로 야기되는 복잡성을 피하기 위해 오늘날의 언어들은 하나의 구문으로 전체 분기(branching) 패턴을 표현할 수 있는 제어문들을 사용한다. 

- 구조적 프로그래밍(structured programming) : 조직적 설계 방법론 + 프로그래밍 언어 제어문들의 적절한 이용

 

6) 주석

  • 주석 (comment) : 프로그램 설명문
  • 주석의 목적은 프로그램을 되풀이하는 것이 아니라 설명하는 것이다. 

3. 프로그램 단위

1) 함수

- 함수 (fuction) : 다른 프로그램 단위에서 사용될 수 있으며, 어떤 작업을 수행하는 명령들의 모임

  • 함수 호출(calling, invoking) : 제어를 함수로 이동하는 과정
  • 호출 단위 : 함수의 실행을 요청하는 프로그램 단위
  • 함수는 개별 프로그램 단위로 작성된다. 
  • 함수 헤더 (function header) : 함수의 이름을 포함하는 문장
  • 지역 변수(local variable) : 함수 안에서 선언되는 변수 (함수 내에서만 참조됨)
  • 참조 번위(scope) : 어떤 변수가 참조될 수 있는 프로그램 부분
  • 전역 변수 (global variable) : 참조 범위가 프로그램의 특정 부분으로 제한되지 않는 변수

 

2) 매개변수

매개변수 (parameter) : 함수 내부의 그와 같은 일반형 명칭

  • 형식 매개변수(formal parameter) : 함수 내부의 일반형 명칭
  • 실질 매개변수(actual parameter) : 함수가 호출될 때 정해지는 형식 매개변수의 정확한 의미
  • 둘 이상의 매개변수가 사용될 경우, 실질 매개변수와 함수 헤더의 형식 매개변수는 한 항목씩 대응한다. 
  • 값에 의한 전달 (passed by value) : 함수에 주어지는 실질 매개변수가 나타내는 데이터의 복사본이 만들어져 함수에 전달된다. 함수 안에서 데이터가 어떻게 변경되더라도 복사본에만 변화가 반영되며, 호출 프로그램 단위의 데이터는 변화하지 않는다.
  • 참조에 의한 전달 (passed by reference) : 함수의 호출 프로그램 단위 쪽의 실질 매개변수의 주소를 알려줌으로써 함수가 실질 매개변수에 접근할 수 있게 하는 방식으로 호출 환경 쪽에 있는 데이터의 변경을 허용한다.  

 

3) 결과 있는 함수

프로시저 (procedure) : 값을 리턴하지 않는 서브프로그램 / 파스칼에서 사용한 용어

(C와 자바에서는 void라는 키워드를 사용하고, 파이썬에서는 리턴 값을 가지는 함수를 구별하기 위해 결과 있는 함수(fruitful function)라는 용어를 사용한다. )

 

4. 언어의 구현

1) 번역 과정

- 번역(translation) : 프로그램을 한 언어에서 다른 언어로 변환하는 과정

  • 소스 프로그램(source program) : 원래 형태의 프로그램
  • 목적 프로그램 (object program) : 번역된 프로그램
  • 번역 과정은 어휘 분석, 구문 분석, 코드 생성 등의 세 가지 활동으로 이루어진다.
  • 위의 과정은 각기 번역기 내의 어휘 분석기, 구문 분석기, 코드 생성기 등에 의해 수행된다. 

(1) 어휘 분석기 (lexical analyzer)

  • 소스 프로그램을 기호별로 읽어나가면서 토큰을 이루는 기호 그룹들을 식별하고, 숫자, 단어, 산술 연산자 등등으로 토큰들을 분류한다. 
  • 각 토큰들을 분류 코드로 인코딩하여 구문 분석기에 넘긴다. 
  • 주석은 건너뛴다. 

(2) 구문 분석기 (paroser)

  • 프로그램을 개별 기호로서가 아니라 토큰이라는 어휘 단위로 파악한다.
  • 어휘 단위들을 문장으로 묶는 것은 구문 분석기가 한다. 
  • 초창기 프로그래밍 언어들은 프로그램의 각 문장을 인쇄된 페이지에서 특정 위치에 나타나도록 정한다. (고정 형식 언어 : fixed-format language)
  • 오늘날 많은 언어들은 문장의 위치가 중요하지 않은 자유 형식 언어(free-format language)이다. 
  • 자유 형식 언어로 작성된 프로그램을 구문 분석하기 위해서는 프로그램의 구조를 식별할 수 있도록 언어 구문이 설계되어야 한다. 
  • 대부분의 자유 형식 언어는 문장 끝을 표시하는 세미콜론을 사용한다. 
  • 구절의 시작에는 키워드(keyword)를 사용하는데, 키워드는 대개 예약어(reserved word)이다. (다른 용도로 사용 불가)
  • 구문 분석과정은 프로그래밍 언어의 구문을 정의하는 규칙들의 집합인 문법(grammer)에 기초하여 이루어진다. 
  • 구문 규칙을 표현하는 한 가지 방식은 구문 다이어그램 (syntax diagram)을 사용하는 것이다. 
  • 구문 다이어그램은 프로그램의 문법적 구조를 그림으로 표현하는 것이다. 
  • 특정 문자열을 구문 다이어그램에 맞게 그림 형태로 표현하는 것을 구문 분석 트리(parse tree)라 부른다.
  • 프로그램을 구문 분석하는 과정은 기본적으로 소스 프로그램을 위한 구문 분석 트리를 구축하는 과정이다. 
  • 구문 분석 트리는 프로그램의 문법적 구조에 대한 구문 분석기의 해석에 해당한다. 
  • 모호한 문법 (ambiguous grammer) : 하나의 문자열에 대해서 두 개의 서로 다른 구문 분석 트리를 허용한다.  
  • 구문 분석기가 선언문을 인식하게 되면, 심볼 테이블(symbol table)이라는 테이블 안에 선언된 정보를 기록한다. 

* 타입 변환

  • 묵시적 변환 (corecion) : 자동적으로 이루어짐
  • 묵지적 변환으로 데이터 항목의 값이 변경될 수 있어서 대부분의 프로그래밍언언느 강한 타입 규칙(strongly typed)을 가진다. 
  • 강한 타입 규칙은 프로그램에서 요청되는 모든 활동은 묵시적 변환 없이도 함께 쓰일 수 있는 데이터 타입만을 허용해야 한다는 것이다. 
  • 타입 확장 변환 (typed promotion) : 낮은 정밀도의 값에서 높은 정밀도의 값으로 변환 (자바와 같은 일부 언어에서는 타입 확장 변환에 한해 묵시적 변환을 허용한다. )
  • 타입 강제 변환 (type cast) : 프로그래머가 타입 변환을 요청 (컴파일러에게 프로그래머가 타입 변환을 알고 있음을 알림.)

(3) 코드 생성 (code generation)

  • 구문 분석기가 인식한 문장을 구현하기 위해 기계어 명령들을 생성하는 것
  • 코드 최적화(code optimization) : 보다 효율적인 코드를 구현하는 과정

2) 소프트웨어 개발 패키지

- 소프트웨어 개발 과정에서 사용되는 편집기와 번역기 등과 같은 소프트웨어 도구들은 종종 하나의 통합 소프트웨어 개발 시스템으로 기능하는 패키지로 묶여 있다. (응용 소프트웨어로 분류될 것이다.)

- 통합 시스템 이용의 장점 중 하나는 프로그래머가 프로그램을 고치고 테스트하면서 편집기와 디버깅 도구 사이를 쉽게 이동할 수 있다는 점이다.

- 서로 연관된 프로그램 단위들을 이용하기 쉽게 연결 가능하다.

- 컴포넌트 구조 (component architecture)  모델에 기초한 소프트웨어 개발 패키지는 종종 컴포넌트들을 디스플레이상의 아이콘으로 표현하는 그래픽 인터펭스를 이용한다.  

5. 객체지향 프로그래밍

1) 클래스와 객체

- 클래스(class) : 객체지향 패러다임에서 객체들을 위한 틀(template)

- 인스턴스 변수 (instance variable) : 객체 안에 들어 있는 변수

- 메서드 (method) : 객체 안의 함수

- 인스턴스 (instance) : 객체는 객체 생성에 사용되는 클래스의 인스턴스라 한다. 

 

2) 생성자

- 생성자 : 해당 클래스 안의 특별한 메서드

 

3) 기타 특성

- 상속 (inheritance) : 한 클래스가 다른 클래스의 속성을 포함할 수 있게 하는 기법

- 다형성 (polymorphism) : 객체마다 메시지를 자신에 맞게 달리 해석하는 것

- 캡술화 (encapsulation) : 객체의 내부 속성에 대한 접근을 제한하는 것

 

6. 병행 활동 프로그래밍

(1)병렬 처리 (parallel processing) == 병행 처리 (concurrent processing)

: 여러 프로그램에 대한 활성화를 동시에 실행시키는 것

  • 진정한 병렬 처리를 위해서는 각 활성화에 한 개씩 여러개의 CPU 코어가 필요하다. 
  • 자바에서는 여러 동작을 동시에 수행하기 위해 여러 개의 쓰레드를 생성한다.
  • 병렬 처리의 경우 요청된 함수가 작업을 수행하는 동안 요청 프로그램 단위도 실행을 계속한다.
  • 한 번에 한 쓰레드에 의해서만 접근될 수 있는 데이터는 상호 배제 접근성을 갖는다.

 

(2) 상호 배체 접근성 구현 방법

- 한 쓰레드가 공유 데이터를 사용할 때 다른 쓰레드들이 안전하게 접근할 수 있을 때까지 다른 쓰레드들의 접근을 차단하도록 관련 쓰레드를 기술하는 프로그램 단위를 기술한다. 

  • 상호 배제를 보장하는 책임을 프로그램의 다양한 부분에 분산시키는 단점이 있다.
  • 공유 데이터에 접근하는 각 프로그램 단위는 상호 배제를 지킬 수 있도록 적절히 설계되어야 하는데, 어느 한 부분이라도 잘못되어 있으면 전체 시스템에 문제가 발생한다.

- 데이터 항목 자체가 자신에 대한 접근을 제어할 수 있는 능력을 갖도록 만든다. 

  • 공유 데이터에 접근하는 쓰레드들이 다중 접근에 대한 보호를 적절히 구현하는 것을 믿기 보다는 데이터 항목 자체에 이러한 책임을 맡긴다.
  • 접근에 대한 제어는 여러 프로그램 단위에 흩어져 있기보다는 프로그램상의 한 곳에 집중된다.
  • 모니터 (monitor) : 자신에 대한 접근 제어 능력을 갖춘 데이터 항목

 

7. 선언형 프로그래밍

1) 논리적 추론

- 분해 (resoultion) : 연역적 추론 원리 중 하나 (a 혹은 b인데, a가 거짓임을 알게되자 b라고 결론지붆음)

- 추론 규칙 (inference rule) : 집합에서 결론을 유도하는 데 사용되는 많은 기법

- 분해문 (resolvent) : 원래 명제들의 논리적 결과 (원래 명제들이 참이면 분해문 또한 반드시 참)

- 분해는 절 형태(clause) 로 나타나는 명제들의 쌍에만 적용 가능

- 명제 집합 안의 모든 명제가 동시에 참이 될 수 없을 경우, 이러한 집합은 모순적(inconsistent)이라 표현한다. 

- 단일화 (unification) : 분해가 수행될 수 있도록 값을 변수에 배졍하는 과정

 

2) Prolog

- 프로그래밍 언어 Prolog (PROgramming in LOGic) 는 반복적 분해에 기초한 문제 해결 알고리즘을 이용하는 선언형 프로그래밍 언어이다.

- 위와 같은 언어를 논리 프로그래밍(logic programming)언어라 한다. 

-  Prolog로 작성된 프로그램은 기반 알고리즘이 연역적 추리를 적용할 초기 명제들의 집합으로 이루어진다. 

- 명제를 이루는 구성요소는 술어 (predicate)라 불린다.