CommonLisp 학습을 위한 책

프로젝트 셋팅 스크린캐스트

CL-USER> (ql:quickload :cl-project)
CL-USER> (cl-project:make-project #p"~/myproject" :author "Jeongsoo Park" :email "toracle@gmail.com")
CL-USER> (ql:quickload :myproject)
CL-USER> (push #p"/home/toracle/myproject/" asdf:*central-registry*)
CL-USER> (ql:quickload :myproject)
CL-USER> (asdf:test-system :myproject)

Roswell을 사용하면 환경을 통합적으로 구축할 수 있다.

  1. roswell 설치

  2. ros install sbcl-bin
  3. ros install quicklisp
  4. ros install slime (실행 마치고 출력으로 나오는 helper.el을 emacs init에 추가해준다)

Windows에서는 sbcl 버전이 낮은데, roswell에서 기본적으로 sbcl을 사용한다. 아마 roswell에서 사용하는 코드가 sbcl 상위 버전에서만 지원하는 것 같다. 그래서 Windows에서는 clisp 등, 각 배포본을 직접 실행하는 편이 나은 것 같다.

  1. clisp를 설치한다.
  2. quicklisp을 설치한다.
    1. 인스톨러를 다운로드 받는다.

    2. (load "/Users/torac/Downloads/quicklisp.lisp") 를 실행한다.

Lisp for the Web 책에서 추천한 책들

책/PracticalCommonLisp 이 시작하기에는 제일 좋은 책이다.

언어의 기본적인 내용을 익혔다면, 그 다음에는 PAIP를 읽으면 좋다. 특히 좋은 코딩 스타일을 익히는데는 더할나위 없이 좋다.

함수형 스타일을 익히려면 LittleSchemer 를 읽어라. 이 책은 어떤 특정한 언어나 문법에 대한 책이 아니다. 특히 책이 쓰여진 방식 - 소크라테스식 대화 방식이 독특하고, 교육 방식을 살펴보는 측면으로도 좋은 책이다.

견고한 함수형 마인드셋을 익히려면 OCaml을 참고해보면 좋다. Ocaml from the Very Beginning by John Whitington은 좋은 책이다.

이 이후에, 더 중무장하려면, 책/OnLisp 이나 LetOverLambda 를 읽어라.

QuickLisp 설치

QuickLisp는 CL에서 널리 사용되는 패키지 관리자이다. 일단 이걸 처음에 셋팅해놓아야 다른 패키지들 설치하기가 편리하다.

우선 아래 명령으로 quicklisp 부트스트래핑 스크립트를 다운로드 받는다.

$ curl -O https://beta.quicklisp.org/quicklisp.lisp

그리고 CL REPL을 실행하고, 해당 파일을 로드한다.

CL-USER> (load "~/quicklisp.lisp")

아래 명령으로 quicklisp을 설치한다.

CL-USER> (quicklisp-quickstart:install)

설치가 진행되고, ~/quicklisp 디렉토리가 생성된다.

아래 명령을 실행하여 quicklisp이 매 세션마다 자동적으로 로딩되도록 한다.

CL-USER> (ql:add-to-init-file)

Optimization

(declaim (optimize (speed 3) (safety 0) (debug 0)))

Make Executable

QuickLisp은, 개발 과정에서는 사용하기에 용이하지만, 배포할 때는 문제가 좀 있다. 서버에 배포할 때는, 배포 과정에서 설치 과정이 있을 수 있다. 이 때 QuickLisp도 설치하고, 연관 패키지도 설치할 수 있겠다. 그런데, 단독 실행 파일로 묶어서 배포하는 경우에는, 실행을 했는데 갑자기 QuickLisp 다운받기 시작하면 좀 이상하겠다.

이런 경우에는, 모든 연관 라이브러리를 Quicklisp을 통해서 메모리에 불러들인 뒤에, 이미지 저장을 하여 내보내기를 한다.

ECL에 자꾸 생각이 미치는데, ECL은 결과물을 C로 컴파일한다. 그래서 가장 코드 난독화 수준이 높다. 디컴파일도 안되고.

SBCL도, 바이트 코드로는 컴파일을 하니까 그리 나쁘지는 않으려나?

ECL로 코드를 작성하고, 외부 라이브러리를 어떻게 불러 쓰는지가 궁금하다. 왜냐면 QuickLisp 은 실행시점에 QuickLisp 홈 디렉토리에 라이브러리를 다운로드 받아서 사용한다. 그러나 그보다는 애초에 배포되는 바이너리에 함께 포함되어 있는 것이 좋을 것 같다. 어떻게 그렇게 하지? ASDF에 뭔가 방법이 있지 않을까? QuickLisp처럼 웹에서 다운로드 받는 방식이 아니라, 미리 다운로드 받아서 로딩하는 방법이.

자주 쓰는 명령어

Land of Lisp에서 나온 명령

(ash number shift-digits)
숫자를 bit만큼 shift.
(1- number)
숫자를 1 감소하여 반환.
(1+ number)
숫자를 1 증가하여 반환.
(flet ...)
let과 비슷하나 변수 대신 함수를 정의하는 용도.
(labels ...)
flet과 비슷하나, 내부에서 정의된 함수끼리 서로 호출할 수 있음. let과 let*의 차이와 비슷함.
(princ str)
화면에 출력. 사용자 친화적으로 출력.
(oddp number)
홀수인지 여부를 반환.
(cond ...)
조건 분기의 일반화된 form.
(case ...)
조건 대신 값을 기준으로 분기하는 form.
(eq a b)
symbol의 경우 동등성 비교에 사용.
(equal a b)
symbol 이외의 모든 경우 동등성 비교에 사용.
(assoc key alst)
alist에서 key에 해당하는 entry를 추출.
(mapcar func lst)
list 각 원소에 대해 func를 적용하고 그 결과를 list로 이어붙여 반환.
(apply func args)
args와 함께 func를 호출.
(append args)
args들을 이어붙여 하나의 list로 반환.
(remove-if-not func lst)
list의 각 원소 중, func가 nil인 원소만 제외하고 반환.
(find key lst :key func)
list에서 key를 찾아 반환. 각 원소에서 key를 찾는 방법을 지정할 수 있음.
(member elem lst)
elem이 lst에 속하는지 여부를 반환.
(push elem lst)
lst의 맨 앞에 elem을 추가하여 반환.
(progn stmts)
여러 stmt들을 수행함.
(print str)
화면에 출력. 개행을 덧붙임.
(prin1 str)
화면에 출력하되 개행을 생략.
(read)
입력값을 받음.
(eval stmt)
구문을 평가하여 실행.
(loop stmt)
구문을 반복하여 실행.
(unless cond stmt)
조건이 참이 아닌 경우에만 실행
(read-from-string str)
문자열로부터 입력값을 받음.
(concatenate 'string args)
args를 문자열로 간주하여 이어붙여 반환.
(read-line)
한 행의 입력값을 받음.
(if cond true-stmt false-stmt)
if 구문
(coerce str 'list)
문자열을 문자들의 list로 쪼개어 반환.
(coerce lst 'string)
문자들의 list를 문자열로 붙여 반환.
(substitute-if replace-val func lst)
lst에서 func이 true일 때 해당 원소를 replace-val로 변경하여 반환.
(complement func)
...
(mapc func lst)
lst의 각 원소에 대해 func를 실행. mapcar와는 달리 아무 값도 반환하지 않는다.
(with-open-file (stream-name filename :direction :output :if-exists :supersede) stmt)
파일 스트림을 연다.
thunk
인자가 0개인, 즉시 실행할 수 있는 상태의 함수
*standard-output*
표준 출력 스트림
(funcall func)
func를 실행
(ext:shell str)
쉘에서 str을 실행
(maplist func lst)
lst에서 원소 하나씩 뒤로 가면서 func에 인자로 전달. (A B C) (B C) (C)
(random num)
num 이하의 난수를 발생
(loop repeat num collect val)
num 횟수만큼 반복하면서 각 val을 원소로 하는 list를 반환.
(loop for n from from-num to to-num collect val)
from-num부터 to-num까지 각 값을 n에 할당하며 반복하면서 각 val을 원소로 하는 list를 반환.
(remove-duplicates lst)
중복 원소를 제거하여 반환.
(mapcan func lst)
mapcar와 같지만, 각 결과값을 flatten하여 append한 결과를 반환.
(pushnew elem lst)
lst의 맨 앞에 elem을 추가. inplace 함수.
(make-array num)
num 크기의 배열을 생성.
(aref arr pos)
배열 arr에서 pos 위치의 원소를 반환.
(make-hash-table)
해시 테이블을 생성하여 반환.
(gethash key hsh)
hsh 해시 테이블에서 key에 해당하는 값을 반환.
(values args)
args를 하나의 값으로 합쳐서 반환. 별다른 조치를 하지 않으면 가장 앞의 값을 기본값으로 사용.
(multiple-value-bind (var1 var2 ...) val stmt)
multiple value인 val을 var1, var2로 분해하고 stmt를 실행.
(setf var value)
var에 value를 저장.
(dotimes (var value) stmt)
반복 회차 값을 var에 할당하면서 value 만큼 stmt를 반복.
(defstruct typename args)
typename을 이름으로 하는 구조체를 선언.
(make-typename)
typename 구조체 인스턴스를 생성.
(length obj)
obj의 길이를 반환. obj는 리스트, 문자열, 배열 등이 올 수 있음.
(find-if func lst)
lst에서 func를 만족시키는 첫번째 원소를 반환.
(count v lst)
lst에서 v와 같은 원소의 갯수를 반환.
(position v lst)
lst에서 v가 처음 등장하는 위치를 반환.
(some func lst)
lst에서 func를 만족하는 원소가 있는지 여부를 반환.
(every func lst)
lst에서 모든 원소가 func를 만족하는지 여부를 반환.
(reduce func lst)
lst를 순회하면서, lst의 각 원소를 func에 대입하여 실행한 값을 반환.
(reduce func lst :initial-value v)
lst를 순회하면서, lst의 각 원소를 func에 대입하여 실행한 값을 반환하되, 처음을 v로 시작.
(map type func lst)
lst에서 각 원소에 대해 func를 실행하고, 그 값을 이어붙여 type 형태로 반환.
(subseq lst start end)
lst에서 start부터 end 사이의 값만 슬라이싱하여 반환.
(sort lst cmp)
lst를 cmp 함수에 따라 정렬한 값을 반환.
(numberp v)
v가 숫자인지 여부를 반환.
(defmethod funcname ((v type) ...) stmt)
funcname을 이름으로 하는 메소드를 정의.
(integerp v)
v가 정수인지 여부를 반환.
(type-of obj)
obj의 타입을 반환.
(incf var val)
var에서 val만큼을 더한 값을 inplace로 대치.
(decf var val)
var에서 val만큼을 뺀 값을 inplace로 대치.
(defstruct (typename (:include typename2)) args)
typename2를 상속받아 typename을 선언.
(zerop v)
v가 0인지 여부를 반환.

CommonLisp (last edited 2022-09-02 01:05:43 by 정수)