QuickLisp는 Common Lisp와 함께 사용될 수 있는 라이브러리 매니저이다.



설치 참고사항


- 지원 플랫폼 : Linux, Mac OS X, Windows

- 지원 Common Lisp 구현체 : ABCL, Allegro CL, Clasp, Clozure CL, CLISP, CMUCL, ECL, LispWorks, MKCL, SBCL, Scieneer CL



설치


1) QuickLisp 홈페이지상의 링크 혹은 다음 커맨드를 사용해서 QuickLisp.lisp 파일을 다운받는다.


홈페이지 : https://www.quicklisp.org/


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



2) [선택사항] QuickLisp 홈페이지상의 링크 혹은 다음 커맨드를 사용해서 사인키를 다운받는다.

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



3) [선택사항] 다운받은 QuickLisp.lisp 파일을 검증한다.


gpg --verify quicklisp.lisp.asc quicklisp.lisp



4) QuickLisp.lisp 파일을 실행한다. (SBCL의 경우)


sbcl --load quicklisp.lisp



5) REPL 상에서 다음 커맨드를 입력하여 QuickLisp 설치를 진행한다.

(quicklisp-quickstart:install)



6) [선택사항] 다음 커맨드를 입력하여 다음 번 시작시에 QuickLisp가 자동 로드되도록 한다.


(ql:add-to-init-file)



사용 방법


1) 라이브러리 검색


(ql:system-apropos "vecto")



2) 라이브러리 설치 (혹은 이미 설치되어있을 경우, 로딩을 수행한다)


(ql:quickload "vecto")



기타


- (ql:add-to-init-file)을 실행하지 않았을 경우, 다음 번 실행시에 <사용자 폴더>/quicklisp/setup.lisp 파일만 로딩하는 것으로 QuickLisp 기능을 사용할 수 있다.


(load "~/quicklisp/setup.lisp")


- 런타임에 생성되는 파일은 <사용자 폴더>/quicklisp에 저장되니 언인스톨시 이 파일들도 지우도록 한다.



링크


- 홈페이지 : https://www.quicklisp.org/



<테스트 환경>

OS : Windows 7

QuickLisp 버전 : beta



,


SBCL과 함께 제공되던 Lisp 라이브러리 매니저인 asdf-install은 폐기되었습니다.


그 대체품으로 최근에는(?) QuickLisp가 쓰입니다.


-> 참고 : QuickLisp 사용 방법


,

Common Lisp에서 List, Array, Vector, String, Sequence 차이점

List
 - 순차적 접근 가능.

Array
 - 임의의 접근 가능.
 - 여러 차원도 가능.
 - 기본적으로 고정 사이즈이지만, 제한된 범위 내에서 사이즈를 변경하거나(:fill-pointer), 제한 없이 사이즈를 변경할 수 있는(:adjustable) 옵션을 줄 수 있다.

Vector
 - Array 중 1차원 Array.

String
 - Vector 중 character를 구성 요소로 하는 Vector

Sequence
 - List, Vector의 상위 개념


List의 생성

(list a b c)
-> (a b c)

'(a b c)
-> (a b c)

