트랙
/
Common Lisp
Common Lisp
/
연습 문제
/
양말과 Sexpr 정리하기
양말과 Sexpr 정리하기

양말과 Sexpr 정리하기

학습 연습 문제

소개

주석

Common Lisp에서는 컴퓨터가 무시하는 "주석"을 작성할 수 있어요. 한 줄 주석은 세미콜론(;) 한 개 이상으로 시작하고, 가끔 다음과 같은 형태를 볼 수도 있어요:

(code...) ; => value

여기서 주석은 그 줄의 코드를 실행한 뒤 Common Lisp이 어떤 값을 반환하는지 나타내는 데 쓰여요.

줄 끝에 짧은 주석을 달 때는 세미콜론 한 개, 코드 묶음 위에 더 긴 주석을 달 때는 두 개, 함수 같은 것을 설명하는 긴 주석에는 세 개, 소스 파일 맨 위의 헤더 같은 주석에는 네 개를 쓰는 것이 관례예요.

콘스

Common Lisp의 모든 객체는 "원자"(더 이상 나눌 수 없는 단일 값)이거나, 생성 함수 "cons"로 만드는 "콘스"예요. 콘스는 두 부분으로 이루어져 있어요. 첫 번째 요소, 그리고 나머지 요소예요. 역사적인 이유로 이 두 부분에는 car와 cdr라는 이름이 붙었어요. 이 콘스가 코드로 평가될 때, 첫 번째 요소(car)는 호출되는 함수를 나타내고 나머지 요소(cdr)는 그 함수의 인자를 나타내요:

(<function> <arg1> <arg2> ... <argN>)
; ^ car ^  |        ^ cdr ^

표현식

Common Lisp 코드는 모두 S-표현식(Symbolic Expression)으로 이루어져 있어요. 줄여서 sexpr라고 불러요. 모든 sexpr은 원자 아니면 콘스예요. S-표현식이 평가되면 어떤 값이 자동으로 반환되고, 그 값이 표현식을 대신해요. 직접 함수를 작성할 때(defun을 사용해서)는 defun 본문의 마지막 값이 자동으로 반환돼요:

;; Defining a new function
(defun gimme-foo () 'foo)
;; Calling the function as an S-Expression
(gimme-foo) ; => FOO

심볼

Common Lisp에서 심볼은 다른 값을 가리킬 수도 있고, _키워드_의 경우에는 자기 자신을 가리키는 특별한 값이에요. 심볼이 Lisp에 의해 평가되면, 심볼이 가리키는 값으로 바뀌어요:

foo  ; => <whatever-foo-points-to>
:foo ; => :FOO

키워드는 앞에 콜론(:)을 붙여 나타낸다는 점에 주의해요.

인용, 즉 S-표현식 앞에 '를 붙이는 것은 Lisp에게 그 표현식을 평가하지 말라고 알려주는 거예요. defun 예제에서 'foo를 인용했기 때문에, Lisp이 FOO가 가리키는 것을 찾으려고 시도하고 (찾지 못하고) 실패하는 일을 피하고, 대신 FOO라는 값 자체를 반환했어요. 우리 프로그램 어디에서도 FOO가 정의되지 않았다면:

foo  ; => <ERROR! Lisp doesn't know what foo points to!>
'foo ; => FOO

지금은 함수에서 심볼을 반환하는 방법 정도로만 여겨도 괜찮아요. 하지만 인용은 나중에 다시 다루고, 앞으로의 개념 연습 문제에서 그 의미를 더 자세히 살펴볼 거예요.

지침

여러분! 그래요, 용감한 리스퍼 바로 여러분이에요! 리스프 외계인 레니가 여러분의 도움이 필요해요! 레니는 다른 리스프 외계인들과 어울리러 나가고 싶어 하지만, 레니의 부모님은 방이 깨끗해질 때까지는 어디에도 못 간다고 했어요.

문제는 레니가 바닥을 거의 볼 수 없다는 거예요! 레니는 atom과 cons를, symbol과 keyword를, car와 cdr를 구별하지 못해요!

레니가 이 엉망진창을 정리하도록 도와줄 수 있을까요? 괄호도 잊지 마세요, 꼭 필요할 거예요!

1. 레니에게 symbol 다시 알려주기

시작하기 전에, 레니는 symbol과 keyword가 어떻게 생겼는지 다시 떠올려야 해요. 마지막으로 정리한 지도 꽤 오래됐거든요... 두 개의 함수를 정의해서 레니를 도와주세요. lennys-favorite-food는 어떤 symbol로 평가되고, lennys-secret-keyword는 어떤 keyword로 평가돼요. 예를 들면:

(lennys-favorite-food) ; => LASAGNA
(lennys-secret-keyword) ; => :ALIENS-ARE-REAL

원하는 symbol이나 keyword를 아무거나 반환해도 돼요 (라자냐를 별로 안 좋아하거나 외계인의 존재를 믿지 않아도 괜찮아요).

2. 레니가 atom과 cons를 구별하도록 돕기

레니가 symbol과 keyword를 다시 익혔으니, 이제 청소할 차례예요! 방에 널려 있는 S-Expression을 정리하려면, 레니는 무엇이 cons이고 무엇이 atom인지 판단하는 데 도움이 필요해요. 두 개의 함수 is-an-atom-p와 is-a-cons-p를 구현해서 도와줄 수 있어요:

(is-an-atom-p 'one-thing)    ; => T
(is-an-atom-p '(two things)) ; => NIL
(is-a-cons-p 'one-thing)    ; => NIL
(is-a-cons-p '(two things)) ; => T

이번 섹션에서는 유용하게 쓸 수 있는 내장 lisp 함수가 여러 개 있어요. 그리고 지금은 T를 참, NIL을 거짓으로 생각하면 돼요.

3. 레니가 cons를 나누도록 돕기

마지막으로, 레니의 cons는 너무 부피가 커서 깔끔하게 정리할 수 없어요. 그래서 더 작은 부분으로 나눠야 해요. cons를 나누는 두 개의 함수(first-thing과 rest-of-it)를 정의해서 도와줄 수 있을까요?

(first-thing '(first second third)) ; => FIRST
(rest-of-it  '(first second third)) ; => (SECOND THIRD)

다시 말하지만, Common Lisp의 내장 함수 몇 가지가 여기서 유용할 거예요.

GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Common Lisp Exercism

양말과 Sexpr 정리하기 문제를 시작해 볼 준비가 됐나요?

Exercism에 가입하고 Common Lisp 트랙을 개념 27개연습 문제 92개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.