brunch

You can make anything
by writing

C.S.Lewis

by 스캇아빠 May 10. 2024

만약과 도돌이표

프로그래밍 101

0. 시작하면서


드디어 첫 번째, 프로그램 글. 나는 이번 단락에서 if와 loop에 대한 (조건문과 반복문) 설명을 하려 했다. 그리고 나는 목차를 작성했던 당시 내가 제정신이 아니었음을 상기하며 후회했다. 시작이 참 순조롭다.


반복과 조건문은 컴퓨터 언어에서 가장 중요한 구문중 하나다. 솔직히 뭐든 안 중요한 게 있겠냐만은, if와 loop를 이해할 수 있다면, 컴퓨터 언어를 반은 배운 거나 다름없다.


그래서 그렇게 중요한 것을 제일 먼저 설명하려 했던 것 같은데, 이건 마치 식탁에 앉지도 못하는 사람에게 젓가락질을 가르치려 하는 것과 같다.


하지만 첫 시작부터 포기할 수는 없지. 간단하게 기본지식을 훑고 지나가보자. 뒤에서 다시 설명할 거니까 너무 부담 갖지 말자.


1. 함수


함수는 우리가 중고등학교 수학시간에 배웠던 그 함수다. 모른다고 하지 밀자. 내가 웬만한 사람보다 수학 더 못했었다고 자신할 수 있다. 함수는 쉽게 입력값을 넣으면 특정한 패턴으로 출력값이 나오는 상자와 같다. f(x) = x+ 1. 또는 f(x, y) = x + y 같은 것 말이다. 컴퓨터에서 말하는 함수도 그거다.


2. 데이터


데이터든 데이타든 다타 든 뭐 다 같은 Data다. 데이터마다 형태가 있는데, 위에 f(x)=x+1에서 말하는 x는 숫자형 데이터 일 것이다. 만약 x가 글자였다면 +1을 하는 순간 에러 난다. 첫 시작부터 에러 내지 말자. (“님” 이란 글자에 점찍으면 “남”이 되지 않는다) 데이터에는 크게 숫자형, 문자형, 그리고 집합형이 있는데, 다른 사람들은 Collection을 뭐라고 부르는지 모르겠지만, 따지지 말자. 어차피 이글 읽고 프로그래머 하려 하는 것 자체가 무리다. 그리고 집합형에는 Map, List, Set 형이 있다.


2.1. 맵형 데이터 (Map)


Map 타입은 이름이 여러 가지인데, python 언어에서는 dictionary(dict)이라고 하고 java 언어나 Clojure 언어에서는 Map이라고 하고, javascript 언어나 json 구조에서는 object라고 하고, Perl 언어나 Ruby 언어에서는 Hash, PHP 언어에서는 Associative Array라고 부르는데, 부르는 이름이 중요한 게 아니라, 그 뜻이 키와 값을 나타내는 데이터 형식을 이야기할 때 부르는 이름이란 사실만 알면 된다. 예를 들어 {“국어”:100,”수학”:80} 같은 데이터가 있다면 국어라는 키의 값은 100이고 , 수학이란 키의 값은 80이란 형태의 집합체 데이터다. 그런데 많은 언어에서 키는 데이터 형태가 제한적이고 값의 데이터 형태는 자유롭다 (map, set, list 같은 게 값으로 또 올 수 있다). 단 clojure는 키 데이터도 자유롭다.


2.2. 리스트형 데이터 (List)


그냥 데이터의 나열이다. 그래서 [1,2,3,4,5]가 될 수도 있고, [“국어”, 수학”] 이 될 수도 있다. 때로는 빈리스트 []가 될 수도 있다. 또, 리스트 안에 들어가는 데이터도 리스트가 될 수 있다. 그래서 [[“첫 번째”]] 는 첫 번째 값이 [“첫 번째”]인 리스트인데, 그 첫 번째 값의 첫 번째 값이 “첫 번째”인 데이터가 되는 거다.


2.3. 셋 데이터 (Set)


