코딩을 지탱하는 기술 요약 - ChoDragon9/posts GitHub Wiki

문법의 역사

if, while을 만든 이유

사람이 편하게 쓰고 읽을 수 있는 프로그램을 만들기 위해 if, while과 같은 구조화 프로그래밍을 만들었다.

if else

if의 조건식에 반대가 되는 조건식을 만들기 위해서는 뒤집어서 표현해야 됨으로 혼란 스러울 수 있기 때문에 if else를 만들었다.

while

  • 반복되는 if를 읽기 쉽게 표현한것
  • 조건을 만족하고 있는 동안 블록 안의 내용을 반복하여 실행

for

  • 수치를 증가시키는 while을 읽기 쉽게 표현
  • 한곳에 '시작값, 종료값, 증가값' 3가지를 모두 정리가 되기 때문에 루프의 의도를 쉽게 이해할 수 있다.
  • 반면에 while은 '시작값, 종료값, 증가값' 3가지가 분산되어 의도 파악이 힘들다.

foreach

  • 처리 대상으로 반복제어
  • 어떤 대상의 요소 전부에 어떤 처리를 한다.

함수

  • 코드의 일부를 한 덩어리로 잘라내어 그것에 이름을 붙이는 기능
  • 원래 위치로 돌아갈 위치를 저장해 함수 호출 후 돌아간다.
    • 이로 인해 Stack 이 등장한다.
  • EDSAC에도 사용되었던 개념이다.

네이밍(변수명, 함수명 등)

메모리 주소를 지정하는 것보다. 알기 쉬운 이름을 붙여 편리하게 사용하기 위해 만들었다.

스코프

  • 이름의 유효 범위
  • 전역 스코프만 있었던 시절에 명이 겹치는 이슈가 있어 만들어짐

어떤 종류의 값으로 해석 할지를 판단하기 위해 비트열에 어떤 종류의 값 인가라는 추가 데이터를 붙인것

초기 FORTRAN의 형

메모리에 기록된 값이 정수인지 부동 소수점인지를 사람이 일일이 기억하는 것은 매우 피곤한 일이다. 편하게 할 방법은 없을까?

한 가지 방법은 변수명에 안에 든 것이 무엇인지 표현하기 위한 규칙을 만드는 것이다. 예를 들어 초기 FORTRAN에서는 변수명 선수가 I~N이면 정수, 그 이외이면 부동 소수점이 들어있다는 규칙을 사용했다.

언어 처리계에 변수 종류를 알린다

다른 한 가지 방법은 언어 처리계에 이 변수는 정수다라고 알려서 사람이 아는 컴퓨터가 기억해두는 것이다.

이것이 변수형 선언이 탄생한 이유다. 예를 들어 C언어에서는 int x;라고 선언하면 x라는 이름이 가리키고 있는 메모리 영역은 정수로 해석하고, float y;라고 선언하면 y가 가리키고 있는 메모리는 부동 소수점으로 해석하게 된다.

형 정보가 주어짐으로, 처리계는 덧셈을 할 때 정수를 덧셈할지 부동 소수점을 덧셈할지 판단할 수 있게 된다. 사람이 일일이 지정하지 않아도 되는 것이다.

사용자 정의형과 객체 지향

우선 언어가 가지고 있는 기본적인 형을 조합해서 새로운 형을 만드는 기능이 발명됐다. C언어의 구조체 등이 대표적인 예다. 사용자 정의형이라고도 불린다.

struct person {
  int age;
  char *name;
}

총칭형

다양한 형을 조합해서 만든 복잡한 형이 사용되면서 일부만 바꾸고 재사용하고 싶은 필요성이 생겼다. 그래서 구성 요소의 형을 일부만 바꾸는 형인 총칭형이 탄생했다. 다르게 설명하면 형이 인수를 가지고 형을 만드는 함수이다.

C++의 Template, Java의 Generics, Haskell의 형생성자 등이 그런 구조다.

에러처리

사고 발생 시 큰 사고로 이어지지 않도록 예방하는 것이 필요하여 실패를 알리는 구조가 필요했다.

실패를 전달하는 방법

  1. 반환값으로 실패를 전달하면 호출처가 반환값을 체크해서 에러 처리를 하는 방법
    • 실패를 놓친다
      • 프로그래머가 반환값 확인을 잊어버렸을 때 실패를 놓치게 된다.
      • 연쇄적으로 문제가 발생할 수 있다.
    • 에러 처리 때문에 코드를 해석하기 어렵다.
      • 원래 하고 싶은 것을 기술한 코드 사이에 실패했을 경우의 코드가 채워져 흐름을 읽기 어렵다.
  2. 함수를 호출하기 전에 에러 처리 코드를 등록해두고, 실패 시 에러 처리 코드로 점프하는 방법
  3. 점프로 에러 처리를 한다.
    • 원래 하고 싶은 것을 기술한 코드실패했을 때의 처리가 분리된다.

예외 전파의 문제점

모든 함수의 소스 코드를 보지 않으면 함수가 어떤 예외를 던질 가능성이 있는 지 알 수 없다. 깜박하고 예외가 던져질 가능성이 놓치고 있다면 프로그램이 비정상 종료되어 버릴 수 있는 것이다.

Java의 검사 예외

이 문제를 피하기 위해선 어떤 예외를 던질지를 명시적으로 선언하는 것이 필요하다고 주장했다. Java에서는 throw에서 던질 수 있는 것, 즉 다른 수많은 언어들이 예외라고 부르고 있는 것을 다음과 같이 더 세분화하고 있다.

  • 예외 처리를 하지 않아도 되는 중요한 문제
  • 예외 처리를 해도 좋은 실행 시 예외
  • 예외 처리를 해도 좋은 기타 예외