(cons a '(b c))
-> (a b c)



Vector의 생성

(vector 1 2 3)
-> #(1 2 3)

#(1 2 3)
-> #(1 2 3)

(make-array 3 :initial-element 1)
-> #(1 1 1)



,

Vector 정의 방법

(defparameter some-array (make-array 4 :initial-element 1)) ; 길이가 4이고 1로 초기화



IF NOT 구문


(unless A B C D)


; (if (not A) (progn B C D)) 와 동일



NOT vs. NULL


: 동작 상으로 동일 (nil 값인지 여부를 체크)


(not T)

-> NIL


(not nil)

-> T


(not '())
-> T

(not 10) ; 10은 nil 값이 아니므로

-> NIL



DO 구문 사용


;; 사용법 :

(do ((변수명 초기값 다음값_계산식)) (종료조건1 종료조건2 ..)) ; 모든 종료 조건을 만족해야 종료 (AND 연산)
    실행구문1
    실행구문2
    ..
)

(do ((i 0 (+ i 1))) ((>= i 100)) ; => for (i = 0; i < 100; i++) 과 동일

)



DOLIST 구문 사용하여 리스트 순회하기


(dolist (x '(1 2 3 4))
    (print x)
)
-> 1
2
3
4
NIL


(dolist (x '(1 2 3 4))
    (print x)
    (if (= x 2) (return)) ; return 문을 사용하여 루프를 빠져나올 수 있음
)
-> 1

2

NIL



키보드 입력 받기


(read-line)



,

Common Lisp 문자열 관련 함수 사용법을 정리해보았습니다



문자열 자르기 & 치환

; 문자열 자르기
(subseq "abcdef" 2)
-> "cdef"

(subseq "abcdef" 0 2)
-> "ab"

; 문자열 치환
(defparameter str (string "abcdef"))
(setf (subseq str 0 2) "AB")
str
-> "ABcdef"

; 치환시 변경 전의 길이보다 변경 후의 부분이 더 길 경우, 변경 전의 길이만큼만 바뀐다. (늘어나지 않음)
(defparameter str (string "abcdef"))
(setf (subseq str 0 2) "ABC")
str
-> "ABcdef"

(setf (subseq "abcdef" 0 2) "AB") ; (X) 변수에 대해서만 사용가능하다

(setq (subseq str 0 2) "ABC") ; (X) setq가 아닌 setf만 가능함에 유의



문자열 결함

; 문자열 결합
(concatenate 'string "abc" "def") ; 문자열로 결합
-> "abcdef"

(concatenate 'list "abc" "def") ; 문자의 리스트로 결합
-> (#\a #\b #\c #\d #\e #\f)

; 문자열 포매팅
(format nil "this is ~A" '(a b c))
-> "this is (A B C)"


(format nil "this is ~{ ~A~}" '("abc" "def" "ghi"))
-> "this is  abc def ghi"



개별 문자 처리

; 개별 문자 얻기
(char "abcdef" 2)
-> #\c

; 개별 문자 치환
(defparameter str (string "abcdef"))
(setf (char str 5) #\g)
str
-> "abcdeg"

; 문자 -> 코드 변환
(char-code #\a)
-> 97

; 코드 -> 문자 변환
(code-char 97)
-> #\a



개별 문자 나열

; 한번에 한 문자씩 처리 : 익명 함수를 통해 직접 처리하는 방법
(map 'string #'(lambda (c) (print c)) "abcde")
-> #\a
#\b
#\c
#\d
#\e
"abcde"
 
; 한번에 한 문자씩 처리 : loop를 사용하여 문자 리스트를 얻어 처리하는 방법
(loop for char across "abcde" collect char)
-> (#\a #\b #\c #\d #\e)



문자 제거

; 특정 문자 제거
(remove #\b "abcdef")
-> "acdef"


(remove #\b "bbbbbb1" :start 2) ; 인덱스 2 이후 문자를 대상으로
-> "bb1"



; 특정 조건을 만족하는 문자 제거
(remove-if #'upper-case-p "AbCdEf") ; 대문자 제거
-> "bdf"

; 특정 문자 대체 (뒤엣것으로 앞엣것을 대체)
(substitute #\z #\a "abcdefga")
-> "zbcdefgz"

; 특정 조건을 만족하는 문자 대체
(substitute #\Z #'upper-case-p "AbCdEf")
-> "ZbZdZf"

; 특정 구간만큼 대체
(replace "abcdef" "12345" :end1 3) ; 인덱스 3 앞의 문자를 대상으로
-> "123def"



문자열, 문자 사이의 변환

; 길이가 1인 문자열 -> 문자
(coerce "a" 'character)
-> #\a

(coerce "ab" 'character) ; 길이가 1이 아닐 때 쓰면 에러 발생!

; 길이가 1이 넘는 문자열 -> 문자 리스트
(coerce "abc" 'list)
-> (#\a #\b #\c)

; 길이가 1인 문자열 <- 문자
(string #\a)
-> "a"

; 길이가 1이 넘는 문자열 <- 문자
(coerce '(#\a #\b #\c) 'string) ; 리스트로부터 생성
-> "abc"


(coerce #(#\a #\b #\c) 'string) ; array로부터 생성 (1)
-> "abc"


(defparameter some-array (make-array 4 :initial-element #\a)) ; array로부터 생성 (2)
(coerce some-array 'string)
-> "aaaa"



; [참고] array 정의 방법
(defparameter some-array (make-array 4 :initial-element #\a)) ; 길이가 4이고 a로 초기화



기타 처리

; 문자열 뒤집기
(reverse (string "abcde"))
-> "edcba"

; 대소문자 변환
(string-upcase "abcde")
-> "ABCDE"
(string-downcase "ABCDE")
-> "abcde"
(string-capitalize "abcde")
-> "Abcde"

; 문자열 트림 : 첫번째 파라미터의 문자들을 두번째 파라미터 끝에서부터 제거한다
(string-trim " " " abc def ")
-> "abc def"
(string-trim " ac" " abc abc ")
-> "bc ab"
(string-left-trim " ac" " abc abc ") ; 왼쪽에서부터 제거
-> "bc abc "
(string-right-trim " ac" " abc abc ") ; 오른쪽에서부터 제거
-> " abc ab
(string-right-trim '(#\Space #\a #\c) " abc abc ") ; 첫번째 파라미터로 문자열 대신 리스트 사용
-> " abc ab"



심볼 관련

; 문자열 -> 심볼(변수명 등) 변환
; - 단지 변환하는 것이 아닌 해당 심볼이 패키지에 존재하지 않을 경우 추가된다.
(intern "SOME-SYMBOL")

; 심볼 -> 문자열 변환
; - 심볼 존재 유무에 상관없이 동작
(symbol-name 'SOME-SYMBOL)
-> "SOME-SYMBOL"



문자 검색

; 문자 검색 후 발견한 문자 반환

(find #\t "The quick onyx goblin jumps over the lazy dwarf." :test #'equal) ; 대소문자 구별하여 검색
-> #\t


(find #\Z "The quick onyx goblin jumps over the lazy dwarf." :test #'equal) ; 미발견시
-> NIL

(find #\Z "The quick onyx goblin jumps over the lazy dwarf." :test #'equalp) ; 대소문자 구별 없이 검색
-> #\z


; 특정 함수를 만족시키는 문자 검색하여 문자 반환
; (digit-char-p : 문자가 숫자를 나타내는 문자인지 체크하는 함수)

(find-if #'digit-char-p "The quick onyx goblin jumps over the lazy dwarf. 1234567890") ;
-> #\1

(find-if #'digit-char-p "The quick onyx goblin jumps over the lazy dwarf. 1234567890" :from-end t) ; 뒤에서부터 검색
-> #\0


; 문자 검색 후 발견한 위치 반환

(position #\t "The quick onyx goblin jumps over the lazy dwarf." :test #'equal) ; 대소문자 구별하여 검색
-> 33

(position #\t "The quick onyx goblin jumps over the lazy dwarf." :test #'equalp) ; 대소문자 구별 없이 검색
-> 0



; 특정 함수를 만족시키는 문자 검색하여 위치를 반환

(position-if #'digit-char-p "The quick onyx goblin jumps over the lazy dwarf. 1234567890")
-> 49

(position-if #'digit-char-p "The quick onyx goblin jumps over the lazy dwarf. 1234567890" :from-end t) ; 뒤에서부터 검색
-> 58


; 문자 검색 후 발견한 갯수 반환

(count #\t "The quick onyx goblin jumps over the lazy dwarf." :test #'equal) ; 대소문자 구별하여 검색
-> 1


(count #\t "The quick onyx goblin jumps over the lazy dwarf." :test #'equalp) ; 대소문자 구별 없이 검색
-> 2



; 특정 함수를 만족시키는 문자 검색하여 갯수를 반환

(count-if #'digit-char-p "The quick onyx goblin jumps over the lazy dwarf. 1234567890")
-> 10

(count-if #'digit-char-p "The quick onyx goblin jumps over the lazy dwarf. 1234567890" :start 51) ; 특정 위치에서부터 검색
-> 5



문자열 검색

; 기본 검색
(search "the" "the quick onyx goblin jumps over the lazy dwarf.")
-> 0

; 뒤에서부터 검색
(search "the" "the quick onyx goblin jumps over the lazy dwarf." :from-end t)
-> 33

; 시작 위치를 지정하여 검색
(search "the" "the quick onyx goblin jumps over the lazy dwarf." :start2 4)
-> 33

; 뒤에서부터 시작 위치를 지정하여 검색
(search "the" "the quick onyx goblin jumps over the lazy dwarf." :end2 10 :from-end t)
-> 0

; 미발견시
(search "JUMPS" "the quick onyx goblin jumps over the lazy dwarf.")
-> NIL

; 특정 함수를 만족시키는 문자열 검색
(search "JUMPS" "the quick onyx goblin jumps over the lazy dwarf." :test #'char-equal) ; 대소문자 구별없이 문자 비교
-> 22



문자열, 숫자  사이의 변환


문자열 -> 숫자 변환 (PARSE-INTEGER 사용)
 - 두번째 리턴값은 parsing이 멈춘 위치

 - "#X23"과 같은 기수 표기법은 사용 불가

(parse-integer "37")
-> 37
2

(parse-integer "37" :start 1) ; 시작 위치를 지정
-> 7
2

(parse-integer "37" :end 1) ; 종료 위치를 지정
-> 3
1

(parse-integer "37" :radix 8) ; 진수를 지정
-> 31
2

(parse-integer " 37 ") ; space가 포함되도 괜찮다
-> 37
4

(parse-integer " 37 is thirty-seven" :junk-allowed t) ; space 외 다른 문자가 포함되는 것을 허용할 경우
-> 37
3


(parse-integer " 37 is thirty-seven") ; space 외 다른 문자를 허용하지 않을 경우
-> junk in string " 37 is thirty-seven" (에러 발생!)


문자열 -> 숫자 변화 (READ-FROM-STRING 사용)

 - 역시 두번째 리턴값은 parsing이 멈춘 위치
 - 기수 표기법은 물론 커맨드 라인에서 가능한 모든 표현이 가능
 - 심지어 문자열 안에서 함수를 실행 후 그 결과값을 사용할 수도 있다.

(read-from-string "#X23") ; 8진수 표현
-> 35
4

(read-from-string "4.5") ; 소수점
-> 4.5
3

(read-from-string "6/8") ; 분수
-> 3/4
3

(read-from-string "#C(6/8 1)") ; 복소수 (실수부 + 허수부)
-> #C(3/4 1)
9

(read-from-string "1.2e2") ; 지수 표기법
-> 120.00001
5

(defparameter a-symbol 34)
(read-from-string "a-symbol")
-> A-SYMBOL
8

(defparameter a-symbol 34)
(read-from-string "#.a-symbol") ; 변수의 실제값을 사용
-> 34
10

(read-from-string "#.(+ 1 2)") ; 함수 호출 결과값을 사용
-> 3
9


숫자 -> 문자열 변환

(write-to-string 250)
-> "250"

(write-to-string 250.02)
-> "250.02"

(write-to-string 250 :base 16) ; 16진수로
-> "FA"

(write-to-string (/ 1 3)) ; 유리수
-> "1/3"



문자열 비교

(string= "Abcd" "Abcd") ; 대소문자 구별하여 비교
-> T


(string= "Abcd" "abcd")
-> NIL


(string-equal "Abcd" "abcd") ; 대소문자 구별하지 않고 비교
-> T

(string< "aaaa" "aaab") ; "aaaa"가 "aaab"보다 작을 경우 달라지는 인덱스를 반환. 그렇지 않을 경우, NIL을 반환
-> 3

(string< "aaab" "aaaa")
-> NIL

(string-lessp "AAAA" "aaab") ; string<와 대소문자 구별을 하지 않는다는 점만 빼고 같음
-> 3

(mismatch "abcde" "bbcde" :test #'char=) ; char= 함수를 만족하지 않는 인덱스를 반환
-> 0

(mismatch "abcde" "abdde" :from-end t :test #'char=) ; 뒤에서부터 비교하여 char= 함수를 만족하지 않는 인덱스를 반환 (주의 : 0번째 문자에서 달라질 경우 1을 반환)
-> 3



참고 : http://cl-cookbook.sourceforge.net/strings.html의 내용을 바탕으로 작성했습니다.


,

SBCL 소스를 커맨드 프롬프트에서 스크립트처럼 실행 시에, 특수 변수 *posix-argv*를 사용하여 실행 파라미터(인자)를 리스트의 형태로 가져올 수 있다.


아래와 같이 example.lisp을 작성한다.

(print *posix-argv*)


실행 파라미터를 주어 실행해보면, 

sbcl --script example.lisp enter your argument


("sbcl" "enter" "your" "argument")


첫번째 요소로 SBCL 실행 파일명을 가지며, 나머지에 실행 파라미터가 포함된 리스트가 출력되는 것을 볼 수 있다.




<테스트 환경>

OS : Windows 7

SBCL 버전 : 1.2.7


,

SBCL에서 디렉토리 내의 파일/디렉토리 리스트를 가져오는 방법입니다. (sb-posix, sb-grovel을 사용한 방법)


1. 디렉토리 리스트 가져오기


(require :sb-posix)
(require :sb-grovel)

(defun is-directory (pathname) ; 주어진 경로가 디렉토리인지 체크한다
  (ignore-errors
    (sb-posix:s-isdir (sb-posix:stat-mode (sb-posix:lstat pathname)))
    )
  )

(defun get-directory-contents (pathname) ; 주어진 경로 내의 모든 디렉토리/파일 리스트를 얻는다
  (let ((dir (sb-posix:opendir pathname)))
    (prog1
      (loop for dirent = (sb-posix:readdir dir)
           until (sb-grovel::foreign-nullp dirent)
           unless (or (equal (sb-posix:dirent-name dirent) ".")
                      (equal (sb-posix:dirent-name dirent) ".."))
           collect (concatenate 'string pathname "/"
                                (sb-posix:dirent-name dirent)))
      (sb-posix:closedir dir))
    )
  )

(defun get-dirs (dir) ; 주어진 경로 내의 모든 디렉토리 리스트를 얻는다 (get-directory-contents의 결과 중 디렉토리가 아닌 항목을 제거한다)
  (remove-if-not #'is-directory (get-directory-contents dir))
  )


(defparameter dir-list nil)

(setq dir-list (get-dirs ".")) ; 현재 디렉토리 내의 디렉토리를 가져와 DIR-LIST에 저장한다


(dolist (dir dir-list) ; DIR-LIST의 내용을 출력한다
  (print dir)
  )




2. 파일 리스트 가져오기


(require :sb-posix)
(require :sb-grovel)

(defun is-directory (pathname) ; 주어진 경로가 디렉토리인지 체크한다
  (ignore-errors
    (sb-posix:s-isdir (sb-posix:stat-mode (sb-posix:lstat pathname)))
    )
  )

(defun get-directory-contents (pathname) ; 주어진 경로 내의 모든 디렉토리/파일 리스트를 얻는다
  (let ((dir (sb-posix:opendir pathname)))
    (prog1
      (loop for dirent = (sb-posix:readdir dir)
           until (sb-grovel::foreign-nullp dirent)
           unless (or (equal (sb-posix:dirent-name dirent) ".")
                      (equal (sb-posix:dirent-name dirent) ".."))
           collect (concatenate 'string pathname "/"
                                (sb-posix:dirent-name dirent)))
      (sb-posix:closedir dir))
    )
  )

(defun get-dirs (dir) ; 주어진 경로 내의 모든 디렉토리 리스트를 얻는다 (get-directory-contents의 결과 중 디렉토리가 아닌 항목을 제거한다)
  (remove-if-not #'is-directory (get-directory-contents dir))
  )

(defun get-files (dir) ; 주어진 경로 내의 모든 파일 리스트를 얻는다 (get-directory-contents의 결과 중 디렉토리인 항목을 제거한다)
  (remove-if #'is-directory (get-directory-contents dir))
  )

(defparameter file-list nil)

(setq file-list (get-files ".")) ; 현재 디렉토리 내의 파일 리스트를 가져와 FILE-LIST에 저장한다


(dolist (file-item file-list) ; FILE-LIST의 내용을 출력한다
  (print file-item)
  )




<테스트 환경>

OS : Windows 7

Emacs 버전 : Emacs 24.3 윈도우용

SBCL 버전 : 1.2.7

Slime 버전 : 2.21


,

파일에 저장된 SBCL 소스를 커맨드 프롬프트에서 스크립트처럼 실행할 수도 있다.


sbcl.exe --script <filename>



아래와 같이 배치파일로 저장해두어도 된다.


runcl.bat :

sbcl.exe --script %1



<테스트 환경>

OS : Windows 7

SBCL 버전 : 1.2.7


,