마지막으로 Set 타입, 다른 언어에서는 별로 중요하게 생각 안 하는데, Clojure에서는 꽤 중요한 데이터종류다. Set은 기본적으로 List와 같지만, Set 형태에서는 같은 값이 두 번 들어가지 않고, 데이터에 순서가 없다. 있는 것처럼 보이지만 실제 없다고 생각해야 한다. 오직 데이터가 있냐 없냐만 중요하다. 예를 들어. [1,2,3,4]를 Set으로 만들면 #{1,2,3,4}가 되고 , 이건 #{4,3,2,1}과 같은 값이다. 또 [1,2,2,3,4]을 Set으로 만들면, #{1,2,2,3,4}가 아닌 #{1,2,3,4}가 된다. 그리고 마찬가지로, Set 안 데이터는 어떤 데이터든 들어올 수 있다. #{#{”Set”}} 는, “Set”을 가지고 있는 Set을 가지고 있는 Set 이 된다. (솔직히 이젠 나도 헷갈린다)


자 그럼 이제 우리는 함수와 데이터를 간단히 배웠다. 가장 기본적인 내용이기 때문에 모두들 잘 따라왔으리라 생각한다. 솔직히 벌써 내가 이때까지 브런치에 쓴 글 중에서 제일 긴 글이 되었는데, 아직 본론은 시작도 못했다니…


3. 이제 진짜로 조건문과 반복문


컴퓨터가 잘하는 게 뭘까? 컴퓨터는 우리가 알고 있는 것처럼 똑똑하지 않다. 해상도도 현저히 떨어지고, 처리량도 엄청 제한적이고, 딴짓거리도 잘 못한다(하지만 요즘 이거를 시작해서 AI가 창의적이 되어간다). 그럼 왜 사람들이 컴퓨터를 똑똑하다고 할까? 그건 컴퓨터는 똑같은 작업을 잘하기 때문이다. 그래서 우리는 그걸 시키기 위해 반복문과 조건문을 배워야 한다.


if와 loop를 이용한 프로그램을 간단히 만들어 보자. 프로그램은, 구구단을 출력하는 프로그램이다.


(loop [x 2, y 2]

  (println x "x" y "=" (* x y))

  (if (< y 9)

    (recur x (inc y))

    (do

      (println "")

      (if (< x 9)

        (recur (inc x) 2)

        (println "done")))))


clojure를 읽는 방법은 매우 간단하다. ()가 함수를 호출하는 블럭이다. ()에서 제일 먼저 나오는 값이 함수다. 그리고 나머지는 모두 함수에 들어가는 입력값이다.


위의 프로그램을 읽기 위해 몇 가지 함수를 읽을 줄 알면 된다

- println : 입력값을 화면에 출력한다

- * : 입력값들을 곱해서 한 개 숫자로 돌려준다

- inc : 입력값에 1을 더란 숫자를 돌려준다

- if : 첫 번째 인자가 false나 nil이 아니라면 두 번째 입력값을 돌려주고, 맞다면 세 번째 입력값을 돌려준다. (사실 이거 함수 아닌데..라고 따지지 말자)

- loop : 첫 번째 인자는 무조건 리스트데이터야 하고 리스트데이터의 개수는 무조건 짝수여야 하고, 짝수 번째 데이커가 홀수 번째 데이터로 이름 붙는다

- recur : loop로 돌아가기 입력값은 loop의 첫 번째 인자 리스트의 개수의 반이 되어야 한다.

- do : 입력값의 각 함수들을 실행하고 맨 마지막 입력값을 리턴한다.


자 이제 하나씩 따라가 보자

1. loop 시작점에는 x 가 2이고 y 가 2로 정한다.

2. println으로 화면에 2 x 2 = 4를 출력한다.

3. y 가 9 보다 작은지 확인하고, 9보다 작기 때문에 다시 loop지점으로 옮겨간다. 이때에 x는 그대로 x 인 2이고 y는 1이 늘어난 3으로 시작한다.

4. 다시 println으로 화면에 2 x 3 = 6을 출력한다.

5. y 가 9 보다 작은지 확인하고.....

...