여기서 기타 예외는 검사 예외라고 불리며 메소드 정의 시 예외를 밖으로 던진다는 것을 선언해줄 필요가 있다. 그것을 위해 준비되어 있는 것이 throw 절이다. 다음 코드에는 void shippai () throws MyException라고 쓰여있다. 이것은 메소드는 MyException 예외를 던질 가능성이 있다는 선언이다.

class Foo {
  void shippai () throws MyException {
    throw new MyException();
  }
}

검사 예외를 사용마녀 깜박하고 예외를 던질 가능성을 놓쳐버리는 일 따위는 발생하지 않게 된다. 예외를 던질 가능성이 있는 메소드를 호출할 경우 메소드가 던지는 예외를 그대로 호출처로 전달할지 아니면 메소드가 던지는 예외를 자신이 처리할지 둘 중 하나를 선택해서 구현하게 된다. 어느 쪽도 동작하지 않는 경우는 놓친 것을 컴파일러가 지정해준다.

검사 예외가 잘 사용되지 않는 이유

검사 예외는 매우 좋은 기능 처럼 보이지만 다른 언어에서 잘 채용되지 않고 있다. 그 이유는 throws나 try/catch에 기술하는 예외의 갯수가 너무 방대해 지거나. 어떤 메소드에서 던질 예외를 하나 추가하면 그 메소드를 호출하고 있는 모든 메소드를 수정해야만 한다.

병행처리

한 CPU에서 여러 개를 동시에 처리하는 방법은 사람이 눈치 챌 수 없는 만큼의 시간만큼의 짧은 시간에 복수의 처리를 변경해가며 실행

방법

  1. 협력적 멀티 테스크
    • 타이밍이 좋은 시점에 교대
    • 자발적인 처리 교대하는 방법
  2. 선점적 멀티 테스크
    • 일정 시간에 교대
    • 강제적으로 중단 시켜 다른 프로그램 시작

경합 상태(Race Condition)

이 상태를 Thread safe가 아니다 라고도 하며 발생경우는 아래와 같다.

  1. 2가지 처리가 변수를 공유하고 있다.
  2. 적어도 하나의 처리가 그 변수를 변경한다.
  3. 한쪽 처리가 한 단락 마무리 되기 전에, 다른 한쪽의 처리가 끼어들 가능성있다.

1번 해결방안

프로세스
  • UNIX에서는 실행중인 프로그램을 이야기한다.
  • 프로그램같의 메모리를 공유하지 않는다.
  • 메모리 영역을 지정해서 사용한다.
스레드
  • 메모리를 공유하는 경량 프로세스
  • UNIX 출시 10년 후 메모리를 공유하는 기능이 필요하여 만듬
액터
  • 메모리를 공유하지 않는 다.
  • 메세지를 보내 데이터를 공유한다.

2번 해결방안

Immutable, const
  • 메모리를 공유해도 변경을 하지 않으면 문제가 없다.
  • getter는 있으나, setter가 없다.

3번 해결방안

코루틴
  • 끼어들지 않게 협력적 스레드를 만든다.
  • 지금 끼어들면 곤란해 라는 표식으로 사용중이라는 표식이다.
  • [문제점] 2개의 명령이 a,b의 unlock과 lock을 교차 요청하면 무한이 상대방 락이 풀리는 것을 기대하는 Deadlock(교착상태)가 발생한다.
    • [해결방안] 트렌젝션 메모리 접근법을 착용한다.
    • 트렌젝션 메모리는 실험적으로 해보고, 실패하면 처음부터 다시 고쳐서하고 성공하면 변경을 공유하는 기법이다.

객체지향

현실 세계의 사물을 컴퓨터에서 조작하기 위한 모델의 개념으로 생각하는 것이 객체지향의 목적이다.

  • Alan Kay(객체 지향 발명가, Smallback 설계자)
    • 형/상속은 고생하고 좋지 않다.
    • 객체지향은 상태를 가진 객체가 메세지를 주고 받아서 커뮤니케이션 하는 것이다.라고 설명한다.
  • Bjarne Stroustrup(C++ 설계자)
    • 객체지향은 사용자 정의형과 상속을 사용한 프로그래밍이다.
    • Class는 사용자 정의형을 만들기 위한 도구이다.
    • Simula의 상속 구조가 문제 해결의 키이다.
  • 사물은 객체(Object), 모형은 모델(Model)이라 칭한다.
    • 모형으로 사물을 만들듯이, 모델로 객체를 만든다.
    • 모형 : 모양이 같은 물건을 만들기 위한 틀

모형을 만드는 방법

  1. Class 사용(C++, java)
    • 현실 세계 사물을 Class로 분류
    • 세분화된 종류는 subclass로 정의
  2. Module 또는 Package
    • 함수를 하나로 모아두기 위한 기능
  3. Hash에 함수/변수를 정의
    • first-class citizen 을 사용
      • 변수나 데이터 구조안에 담을 수 있다.
      • 파라미터로 전달 할 수 있다.
      • 반환값(return value)으로 사용할 수 있다.
      • 할당에 사용된 이름과 관계없이 고유한 구별이 가능하다.
      • 동적으로 프로퍼티 할당이 가능하다.
    • javascript로 예를 들면 { fn: () => {}, data: 1 } 이러한 형태이다.
  4. Closure
    • 함수 실행시의 이름 공간의 변수를 하나로 묶기 위해 사용하는 방법
    • 함수형 프로그래밍에서 사용하는 방법
    • Closure 라고 불리는 이유?
      • [1987 Ake Wikstrom] 자유 변수를 포함한 식을 '열린 식'이라고 부르고, 그 자유 변수의 바인딩을 조합함으로서 해당식을 닫고 있기 때문이다.