ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 내 맘대로 프로그램 설계 3. - 함수와 변수.
    프로그래밍/설계 2018. 1. 16. 04:23

    원래 합쳐져서 Section2 였던 '기초'에서 '간단한 데이터 처리'와 '함수'로 나뉘어 구성하게 되었습니다.

    너무 길어서 로딩도 오래걸리고, 읽는 사람도 힘들겠더군요.



    내 맘대로 하는 프로그램 설계 시리즈.

    Chapter1 - 간단한 데이터 처리(4섹션)

    2017/12/27 - [프로그래밍/설계] - 내 맘대로 프로그램 설계 1. - 이유와 준비.

    2018/01/11 - [프로그래밍/설계] - 내 맘대로 프로그램 설계 2. - 데이터 타입.

    2018/01/16 - [프로그래밍/설계] - 내 맘대로 프로그램 설계 3. - 함수와 변수.(현재)

    2018/05/29 - [프로그래밍/설계] - 내 맘대로 프로그램 설계 4. - 고정 크기 데이터.

    2017/06/30 - [프로그래밍/설계] - 프로그래밍과 추상화에 대하여.[부록]


    Chapter2 - 임의의 데이터 처리

    2018/06/10 - [프로그래밍/설계] - 내 맘대로 프로그램 설계 5. - 리스트와 재귀.

    어.. 전 포스트에 비해 분량이 조금 많아 보이는데..

    조절을 실패해서 그렇습니다;;


    이렇게 된거 그냥 초반의지가 불타올랐을때 끝장내버립시다.ㅎㅎ

    나중에 갈수록 하기 싫어지더라고요.


    1. 시작하기.

    지금껏 간단히 각종 데이터들을 간단하게나마 다루어보았습니다.


    이제 함수를 본격적으로 다루어보기로 합시다.

    Parameter와 Argument 쪽 +.를 보셨다면 이해의 속도가 훨씬 빨라질 것입니다


    2. 함수

    2.1 함수 만들기.

    Racket에서 함수의 개념은 우리가 수학에서 배웠던 함수와 유사합니다.


    아래와 같은 관계가 성사됩니다.

    함수[from Wikipedia]


    수학에서 직사각형의 넓이(S)와 둘레(L)를 구하는 함수를 한번 생각해봅시다.

    밑변과 높이를 각각 w와 h라 표시하면

    $$S= w \times h \\ L=2w + 2h$$

    가 되겠죠.


    위 2개 함수들을 Racket의 방식으로 만들어봅시다.


    우선 Racket의 함수를 만드는 방식은

    (define (함수이름 매개변수) 함수표현식)

    과 같다.


    우선 S과 L는 아래 식처럼 나타낼 수 있다.

    관습에 따라 각각 f(x), g(x)의 형태를 취하겠습니다.

    $$S = f(w, h) = w \times h \\
    L = g(w,h) = 2w + 2h$$

    여기서 둘의 생김새가 굉장히 유사하며,

    수식

    Racket

    함수이름

    f, g

    함수이름

    독립변수

    w, h

    매개변수

    함수수식

    w*h, 2w+2h

    함수표현식

    종속변수

    S, L

    결과값


    관계임을 알 수 있습니다.


    이제 Racket 함수로 바꾸어 봅시다.

    Code 3.1

    (define (f w h) (* w h))
    (define (g w h)
      (+ (* 2 w)
         (* 2 h)))

    쉽죠?


    그런데 실행해보면 아무일도 일어나지 않습니다.

    함수만 만들어졌지 쓰지 않았기 때문이죠.

    * 대화창 영역에 실행시키면 define은 사용할 수 없다고 나옵니다. 그 이유론, 만약 대화창 영역에서 함수를 만들 수 있다면 그 후 모든 결과 값에 영향을 미칠 수 있는 걸 들 수 있습니다.


    한번 예제를 짜보고 그에 맞게 나오는지 확인해봅시다.

    w, h

    1, 1

    1, 2

    2, 1

    2, 2

    ...

    f(w,h), g(w,h)

    1, 4

    2, 6

    2, 6

    4, 8

    ...


    이렇게 테스트 케이스를 만드는 작업은 함수를 잘못 만들어진 경우, 그 이유를 찾기 쉽게 만듭니다.

    그래서 함수를 만든다면 테스트 케이스를 만들어서 테스트를 해보시기 바랍니다.

    * 사실 함수를 만들기 전에 미리 테스트 케이스를 만들어야 하지만, 여기 예제에서는 테스트 케이스 만드는 것을 중간에 넣는 게 집중을 흐릴수 있어 함수를 만든후 작성하였습니다.


    Code 3.2

    (f 1 1)
    (g 1 1)
    
    (f 1 2)
    (g 1 2)
    
    (f 2 1)
    (g 2 1)
    
    (f 2 2)
    (g 2 2)

    사용법은 예전에 쓰던 것과 동일합니다.

    결국 2htdp\image.rkt라는 배움꾸러미는 이런 함수들을 미리 정의하고 모아놓은 것에 불과했다는 것임을 알 수 있습니다.

    예전부터 써왔던 함수들 또한 기본적으로 정의가 되어있었던 것이고요.

    +.

    원래 +. 파트는 안읽어보아도 된다고 했지만 여기는 왠만하면 읽어보았으면 합니다.

    그리 어렵지 않아요.


    프로그래밍을 하다보면 Parameter(매개변수)와 Argument(인자)라는 단어가 계속 나올 것입니다.

    혹시나 궁금하다면 미리 알아놓도록 합시다.

    https://black7375.tumblr.com/post/169349889805/parameter-vs-argument


    Racket처럼 바꾸어보자면

    (define (fn x y)
      ;;표현식(expression)
      )
    
    (fn 1 2.3)

    의 형태이겠죠.


    ++.

    내가 생각하는 적절한 함수의 길이

    https://black7375.tumblr.com/post/162403828110/내가-생각하는-적절한-함수의-길이

    https://black7375.tumblr.com/post/162403828110/내가-생각하는-적절한-함수의-길이

    함수는 1~2가지의 기능만 해야한다.

    함수가 길어지면 복잡해지고 구조를 알아보기 힘들어지는 것은 필연적인 결과다.


    +++.

    Racket에서 함수는 무조건 1개이상의 매개변수가 필요하고, 1개의 결과값이 반환(Return)되지만,

    C언어 계열에서는 void라는 키워드를 사용하면 매개변수 없이, 결과값 없이 함수만 사용할 수 있습니다.

    파스칼이나 SQL 쪽에선 반환값이 있으면 Function, 없으면 Procedure로 구분합니다.


    2.2 함수 업그레이드.

    그런데 이 함수들에게는 몇가지 문제점이 존재합니다.

    아니, 잘못한게 없는데 뭔말이지?!?!?!?!!?!


    틀린게 아니라고 문제가 없다는건 아니다.

    직관적이거나 우아하지 않다는게 문제다.

    하나씩 문제점을 짚어보도록 하자.


    2.2.1 직관성.

    우리는 지금까지 미리 정의된 함수들을 사용해왔다.

    그것도 무척 자연스럽게.


    왜 그럴까요?

    우리 모두에게 통용될만한 기호나 단어들을 써왔기 때문이죠.


    우리가 정의하는 함수나 변수의 이름도 그러해야 합니다.

    대부분의 수학문제를 풀때야 1문제에 나오는 함수가 수십~수십만개 단위로 잘나오지 않습니다.

    그러나 프로그래밍을 할때는 규모가 커지는 경우가 많기 때문에 '작명(네이밍)'하는 행위는 굉장히 중요합니다.


    또한 다른 사람이 함수나 변수의 이름을 봤을때 바로 알아보지 못하면 함수내부의 구조를 살펴보아야 하기 때문에 낭비되는 시간이 굉장히 많이 생깁니다.

    만약 다른 사람에게 보여주지 않을거라 해도 '미래의 자신'이라는 또 다른 강적이 존재합니다. ㅎㅎ

    (과연 천재가 아닌 이상 모든걸 기억할 수 있을까?)


    이제 작명을 하는 방법을 한번 알아보자.


    문장에서 그 문장의 요약을 할때 꼭 필요한 요소를 뽑는다고 하면

    동사. 명사(주어, 목적어).

    를 들 수 있다.


    절대적이지는 않지만

    함수는 기능을 하는 단위며 결과값을 반환(Return)하기 때문에 동사(기능)와 주어(문장의 주체->결과)에 가깝고,

    변수는 구성요소이므로 명사에 가까운 특성을 가진다고 볼 수 있다.


    정말 이것만 알아도 어느정도 괜찮은 작명을 할 수 있다.

    (데이터베이스 모델링할 때 얻은 아이디어 입니다. - 개체도출과 관계설정.)


    아까 우리가 만든 함수의 요구사항을 설명해보자.


    - 직사각형의 크기는 가로 * 세로다.

    - 직사각형의 둘레는 가로*2 + 세로*2다.

    함수를 실행시켰을때 나오는 결과나 함수의 동작을 가장 설명하는 것은 직사각형의 크기와 둘레이며,

    변수는 목적어인 가로와 세로이다.


    그렇다면 함수의 이름은 rectangle-size, rectangle-length, 변수의 이름은 width, height가 적당하다고 할 수 있다.

    * 사실 S, L. 얘들도 Size, Length, w와 h는 width, height의 줄임말로 쓰였다.


    rectangle이 너무 길다면 rect-size, rect-length처럼 처음 한두음절을 이용하는 것도 괜찮다. 이름이 너무 길면 읽는데 방해되기 때문.


    결과:


    Code 3.3

    (define (rect-size    width height)
      (* width height))
    (define (rect-length  width height)
      (+ (* 2 width  )
         (* 2 height )))

    작명은 존재성을 불어넣어주는 행위이다.

    가치를 부여하는 것이므로 항상 조심조심!!

    +.

    이름 짓는 것이 쉬워보이지만



    라는 말이 있다.ㅋㅋ

    그만큼 중요하단 말이겠죠.

    ++.

    언어마다 이름을 짓는 스타일이 조금씩 다르다.

    Racket이나 Lisp 계열은 rect-size였다면, C언어쪽은 rect_size, 자바는 rectSize, 파스칼은 RectSize같은 방식을 사용한다.

    이를 네이밍 컨벤션이나 코드 컨벤션이라고 하는데 기본 함수나 기존 코드들과의 통일성을 위하여 어느정도 맞추어주는 것이 좋다.

    그냥 문체를 비슷하게 맞춘다고 생각하자.


    2.2.2 간결성.

    이과 계열에서 우아하다, 아름답다는 말은 간결하고 기존이론과 들이맞는다는 것이다.


    우리가 만들어 놓은 함수중에 문제가 되는 것은 rect-length다.

    아까 g(w, h)에서

    $$ \begin{matrix} g(w, h) &=& 2w + 2h \\ &=& 2(w + h) \end{matrix}$$

    처럼 수식 부분에서 반복되던 패턴을 줄일 수 있기 때문이다.


    반복되는 패턴이 존재하면 1개 고칠것을 여러개 고쳐야 하고(유지보수), 1번에 처리할 수 있는 것을 여러번에 걸쳐 처리(성능)해야하므로 프로그램을 만들때 반드시 처단해야하는 녀석이다.

    사람도 기계도 뒷감당하기가 힘들어진다.


    결과:

    Code 3.4

    (define (rect-length-upgrade width height)
      (* 2 (+ width height)))

    * rect-length가 이미 있어서 rect-length-upgrade라 하였습니다.


    2.3 변수(variable)와 상수(constant).

    함수는 변수와 변수끼리의 관계를 나타낸다.

    그래서 변수에 대한 이해를 가지는 것은 필요하다.


    2.3.1 변수.

    변수(variable)는 말그대로 변할수 있는 수이다.

    변할수 있다는 말을 뒤집어 보면 무조건적으로 변하지는 않을수도 있다는 뜻을 가진다.

    무조건적으로만 변한다면 랜덤(Random)인 난수이겠지;;


    만약 함수내에서 일정한 패턴으로 변수가 반복된다면?

    간결성이란 항목에서 배웠듯 중복되는 행위는 최대한 줄여야 하고 따로 분리를 해야한다.


    만약 환율과 관계된 프로그램을 만들게된다고 하면 원, 달러, 유로등 각 통화에 대한 변수가 반복이 될것이다.

    그렇다면 각 변수(won, dollar, euro 등)을 한번에 관리하는게 좋지 않겠는가?


    Racket에서는

    (define 변수이름 변수값)

    * 변수값은 정확히 말하자면 표현식을 의미한다.

    로 변수를 선언하여 사용할 수 있다.


    예:

    Code 3.5

    (define won    1000)
    (define dollar 1   )
    (define euro   0.8 )
    won
    dollar
    euro

    결과: 1000, 1, 0.8

    이제 won은 1000, dollar는 1, euro는 0.8로서 사용할 수 있다.

    여기서도 추후 중복될 작업을 줄이려면,


    Code 3.6

    (define usd 1           )
    (define eur (* 0.8  usd))
    (define krw (* 1000 usd))

    * 만약, 같은 이름으로 다시 선언하게되면

    this name was defined previously and cannot be re-defined

    이라고 재선언을 할 수 없다는 에러가 뜨므로 다른 이름을 사용했다.

    * won은 KRW, dollar는 USD, euro는 EUR로 보통 표시한다.


    처럼 달러화만 바꾸면 전체 환율이 변환되도록 할 수 있다.


    변수를 알아보았으니, 변수와 상대적으로 대비되는 개념인 상수를 알아보도록 하자.


    2.3.2 상수.

    상수(constant)는 일정한 수이다.

    상수로 만들 수 있는 수는 수학에서의 π(PI, 3.141592..)나 물리의 중력가속도(g, 9.8...)같은 수.


    이러한 상수도 중복된 패턴(일정한 값)을 가진 상태에서 자주 쓰이는 것이고, 값 정밀도등도 편히 관리하기 위해 선언을 해주는 것이 좋습니다. '이름을 부여'하면 어떠한 상수인지 직관적으로 알아볼 수 있기도 하고요.


    예:

    Code 3.7

    (define PI #i3.14)

    * Racket에서는 자주 쓰이는 상수인 pi, e 같은게 미리 정의되어 있긴 하다.


    여기까지 했다면, Racket으로 만들어지는 프로그램은 함수와 변수로 이루어진 복합체라는 것을 알 수 있다.


    +.

    Racet에서는 변수나 상수 둘다 같은 방식으로 선언했지만, 다른 언어는 다른 방식으로 선언합니다.

    그 이유로는 Racket에선 함수 표현식 내부에서 선언(define)된 변수를 수정할 수 없지만, 다른 언어에서는 a의 값이 1이라 되어 있을때, a=2 또는 a=a+1같은 방식으로 변수를 수정할 수 있습니다.

    이 말은, Racket에서 선언하는 모든 변수는 상수처럼 동작한다는 것을 의미합니다.


    값의 변경을 하려면 set! 이란 함수(void를 반환)를 사용해야 하며, 고급 학생 버전에서 사용해볼 수 있습니다.

    (define name 1)
    name
    (set!   name 2)
    name

    결과: 1, (void), 2


    다른 언어에서는 주로 const라는 키워드를 추가로 붙여 상수로 만들고, 값(상태)의 변경을 막습니다.


    ++.

    컴퓨터 과학쪽에서는 함수가 결과값말고 다른 상태를 변경시키면 부작용(Side Effect, 보통 사이드 이펙트라 부름)이 생겼다고 한다. 사이트 이펙트가 생기면, 예측되지 못한 값이 나올 확률이 올라가 버그가 나오기 쉽고, 컴파일러가 최적화를 하기 힘들어진다.

    근삿값이 정확한 값과 함께 계산하면 근삿값이 나오듯이 사이드 이펙트도 사이드 이펙트가 일어난 함수가 사용되면 퍼져나간다.


    변수의 변경, 저장된 데이터의 변경 이런게 다 상태의 변경에 속한다.


    Racket같이 함수형 언어란 패러다임에 속하는 언어들은 Side Effect를 줄이기 위하여 특정한 경우를 제외하고 변수를 바꾸는 것을 막고 있습니다.

    +++.

    상수를 만들때 관습은 대문자를 사용하는 것입니다.

    아마 변수와 쉽게 구분하기 위해 생긴것이겠죠.


    2.4 조건 함수.

    프로그램들은 여러 상황의 문제를 조건에 맞게 판단해야 한다.

    이때 조건이란 것은 맞는지, 틀린지를 따지는 것이기 때문에 불린과 땔래야 땔수 없는 관계라 할 수 있다.

    우리가 확장된 계산에서 봤던 등호표시(<,<=, >, >=, =)나 불린값에서 봤던 논리계산(and, or, not등)이 그러한 예라 할 수 있죠.


    그러나 저러한 조건 처리 방식은 여러 조건 가운데 어떤 조건이 성립하는지 결정하는 것은 힘들다.

    이걸 결정해주는 함수를 조건함수(Conditonal Function)이라 하고, 조건표현을 통해서 사용할 수 있다.


    조건표현 사용법.

    (cond
      [질문 답]
      ...
      [질문 답]
    [else 답])

    * 대괄호를 소괄호로 해도 되나, 대괄호로 사용하는게 가독성 측면에서 더 좋다.

    * else는 위의 질문들이 모두 false일때 답(결과값)을 반환하며 없어도 상관 없다.

    의 방법으로 사용한다.

    * 위에서 아래로 차례대로 조건을 검사한다.


    문제를 풀어봅시다.

    문제 3.1

    환전을 하기위해 프로그램을 짜는데, 1달러가 0.7 유로보다 작으면 'EUR을 출력하고, 1000원이하이면 'KRW를 출력하며, 6.5위안을 초과하면 'CNY를 출력한다. 그 이외의 결과면 'USD를 출력한다.
    단, 1달러에 6위안(cny)라 정의한다.

    * 환율을 위해 usd, eur, krw, cny를 사용한다.

    코드:

    Code 3.8

    (define cny (* 6 usd))
    (cond
      [(<  usd (/ 0.7  eur)) 'EUR]
      [(<= usd (/ 1000 krw)) 'KRW]
      [(>  usd (/ 6.5  cny)) 'CNY]
      [else                  'USD])

    답: 'KRW


    이번에는 조건함수를 한번 만들어봅시다.

    문제 3.2

    달러로 환전을 할때, 내는 금액에 따라 차등적으로 수수료를 적용하려합니다.

    소금액인 10만원(100000) 이하나 큰 금액인 1억(100000000) 초과는 3%, 10~100만원(1000000) 이하는 4%, 100만~1000만원(10000000) 이하는 5% 을 만들고, 같은 방식으로 5000만원(50000000) 이하는 4%, 1억이하는 3.5%의 수수료를 부과할때, 이천만원(20000000)을 환전한 결과는?

    프로그램 이름은 krw->usd라 하며, 전에 만든 환율을 이용한다.

    코드:

    Code 3.9

    (define (krw->usd won)
      (cond
        [(or (>= 100000    won)
             (<  100000000 won)) (/ (- won (* 0.03  won)) krw)]
        [(>=        1000000 won) (/ (- won (* 0.04  won)) krw)]
        [(>=       10000000 won) (/ (- won (* 0.05  won)) krw)]
        [(>=       50000000 won) (/ (- won (* 0.04  won)) krw)]
        [(>=      100000000 won) (/ (- won (* 0.035 won)) krw)]))
    
    (krw->usd 20000000)

    결과: 19200


    뚜뚱~ 삼연타 문제!

    이게 다 끝나간다는 증거랍니다. 조금더 힘을 내요!!

    문제 3.3

    Code 2.21의 코드가 잘만든 것이라 생각하시나요?

    아니라면 코드를 수정해보고, 그 이유를 대봅시다.

    힌트:

    그야 잘만든게 아니니까 문제가 나왔겠죠?

    1. 1가지 논리적인 오류가 있습니다.

    2. 하나의 함수에 미리 정의해놓은 여러가지 함수가 들어갈수 있습니다.

    3. 함수 업그레이드와 변수와 상수 참고.

    4. 할 수 있으면 가독성도 좋게 만들어봅시다.

    * 논리적 오류는 구문(문법)이 틀렸다는 것이 아니라 알고리즘(풀이과정)에서 잘못된 것이 있다는 것 입니다.

    * 외부 동작은 바꾸지 않으면서, 내부의 구조를 바꾸는 것을 리펙토링(Refactoring)이라 합니다. 여기선 리펙토링을 요구하는 것.


    코드:

    Code 3.10

    ;----------exchange
    (define exch-level1 100000   )
    (define exch-level2 1000000  )
    (define exch-level3 10000000 )
    (define exch-level4 50000000 )
    (define exch-level5 100000000)
    
    (define (exch-usd money exch-rate)
      (/ money exch-rate))
    
    ;----------charge
    (define charge-level1 0.03 )
    (define charge-level2 0.035)
    (define charge-level3 0.04 )
    (define charge-level4 0.05 )
    
    (define (charge-calc charge-level money)
      (- money (* charge-level money)))
    
    ;----------krw->usd exchange
    (define (krw->usd2 won)
      (cond
        [(>  0           won)      'Error-*less-than-0*]
        [(or (>= exch-level1 won)
             (<  exch-level5 won)) (exch-usd (charge-calc charge-level1 won) krw)]
        [(>= exch-level2 won)      (exch-usd (charge-calc charge-level3 won) krw)]
        [(>= exch-level3 won)      (exch-usd (charge-calc charge-level4 won) krw)]
        [(>= exch-level4 won)      (exch-usd (charge-calc charge-level3 won) krw)]
        [(>= exch-level5 won)      (exch-usd (charge-calc charge-level2 won) krw)]))
    
    ;----------Test
    (krw->usd2 20000000)

    결과: 19200

    * 세미콜론(;)로 시작하면 컴퓨터가 그 줄은 무시하며, 주석이라 부릅니다.


    제 코딩 스타일대로 리펙토링을 해보았습니다.

    살짝 길어지긴 했지만, 관리와 가독성 측면에서는 훨씬 더 좋아졌습니다.

    솔직히 길이가 길어진건 강좌를 위해 오버를 한 감도 완전히 없진 않습니다.


    1. 논리적 오류.

    0보다 작아지면, 환전을 할수 없겠죠?

    그래서 저는 에러가 났다고 보여주었습니다.


    논리적 사고 원리.

    논리적으로 생각하는 방법을 크게 두가지로 구분지어 나타낼 수 있다.


    확실성의 원리.

    명료성(섞이지 않았는가), 충분근거율(충분한 근거를 가지고 있는가)로 나눌 수 있다.

    • 충분근거율.

    명료성은 다른 사고와 섞이지 않았는가를 구분하는 것으로 비교적 간단하다.

    하지만 '충분한 근거'는 상당히 애매모호한 개념이라 설명이 필요하다.


      • 이성 vs 경험.
        이성적 확실성은 그 자체로 확증되므로, 필연성(어느 경우에나 반드시)을 가지지만, 경험적 확실성은 사실성(그 경우에는)만을 함유한다.
      • 직접적 vs 간접적.
        매개에 따라 보증한다.
        매개는 확실성의 입증 수단으로 이성, 경험일 수 있다.
        어떠한 현상을 직접 관찰, 증명한 경우는 직접적 확실성을 책에서 보거나 듣기만 한 경우는 간접적 확실성이라 할 수 있다.


    정합성의 원리.

    사고의 앞뒤가 맞게 수행하느냐를 확인하는 것이다.

    이 때 다른 사고는 나의 다른 사고 뿐만이 아니라 다른 사람의 사고도 포함한다.

    정합성의 원리는 동일률, 모순율, 배중률로 나눌 수 있다.


    • 동일률.
      어떤 것도 자기 자신과 같다는 것이다.
      사람은 사람, 동물은 동물, 식물은 식물이라는 것.
      만약 식물을 동물이라고 했다면 정합하지 않다고 할 수 있다.
      하지만 사람은 동물과 같은 포함관계는 정합하다.

      주의해야 할 점은 동일성을 같은 관점으로 바라보아야 한다는 것이다.
      문맥상 앞에서 말하는 것과 뒤에서 말하는 것이 다르다면 분리해여 생각해야한다.
      이는 추후에 나올 Scope 개념으로 생각할 수 있다.

      예시. 난중일기에서 아주 유명한 부분.

      必死則生 반드시 죽으려 하는 자는 살고
      必生則死 반드시 살고자 하는 자는 죽을 것이요

      반드시 (전투에 상관을 신뢰하고 임하여) 죽으려 하는 자는 (전투에서 승리하여) 살고
      반드시 (도주해서라도) 살고자 하는 자는 (군법으로) 죽을 것이요
    • 모순율.
      어긋나는 것이 속할 수 없으며, 어긋나는 성질이 함께 속할 수 없다는 것이다.
      모순의 원형인 어떤 방패라도 뚫을 수 있는 창과 어떤 창도 막을수 있는 방패가 대표적인 예.

      역시 동일한 관점으로 판단하는 것이 중요하다.
      흔히 말하는 웃프다(웃을만 하지만 슬프다)가 대표적인 예로, 즐거움이란 감정과 슬프다는 감정은 공존할 수 없지만 다른 관점으로 보았을 때는 모순되지 않는다.
    • 배중률.
      중간(혹은 3자)는 배제된다, 모순 관계에 있는 두 생각이 모두 틀릴 수 없다는 것이다.
      위에서 나온 최강의 창과 방패란 가정에서 둘 중 하나가 틀리다면 나머지 하나는 정답이다.

      역시 배중률에서도 주의할 점이 존재한다.
      앞서 나온 다른 관점과 모순이 아닌 관계일 때이다.
      배중률은 모순관계인가를 주의깊게 살펴야 적용가능하다.


    2. 변수선언.

    저는 변수를 선언하여, 만약 수수료(charge-level)나 수수료의 기준금액(exch-level)이 바뀌면 변수만 조정하면 되도록 하였습니다.

    이말은 변수만 바꾸면 되며 굳이 함수내부의 구조까지 알 필요가 없다는 뜻입니다.

    관리의 측면에서 큰 이득입니다.


    3. 함수 모듈화.

    반복되는 함수를 나뉘어 놓았습니다.

    우리가 실제로 사용하는 주기능을 하는 함수(krw->usd)와 보조해주는 함수(exch-money, charge-calc)로 나눴습니다.

    주기능을 하는 함수를 주함수(Main Function), 보조해주는 함수를 보조함수(Auxiliary Function)라 합니다.


    1개의 함수는 1~2개 정도의 기능만 해야 하며, 너무 길어지는 것은 관리하기가 힘들어져서 확장하거나 문제가 생겼을때 고치기도 힘들고, 중복된 코드의 양이 늘어날 가능성도 있습니다.


    함수를 쪼개놓으면 미리 만들어놓은 함수를 재사용하게 된다.

    재사용을 하면 같은 문제에 시간을 낭비할 필요가 없어지니 이득!!


    4. 모으기.

    여기부터는 가독성과 관련된 것입니다.


    좋은 글이 읽기 쉬운거처럼, 좋은 소스코드의 조건에도 읽기 쉬워야된다는 조건이 붙습니다.

    그래야 편하게 관리를 하고, 의도를 파악할 수 있죠.


    서로 관련이 있는 항목끼리 모아놓고 관련이 없는 것과는 줄을 띄어서 관리하는 것은 가독성 관리의 기본이라 할 수 있습니다.

    그룹화를 하게되면, 더 큰덩어리로 다룰수 있기 때문.


    줄을 띄어쓰는 것 말고, 주석으로 공간을 구분하여 서로 관련이 있는 기능들을 모아놓았습니다.


    5. 열 맞추기.

    과연 소스코드를 세로로 읽을 수 있는 방법이 없을까 하고 고민하다 제가 작년부터 도입한 방식입니다.

    글의 경우, 왼쪽정렬을 하는 것이 만들기도 편하고 가장 가독성이 좋기에 왼쪽정렬방식으로 되어있습니다.


    이 또한 서로 관련이 있는 항목끼리 열을 맞추어 가독성을 높인 것입니다.

    예) 변수선언, Cond내부 등


    좋은 코드가 되려면 관리성, 가독성, 확장성 등 여러가지를 고려해야 합니다.


    +.

    아래의 제 텀블러 포스트들을 보며 필자의 개똥철학을 느껴보자.

    그냥 싸질러놓은 거라 몇몇요소에서는 살짝 난해할 수도 있다.


    또 염두해야 할건, 강좌로 쓴다고 다 옳다는 건 아니고, 내가 좋다고 생각하는 방식을 소개하는 것뿐이다.

    프로그래밍은 은근 예술(Art)의 축에 속하여 각 사람마다 다른 철학이 첨예하게 논쟁을 벌인다.

    대표적인 것이 앞에서 언급된 네이밍 컨벤션.


    열맞춤을 이용한 가독성 향상법

    https://black7375.tumblr.com/post/168955500760/열맞춤을-이용한-가독성-향상법

    https://black7375.tumblr.com/post/168955500760/열맞춤을-이용한-가독성-향상법


    주석을 되도록 쓰지 말자.

    https://black7375.tumblr.com/post/169042924365/주석을-되도록-쓰지-말자

    https://black7375.tumblr.com/post/169042924365/주석을-되도록-쓰지-말자


    주석의 오모함.

    https://black7375.tumblr.com/post/168962832200/주석의-오모함

    https://black7375.tumblr.com/post/168962832200/주석의-오모함

    2.5 디자인 레시피 기초.

    혹시 문제 2.4를 풀때 살짝 햇갈리지 않았나요?


    프로그램의 규모가 커질수록 내부는 더 복잡해는 경우가 많기 때문에 논리적이고, 체계적으로 접근해야합니다.

    익숙해지면 쓰윽~ 구조만 짜놓고 바로 코드를 짤수도 있겠지만, 이게 잘 안되는 초보자에게는 더더욱 그렇습니다.


    프로그램 만드는 방식을 일종의 공식화를 해놓은 것을 여기선  '디자인 레시피'라고 부릅니다.(전글에 소개시켜줬습니다.)


    디자인 레시피를 이용해 2.5의 코드를 만들어봅시다.

    하지만 2.5코드의 구조는 그리 복잡하지 않기에 축소된 디자인 레시피를 사용할 것입니다.

    소잡는데 쓰는 칼을 닭잡을 때 쓸 필요가 없죠.


    축소된 디자인 레시피 .

    1. 문제분석.

    함수가 다루는 조건을 살펴본다.

    문제 분석을 통해 문제 속 모든 조건을 나열한다.


    2. 계약, 목적, 헤더.

    계약(Contract): 의미가 있는 이름을 붙이고, 정보의 입출력을 작성한다.
    문제 분석 내용을 기초로 작성합니다.
    계약에 의한 설계(Design by Contract)라는 것을 바탕으로 합니다.


    계약을 작성한 다음, 함수의 헤더(Header)를 작성합니다.

    헤더(Header): 계약을 바탕으로 함수명과 파라미터를 작성합니다.


    마지막으로,

    목적: 프로그램의 목적을 작성한다.


    3. 예시.

    2.2.1 함수 만들기에서 미리 테스트용 예제를 만들어놓을 필요가 있다고 했었습니다.

    각 조건마다 예제를 만듭시다.

    예제들은 조건마다 1개씩은 만들고, 조건 구간의 경계값을 포함해야 합니다.

    조건의 경계구간이 들어가는 예를 작성해야 오류확인이 쉽겠죠.


    4.  함수 정의.

    문제 2.5의 코드는 짧습니다.

    때문에 템플릿과정은 생략된뒤 진행됩니다.


    매개변수(Parameter)를 사용해 각 조건의 틀을 완성 후, 구현을 합니다.


    5. 테스트.

    미리 만들어놓던 예시를 가지고, 테스트를 한다.


    6. 정리.

    필요없는 부분을 제거하고, 코드를 보기좋게 정리합니다.


    한번 만들어봅시다.


    1. 문제분석.

    수수료를 걷을때, 조건에 따라 수수료가 붙는다.

    이때 저는 간결히 표현하기 위해 수식으로 표현하도록 하겠습니다.

    $$f(x) = \begin{cases}
    [0, & 100000], & 3\%  \\
    (100000, & 1000000], & 4\% \\
    (1000000, & 10000000], & 5\% \\
    (10000000, & 50000000], & 4\% \\
    (50000000, & 100000000], & 3.5\% \\
    (100000000, & +\infty), & 3\%
    \end{cases}$$

    []는 닫힌(폐)구간, ()는 열린(개)구간입니다.


    2. 계약, 목적, 헤더.


    계약.

    krw에서 usd로 바뀐 '결과값'이 나오기 때문에 krw->usd를 함수명으로 정하였습니다.

    매개변수인 원화와 결과값인 달러 모두 '숫자'라는 데이터(자료형)을 사용합니다.

    ;;Constract: krw->usd ; number->number

    세미콜론을 사용하여 함수이름과 입출력을 구분하였습니다.


    헤더.

    krw->usd인 함수의 매개변수부분까지 작성합니다.

    이때 저는 들어오는 돈의 단위가 원(won)이므로, won이란 이름의 파라미터를 사용했습니다.

    (define (krw->usd won) 표현식)

    함수정의 및 구현을 할 때 '표현식'부분을 채워갈 것입니다.


    목적.

    이 프로그램의 목적은 원화를 달러로 환전하는 것입니다.

    또한 금액에 따른 수수료를 내야 합니다.

    ;;Purpose: Exchange KRW to USD and make a commission based on the amount.


    3. 예시.

    우리가 만들 함수에서 경계값은 수식에서의 0, 100000, 1000000, 10000000, 50000000, 100000000입니다. 그리고 추가적으로 음수값과 1억이 넘는 값을 넣어보는 것입니다.(양 극단의 경계값이라 할 수 있죠.)


    -1000: 에러. 환전안됨.

    0: 0, 3%

    100000: 3%

    1000000: 4%

    10000000: 5%

    50000000: 4%

    100000000: 3.5%

    200000000: 3%

    라는 것을 염두해보고 8개를 미리 계산해봅시다.


    공식은 다음과 같습니다.

    $$\frac{금액 - (수수료 \times 금액)}{krw(1000)}$$


    그리고, 계산한 결과를 적어줍니다.

    ;;Example: Input     -> Output
    ;;         -1000     -> Error
    ;;         0         -> 0
    ;;         100000    -> 97
    ;;         1000000   -> 960
    ;;         10000000  -> 9500
    ;;         50000000  -> 48000
    ;;         100000000 -> 96500
    ;;         200000000 -> 194000

    가 됩니다.


    4. 함수 정의.

    먼저 관리의 편의성을 위해 수수료 기준 금액과 수수료를 변수로 선언해줍니다.


    수수료 기준 금액은 교환레벨(Exchange Level)이라는 의미에서 exch-level, 수수료는 요금이라는 의미에서 charge-level이라는 이름을 붙였습니다. 만약 한국 돈말고 다른 나라의 돈도 바꾸어야 했다면, exch-level 대신 krw-level을 사용했을겁니다. Commission이란 단어를 수수료의 의미로 사용하지 않은 이유는 길기도 하고, Charge보다 의미를 모르는 사람들이 많을 확률이 높기 때문입니다.

    (define exch-level1 100000)
    (define exch-level2 1000000)
    (define exch-level3 10000000)
    (define exch-level4 50000000)
    (define exch-level5 100000000)
    
    (define charge-level1 0.03)
    (define charge-level2 0.035)
    (define charge-level3 0.04)
    (define charge-level4 0.05)


    변수 설정과정은 끝났고,

    우선 미리 작성해놓았던 헤더의 함수표현식 부분을 각 조건들을 표시해보았습니다.

    (define (krw->usd2 won)
      (cond
        [(> 0 won) ...]
        [(or (>= exch-level1 won)
             (< exch-level5 won)) ...]
        [(>= exch-level2 won) ...]
        [(>= exch-level3 won) ...]
        [(>= exch-level4 won) ...]
        [(>= exch-level5 won) ...])


    이제 응답 부분을 채워야 합니다.

    응답 부분은 수수료를 빼고, 환전을 해주는 기능입니다.

    2가지의 기능이 들어간다는 것을 알 수 있습니다.(수수료를 뺌, 환전)


    저는 krw->usd2가 복잡해지는 것을 방지하기 위해 이 두 기능을 보조 함수로 만들었습니다.

    보조함수를 만들 때도 똑같은 과정을 거칩니다.


    수수료 기능은 수수료를 계산(Charge Calculate)를 해준다는 뜻에서 charge-calc로 정했습니다.

    환전 기능은 달러로 변환을 해주기 때문에 exch-usd로 하였습니다.

    이런식으로 2~3번 과정을 완료합니다.

    ;;Constract: charge-calc ; number, number->number
    ;;Purpose: Calculate money excluding commissions.
    ;;Example: Input     -> Output
    ;;         0.1,  100 -> 90
    ;;         0.03, 100 -> 97
    (define (charge-calc charge-level money) ...)
    
    ;;Constract: exch-usd ; number->usd
    ;;Purpose: Exchange money to USD with exchanging rate.
    ;;Example: Input     -> Output
    ;;         1000, krw -> 1
    ;;         0.8,  eur -> 1
    ;;         6,    cny -> 1
    (define (exch-usd money exch-rate) ...)

    그리고, 함수를 완성시키고 예제를 가지고 테스트 해봅니다.

    (define (charge-calc charge-level money)
      (- money (* charge-level money)))
    
    (define (exch-usd money exch-rate)
      (/ money exch-rate))

    * 테스트하는것은 테스트단계에 한꺼번에 적겠습니다.


    다시 krw->usd2로 돌아옵시다.

    charge-calc과 exch-usd를 가지고 응답부분을 채우면 krw->usd2가 완성됩니다.

    음수 부분은 0보다 작은 값이 들어왔다는 것을 알려주기 위해 'Error-*less-than-0*로 응답하도록 했습니다.

    (define (krw->usd2 won)
      (cond
        [(> 0 won) 'Error-*less-than-0*]
        [(or (>= exch-level1 won)
             (< exch-level5 won)) (exch-usd (charge-calc charge-level1 won) krw)]
        [(>= exch-level2 won) (exch-usd (charge-calc charge-level3 won) krw)]
        [(>= exch-level3 won) (exch-usd (charge-calc charge-level4 won) krw)]
        [(>= exch-level4 won) (exch-usd (charge-calc charge-level3 won) krw)]
        [(>= exch-level5 won) (exch-usd (charge-calc charge-level2 won) krw)]))

    완성되었습니다!!


    5. 테스트.

    안적었었던 charge-calc과 exch-usd 테스트 코드입니다.[보기 쉬우라고 미리 열맞춤을 적용시켜놓았습니다.]

    예제를 기반으로 만들어져있죠?

    (charge-calc 0.1 100) ;90
    (charge-calc 0.03 100) ;97
    
    (exch-usd 1000 krw) ;1
    (exch-usd 0.8  eur) ;1
    (exch-usd 6    cny) ;1


    새로만든 krw->usd2 예전에 만들어놓은 krw->usd를 테스트 코드로 비교해보았습니다.

    (krw->usd2 -1000)     ;Error
    (krw->usd2 0)         ;0
    (krw->usd2 100000)    ;97
    (krw->usd2 1000000)   ;960
    (krw->usd2 10000000)  ;9500
    (krw->usd2 50000000)  ;48000
    (krw->usd2 100000000) ;96500
    (krw->usd2 200000000) ;194000
    
    (krw->usd  -1000)     ;Error -> -0.97 Problem!!!
    (krw->usd  0)         ;0
    (krw->usd  100000)    ;97
    (krw->usd  1000000)   ;960
    (krw->usd  10000000)  ;9500
    (krw->usd  50000000)  ;48000
    (krw->usd  100000000) ;96500
    (krw->usd  200000000) ;194000

    (krw->usd -1000)에서는 -0.97이라는 답이 나오며 원하던 모습과 다르게 동작하여 문제가 생겼습니다.

    음수를 처리하는 것을 깜박하여 생긴것으로 디자인 레시피의 과정을 따라가지 않는다면 충분히 나올수도 있을 법만한 실수입니다.


    테스트까지 모두 끝났으니 답을 한번 구해봅시다.

    (krw->usd2 20000000)

    결과: 19200


    6. 정리.

    디자인 레시피의 마지막 과정으로 코드를 깔끔하게 정리해봅시다.

    유종의 미를 거둘 시간~


    현재 여러분의 다음과 같이 생겼으며 상당히 어지러울 것입니다.

    (define exch-level1 100000)
    (define exch-level2 1000000)
    (define exch-level3 10000000)
    (define exch-level4 50000000)
    (define exch-level5 100000000)
    
    (define charge-level1 0.03)
    (define charge-level2 0.035)
    (define charge-level3 0.04)
    (define charge-level4 0.05)
    
    ;;Constract: charge-calc ; number, number->number
    ;;Purpose: Calculate money excluding commissions.
    ;;Example: Input     -> Output
    ;;         0.1,  100 -> 90
    ;;         0.03, 100 -> 97
    (define (charge-calc charge-level money)
      (- money (* charge-level money)))
    
    (charge-calc 0.1 100) ;90
    (charge-calc 0.03 100) ;97
    
    ;;Constract: exch-usd ; number, number->usd
    ;;Purpose: Exchange money to USD with exchanging rate.
    ;;Example: Input     -> Output
    ;;         1000, krw -> 1
    ;;         0.8,  eur -> 1
    ;;         6,    cny -> 1
    (define (exch-usd money exch-rate)
      (/ money exch-rate))
    
    (exch-usd 1000 krw) ;1
    (exch-usd 0.8 eur) ;1
    (exch-usd 6 cny) ;1
    
    ;;Constract: krw->usd ; number->number
    ;;Purpose: Exchange KRW to USD and make a commission based on the amount.
    ;;Example: Input     -> Output
    ;;         -10       -> Error
    ;;         0         -> 0
    ;;         100000    -> 97
    ;;         1000000   -> 960
    ;;         10000000  -> 9500
    ;;         50000000  -> 48000
    ;;         100000000 -> 96500
    ;;         200000000 -> 194000
    (define (krw->usd2 won)
      (cond
        [(> 0 won) 'Error-*less-than-0*]
        [(or (>= exch-level1 won)
             (< exch-level5 won)) (exch-usd (charge-calc charge-level1 won) krw)]
        [(>= exch-level2 won) (exch-usd (charge-calc charge-level3 won) krw)]
        [(>= exch-level3 won) (exch-usd (charge-calc charge-level4 won) krw)]
        [(>= exch-level4 won) (exch-usd (charge-calc charge-level3 won) krw)]
        [(>= exch-level5 won) (exch-usd (charge-calc charge-level2 won) krw)]))
    
    (krw->usd2 -1000) ;Error
    (krw->usd2 0) ;0
    (krw->usd2 100000) ;97
    (krw->usd2 1000000) ;960
    (krw->usd2 10000000) ;9500
    (krw->usd2 50000000) ;48000
    (krw->usd2 100000000) ;96500
    (krw->usd2 200000000) ;194000
    
    (krw->usd  -1000) ;Error -> -0.97 Problem!!!
    (krw->usd  0) ;0
    (krw->usd  100000) ;97
    (krw->usd  1000000) ;960
    (krw->usd  10000000) ;9500
    (krw->usd  50000000) ;48000
    (krw->usd  100000000) ;96500
    (krw->usd  200000000) ;194000
    
    (krw->usd2 20000000)


    코드제거.

    첫번째로 해야 할 것은 쓸데없는 코드를 제거하는 것입니다.

    쓸데없는 코드는 실행했을때 아무런 의미도 가지지 않는 코드입니다.

    주석이란 뜻이죠.


    그중에서도 가장 쓸모가 없는 것은 예제를 나타낸 코드입니다.

    이미 테스트코드와 예상답을 적어놓았으니 어찌보면 중복된 의미를 가진 것을 제거한다고 볼 수도 있습니다.


    예제코드 제거결과.

    (define exch-level1 100000)
    (define exch-level2 1000000)
    (define exch-level3 10000000)
    (define exch-level4 50000000)
    (define exch-level5 100000000)
    
    (define charge-level1 0.03)
    (define charge-level2 0.035)
    (define charge-level3 0.04)
    (define charge-level4 0.05)
    
    ;;Constract: charge-calc ; number, number->number
    ;;Purpose: Calculate money excluding commissions.
    (define (charge-calc charge-level money)
      (- money (* charge-level money)))
    
    (charge-calc 0.1 100) ;90
    (charge-calc 0.03 100) ;97
    
    ;;Constract: exch-usd ; number, number->usd
    ;;Purpose: Exchange money to USD with exchanging rate.
    (define (exch-usd money exch-rate)
      (/ money exch-rate))
    
    (exch-usd 1000 krw) ;1
    (exch-usd 0.8 eur) ;1
    (exch-usd 6 cny) ;1
    
    ;;Constract: krw->usd ; number->number
    ;;Purpose: Exchange KRW to USD and make a commission based on the amount.
    (define (krw->usd2 won)
      (cond
        [(> 0 won) 'Error-*less-than-0*]
        [(or (>= exch-level1 won)
             (< exch-level5 won)) (exch-usd (charge-calc charge-level1 won) krw)]
        [(>= exch-level2 won) (exch-usd (charge-calc charge-level3 won) krw)]
        [(>= exch-level3 won) (exch-usd (charge-calc charge-level4 won) krw)]
        [(>= exch-level4 won) (exch-usd (charge-calc charge-level3 won) krw)]
        [(>= exch-level5 won) (exch-usd (charge-calc charge-level2 won) krw)]))
    
    (krw->usd2 -1000) ;Error
    (krw->usd2 0) ;0
    (krw->usd2 100000) ;97
    (krw->usd2 1000000) ;960
    (krw->usd2 10000000) ;9500
    (krw->usd2 50000000) ;48000
    (krw->usd2 100000000) ;96500
    (krw->usd2 200000000) ;194000
    
    (krw->usd  -1000) ;Error -> -0.97 Problem!!!
    (krw->usd  0) ;0
    (krw->usd  100000) ;97
    (krw->usd  1000000) ;960
    (krw->usd  10000000) ;9500
    (krw->usd  50000000) ;48000
    (krw->usd  100000000) ;96500
    (krw->usd  200000000) ;194000
    
    (krw->usd2 20000000)


    모으기.[행 단위 정리]

    다음으로 해야 할 일은 비슷한 의미를 가지는 코드들을 모으는 것 입니다.

    여기서 가장 먼저해야 할 것은 진짜 작동하기위한 코드와 테스트용 코드를 분리하는 것입니다.

    이때 계약(Constract)과 목적(Purpose)가 있는 코드를 테스트 코드와 함께 분리합니다.


    계약과 목적부분의 주석을 테스트 코드와 함께 분리하는 이유는 테스트를 해보는 사람에게 함수의 계약과 목적부분은 일종의 문서처럼 작용할 수 있기 때문입니다.

    구현이 된 코드와 테스트하는 코드는 성질이 아예 다른 것이기 때문에 원칙적으로 파일을 분리시켜서 관리해야합니다.(원래 파일이 section3였다면 section3_test와 같이.)


    사실 프로그래밍에 익숙해지면 계약과 목적부분은 굳이 작성하지 않아도 곧바로 만들수 있으며, 모든 함수마다 계약과 목적을 설명하는 것도 힘들어 관리가 힘듭니다. 그래서 전 일관성을 위해 만들었더라도 아예 없애버리는 편입니다. 여기선 힘들게 만들었으니 한번 써먹어보고 남기고 싶다면 어떻게 해야하는지 가이드를 해주는 것에 가깝습니다.


    테스트 모으기 결과.

    (define exch-level1 100000)
    (define exch-level2 1000000)
    (define exch-level3 10000000)
    (define exch-level4 50000000)
    (define exch-level5 100000000)
    
    (define charge-level1 0.03)
    (define charge-level2 0.035)
    (define charge-level3 0.04)
    (define charge-level4 0.05)
    
    (define (charge-calc charge-level money)
      (- money (* charge-level money)))
    
    (define (exch-usd money exch-rate)
      (/ money exch-rate))
    
    (define (krw->usd2 won)
      (cond
        [(> 0 won) 'Error-*less-than-0*]
        [(or (>= exch-level1 won)
             (< exch-level5 won)) (exch-usd (charge-calc charge-level1 won) krw)]
        [(>= exch-level2 won) (exch-usd (charge-calc charge-level3 won) krw)]
        [(>= exch-level3 won) (exch-usd (charge-calc charge-level4 won) krw)]
        [(>= exch-level4 won) (exch-usd (charge-calc charge-level3 won) krw)]
        [(>= exch-level5 won) (exch-usd (charge-calc charge-level2 won) krw)]))
    
    (krw->usd2 20000000)
    
    ;;Constract: charge-calc ; number, number->number
    ;;Purpose: Calculate money excluding commissions.
    (charge-calc 0.1 100) ;90
    (charge-calc 0.03 100) ;97
    
    ;;Constract: exch-usd ; number, number->usd
    ;;Purpose: Exchange money to USD with exchanging rate.
    (exch-usd 1000 krw) ;1
    (exch-usd 0.8 eur) ;1
    (exch-usd 6 cny) ;1
    
    ;;Constract: krw->usd ; number->number
    ;;Purpose: Exchange KRW to USD and make a commission based on the amount.
    (krw->usd2 -1000) ;Error
    (krw->usd2 0) ;0
    (krw->usd2 100000) ;97
    (krw->usd2 1000000) ;960
    (krw->usd2 10000000) ;9500
    (krw->usd2 50000000) ;48000
    (krw->usd2 100000000) ;96500
    (krw->usd2 200000000) ;194000
    
    (krw->usd  -1000) ;Error -> -0.97 Problem!!!
    (krw->usd  0) ;0
    (krw->usd  100000) ;97
    (krw->usd  1000000) ;960
    (krw->usd  10000000) ;9500
    (krw->usd  50000000) ;48000
    (krw->usd  100000000) ;96500
    (krw->usd  200000000) ;194000

    테스트하는 부분을 아래에 싹다 모아놓았습니다.

    그냥 파일이 분리되었다고 생각하십시오.


    나머지부분은 기능의 의미를 기준으로 모아봅시다.

    교환레벨인 exch-level과 usd로 교환하는 exch-usd와 유사합니다.

    charge-level과 charge-calc은 수수료와 관계가 있습니다.


    기능 모으기 결과.

    (define exch-level1 100000)
    (define exch-level2 1000000)
    (define exch-level3 10000000)
    (define exch-level4 50000000)
    (define exch-level5 100000000)
    
    (define (exch-usd money exch-rate)
      (/ money exch-rate))
    
    (define charge-level1 0.03)
    (define charge-level2 0.035)
    (define charge-level3 0.04)
    (define charge-level4 0.05)
    
    (define (charge-calc charge-level money)
      (- money (* charge-level money)))
    
    (define (krw->usd2 won)
      (cond
        [(> 0 won) 'Error-*less-than-0*]
        [(or (>= exch-level1 won)
             (< exch-level5 won)) (exch-usd (charge-calc charge-level1 won) krw)]
        [(>= exch-level2 won) (exch-usd (charge-calc charge-level3 won) krw)]
        [(>= exch-level3 won) (exch-usd (charge-calc charge-level4 won) krw)]
        [(>= exch-level4 won) (exch-usd (charge-calc charge-level3 won) krw)]
        [(>= exch-level5 won) (exch-usd (charge-calc charge-level2 won) krw)]))
    
    (krw->usd2 20000000)
    
    
    ;;Constract: exch-usd ; number, number->usd
    ;;Purpose: Exchange money to USD with exchanging rate.
    (exch-usd 1000 krw) ;1
    (exch-usd 0.8 eur) ;1
    (exch-usd 6 cny) ;1
    
    ;;Constract: charge-calc ; number, number->number
    ;;Purpose: Calculate money excluding commissions.
    (charge-calc 0.1 100) ;90
    (charge-calc 0.03 100) ;97
    
    ;;Constract: krw->usd ; number->number
    ;;Purpose: Exchange KRW to USD and make a commission based on the amount.
    (krw->usd2 -1000) ;Error
    (krw->usd2 0) ;0
    (krw->usd2 100000) ;97
    (krw->usd2 1000000) ;960
    (krw->usd2 10000000) ;9500
    (krw->usd2 50000000) ;48000
    (krw->usd2 100000000) ;96500
    (krw->usd2 200000000) ;194000
    
    (krw->usd  -1000) ;Error -> -0.97 Problem!!!
    (krw->usd  0) ;0
    (krw->usd  100000) ;97
    (krw->usd  1000000) ;960
    (krw->usd  10000000) ;9500
    (krw->usd  50000000) ;48000
    (krw->usd  100000000) ;96500
    (krw->usd  200000000) ;194000

    구현의 exchage 부분이 위쪽에 위치하므로 테스트 부분의 exch-usd를 윗쪽으로 이동하여 순서대로 정렬시켰습니다.


    구분하기.[행 구역 구분]

    주석을 사용하여 코드들을 깔끔하게 구분해봅시다.


    구분하기 결과.

    ;-------------------------Code 3.10-------------------------
    ;-------------------------Solution 3.3
    ;----------exchange
    (define exch-level1 100000)
    (define exch-level2 1000000)
    (define exch-level3 10000000)
    (define exch-level4 50000000)
    (define exch-level5 100000000)
    
    (define (exch-usd money exch-rate)
      (/ money exch-rate))
    
    ;----------charge
    (define charge-level1 0.03)
    (define charge-level2 0.035)
    (define charge-level3 0.04)
    (define charge-level4 0.05)
    
    (define (charge-calc charge-level money)
      (- money (* charge-level money)))
    
    ;----------krw->usd exchange
    (define (krw->usd2 won)
      (cond
        [(> 0 won) 'Error-*less-than-0*]
        [(or (>= exch-level1 won)
             (< exch-level5 won)) (exch-usd (charge-calc charge-level1 won) krw)]
        [(>= exch-level2 won) (exch-usd (charge-calc charge-level3 won) krw)]
        [(>= exch-level3 won) (exch-usd (charge-calc charge-level4 won) krw)]
        [(>= exch-level4 won) (exch-usd (charge-calc charge-level3 won) krw)]
        [(>= exch-level5 won) (exch-usd (charge-calc charge-level2 won) krw)]))
    
    ;----------Test
    (krw->usd2 20000000)
    
    ;-------------------------Test 3.10-------------------------
    ;----------exch-usd
    ;;Constract: exch-usd ; number, number->usd
    ;;Purpose: Exchange money to USD with exchanging rate.
    (exch-usd 1000 krw) ;1
    (exch-usd 0.8 eur) ;1
    (exch-usd 6 cny) ;1
    
    ;----------charge
    ;;Constract: charge-calc ; number, number->number
    ;;Purpose: Calculate money excluding commissions.
    (charge-calc 0.1 100) ;90
    (charge-calc 0.03 100) ;97
    
    ;----------krw->usd exchange
    ;;Constract: krw->usd ; number->number
    ;;Purpose: Exchange KRW to USD and make a commission based on the amount.
    (krw->usd2 -1000) ;Error
    (krw->usd2 0) ;0
    (krw->usd2 100000) ;97
    (krw->usd2 1000000) ;960
    (krw->usd2 10000000) ;9500
    (krw->usd2 50000000) ;48000
    (krw->usd2 100000000) ;96500
    (krw->usd2 200000000) ;194000
    
    ;-----Compare with krw->usd
    (krw->usd  -1000) ;Error -> -0.97 Problem!!!
    (krw->usd  0) ;0
    (krw->usd  100000) ;97
    (krw->usd  1000000) ;960
    (krw->usd  10000000) ;9500
    (krw->usd  50000000) ;48000
    (krw->usd  100000000) ;96500
    (krw->usd  200000000) ;194000

    의미별로 구분되어 쉽게 구별이 가죠?


    열 맞추기.[열 단위 정리]

    문법적으로 비슷한 의미를 가진 열끼리 열을 맞추어봅니다.

    세로로 읽을때 훨씬 깔끔하단 것을 느낄수 있습니다.


    코드를 보는데 표 같아서 좋다고 할까요.

    ;-------------------------Code 3.10-------------------------
    ;-------------------------Solution 3.3
    ;----------exchange
    (define exch-level1 100000   )
    (define exch-level2 1000000  )
    (define exch-level3 10000000 )
    (define exch-level4 50000000 )
    (define exch-level5 100000000)
    
    (define (exch-usd money exch-rate)
      (/ money exch-rate))
    
    ;----------charge
    (define charge-level1 0.03 )
    (define charge-level2 0.035)
    (define charge-level3 0.04 )
    (define charge-level4 0.05 )
    
    (define (charge-calc charge-level money)
      (- money (* charge-level money)))
    
    ;----------krw->usd exchange
    (define (krw->usd2 won)
      (cond
        [(>  0           won)      'Error-*less-than-0*]
        [(or (>= exch-level1 won)
             (<  exch-level5 won)) (exch-usd (charge-calc charge-level1 won) krw)]
        [(>= exch-level2 won)      (exch-usd (charge-calc charge-level3 won) krw)]
        [(>= exch-level3 won)      (exch-usd (charge-calc charge-level4 won) krw)]
        [(>= exch-level4 won)      (exch-usd (charge-calc charge-level3 won) krw)]
        [(>= exch-level5 won)      (exch-usd (charge-calc charge-level2 won) krw)]))
    
    ;----------Test
    (krw->usd2 20000000)
    
    
    ;-------------------------Test 3.10-------------------------
    ;----------exch-usd
    ;;Constract: exch-usd ; number, number->usd
    ;;Purpose: Exchange money to USD with exchanging rate.
    (exch-usd 1000 krw) ;1
    (exch-usd 0.8  eur) ;1
    (exch-usd 6    cny) ;1
    
    ;----------charge-calc
    ;;Constract: charge-calc ; number, number->number
    ;;Purpose: Calculate money excluding commissions.
    (charge-calc 0.1  100) ;90
    (charge-calc 0.03 100) ;97
    
    ;----------krw->usd
    ;;Constract: krw->usd ; number->number
    ;;Purpose: Exchange KRW to USD and make a commission based on the amount.
    (krw->usd2 -1000)     ;Error
    (krw->usd2 0)         ;0
    (krw->usd2 100000)    ;97
    (krw->usd2 1000000)   ;960
    (krw->usd2 10000000)  ;9500
    (krw->usd2 50000000)  ;48000
    (krw->usd2 100000000) ;96500
    (krw->usd2 200000000) ;194000
    
    ;-----Compare with krw->usd
    (krw->usd  -1000)     ;Error -> -0.97 Problem!!!
    (krw->usd  0)         ;0
    (krw->usd  100000)    ;97
    (krw->usd  1000000)   ;960
    (krw->usd  10000000)  ;9500
    (krw->usd  50000000)  ;48000
    (krw->usd  100000000) ;96500
    (krw->usd  200000000) ;194000

    실제로 할때는 시간을 아끼고, 알아보기 쉽게하기 위해 처음부터 정리하는 것을 고려하며 코드를 짜도록 합시다.


    드디어 디자인 레시피까지하여 내 맘대로 하는 프로그래밍 설계 2 - 기초가 끝났습니다.

    진짜 끝났어요.


    3. 섹션3 후기.

    어때요?

    할 만 했나요?

    프로그래밍의 기본기를 익히신것을 축하합니다!!!!


    섹션2는 HtDP의 2판이 완전히 새로 쓰여졌다길래 1판과 비교해가며 쓰느라 분량조절을 실패한듯 합니다.

    전반적으로 2판이 훨씬 재미있게 쓰여졌더라고요.

    제 강좌보다도. 영어가 가능하면 한번 보세요.


    전 텍스트로 놀지만 HtDP 2판에서는 애니메이션처럼 휙휙 움직이는 걸 눈앞에서 볼 수 있습니다.

    전 코드에 그림넣는게 귀찮아서 이 방식을 안따랐어요. 워낙 텍스트를 좋아하기도 하고.


    How to Design Programs: An Introduction to Computing and Programming

    How to Design Programs An Introduction to Computing and Programming Matthias Felleisen Robert Bruce Findler Matthew Flatt Shriram Krishnamurthi The MIT Press Cambridge, Massachusetts London, England

    How to Design Programs, Second Edition

    Matthias Felleisen, Robert Bruce Findler, Matthew Flatt, Shriram Krishnamurthi

    제껀 (제생각에)알아야할 것들 위주로 우겨넣다보니 좀 노잼화가 됐고, '+' 때문에 주제가 이리저리 옮겨다니는 경향이 있는듯.


    다음 강좌에서는 고정된 데이터를 다루는 방법을 알아보도록 합시다.

    혹시 어려운점이나 오류, 추가했으면 하는 내용이 있으면 언제든지 댓글을 달아주세요.

    실시간으로 답변은 힘들지만, 하루에서 이틀안에 답변을 주도록 하겠습니다.



    기타참고.

    철학의 주요개념(백종현)


    댓글

Designed by black7375.