6. 어느덧 x는 2 , y는 9가 되어 loop지점으로 왔다.

7. println으로 화면에 2 x 9 = 18을 출력한다.

8. y 가 9보다 작지 않기 때문에 do 지점으로 옮겨간다.

9. println으로 화면에 빈 줄만을 출력한다.

10. x 가 9보다 작은지 확인하고, 현재 2, 9보다 작기 때문에 다시 loop 지점으로 옮겨가는데, 이때에 x는 1이 늘어난 3으로 시작하고, y는 다시 2로 돌아간다.

11. println으로 화면에 3 x 2 = 6을 출력한다.

12. y 가 9 보다 작은지 확인하고, 현재 2,  9보다 작기 때문에 다시 loop지점으로 옮겨간다. 이때에 x는 그대로 x 인 3이고 y는 1이 늘어난 3으로 시작한다.

....

13. 어느덧 x는 9, y 도 9가 되어 loop지점까지 왔다.

14. println으로 화면에 9 x 9 = 81을 출력한다.

15. y 가 9보다 작지 않기 때문에 do 지점으로 옮겨간다.

16. println으로 화면에 빈 줄만을 출력한다.

17. x 가 9보다 작은지 확인하고, 현재 9, 9보다 작지 않기 때문에 println으로 옮겨간다.

19. println으로 화면에 done을 출력한다.

20. 그리고 더 이상 아무것도 할 게 없기 때문에, loop 함수를 끝낸다. 참고로 loop함수는 nil을 리턴하는데, 이유는 마지막 실행인 println 이 nil을 리턴했기 때문이다. 아 nil 은 아무것도 아닌 데이터란 뜻이다.


자 이제, 당신은 구구단을 출력하는 프로그램을 보았다. 생각보다 쉽지 않은가? 그런데, 프로그램이란 것이 이게 다다. 입력에 따라 원하는 행동들을 하게 하는 것. 어쩐지 자신감이 생기지 않는가?


아래는 위에 것보다 훨씬 간단해서, loop 도 없고, if 도 없는데 똑같이 동작하는 프로그램이 있다. 내부적으로는 if를 썼겠지만, 일단 우리가 볼 수 있는 코드에는 if가 보이지 않는다.


(do

  (doseq [x (range 2 10)]

    (doseq [y (range 2 10)]

      (println x "x" y "=" (* x y)))

    (println ""))

  (println "done"))


몇 가지 새로운 함수들이 있다.

- doseq는 첫 번째 인자가 list 형태이다. loop과 같이 해당 리스트는 짝수번째 인자값이 홀수번째 데이터가 된다. 다만, doseq에서는 짝수번째 데이터가 집합형이어야 해서, 하나씩 빼서 홀수번째 데이터가 된다. 예를 들어서 (doseq [x [1 2 3]] (println x)) 와 같은 함수는 화면에 한 줄씩 1,2,3을 출력하게 된다.

- range 자동으로 list를 만들어 주는 함수인데, 첫 번째가 시작하는 숫자, 두 번째가 마지막 숫자바로 다음이 된다. 예를 들어서 (range 10 14)는 [10,11,12,13] 이 된다. 아 참고로 clojure에서는 list에 쉼표를 안 찍어도 된다.


1. (range 2 10)으로 [2,3,4,5,6,7,8,9]를 만들었고

2. 첫 doseq에서 x가 2로 시작하고, 두 번째 doseq에서 y가 2로 시작한다.

3. println으로 2 x 2 = 4를 출력하고, y 가 9가 될 때까지 두 번째 doseq에서 를 반복한다.

4. doseq를 빠져나가서 println으로 화면에 빈 줄을 출력하고

5. 2번부터 4번까지를 x 가 9가 될 때까지 doseq 함수를 반복한다.

6. 마지막으로 doseq 가 끝나고 println으로 화면에 done을 출력한다.



이전 01화 프롤로그
brunch book
$magazine.title

현재 글은 이 브런치북에
소속되어 있습니다.

작품 선택

키워드 선택 0 / 3 0

댓글여부

afliean
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari