Emacs Lisp Functions

2020년 5월 11일 수정

이 글은 Emacs에서 사용하는 Emacs Lisp의 함수를 정리하는 노트다. 당연히 모든 것을 정리한 것은 아니고, 기회가 될 때 마다 추가하고 있다.

변수

setq

전역 변수를 생성한다.

(setq variable-name 100)
(setq v1 1
      v2 2
      v3 3)

let

지역 변수를 생성한다.

(let (a b)
  ...)

초기값도 사용 가능하다.

(let ((a 1)
      (b 2)
      (c (generate-some-value)))
  ... body ...)

let*

지역 변수를 생성하는데 앞서 생성한 값을 생성하는데 참고할 수 있는 특수한 형태다.

(let* ((a 1)
       (b (+ a 2))
       (c (/ b 3)))
  ... body ...)

리스트 관련

리스트 생성하기

(list 1 2 3 4)         ;; (1 2 3 4)
'(1 2 3 4)             ;; (1 2 3 4)

순차적인 리스트는 number-sequence 를 이용해 생성할 수 있다.

(number-sequence 3)      ;; (3)
(number-sequence 2 5)    ;; (2 3 4 5)
(number-sequence 0 9 2)  ;; (0 2 4 6 8)

아이템 꺼내오기

(car '("a" "b" "c"))            ;; "a"
(first '("a" "b" "c"))          ;; "a"
(nth 3 '("a" "b" "c" "d" "e"))  ;; "d"
(last '("a" "b" "c"))           ;; ("c")
(car (last '("a" "b" "c")))     ;; "c"

리스트 여분 구하기

(cdr '(1 2 3 4))       ;; (2 3 4)
(rest '(1 2 3 4))      ;; (2 3 4)
(butlast '(1 2 3 4))   ;; (1 2 3)
(nthcdr 2 '(1 2 3 4))  ;; (3 4)

리스트 연장하기

(cons "a" '("b" "c"))           ;; ("a" "b" "c")
(cons '("a" "b") '("c" "d"))    ;; (("a" "b") "c" "d")
(append '("a" "b") '("c" "d"))  ;; ("a" "b" "c" "d")

이 외에 nconcappend 와 비슷하게 사용할 수 있다.

(let ((some-list '(1 2 3)))
  (nconc some-list '(4))
  some-list)
;; => (1 2 3 4)

다만 nconc 는 원본 변수의 값을 바꿔버리기 때문에 append 와는 다른 용도를 가지고 있다고 생각하자.

리스트 요소 다루기

(let ((lst '("b" "c")))
  (push "a" lst)
  ;; lst -> ("a" "b" "c")
  (pop lst)  ;; -> "a"
  ;; lst -> ("b" "c")
  )

문자열 관련

length: 길이

(length "abcd")

근데 length 는 다용도라 리스트 개수 세기 용으로도 사용된다.

concat: 이어 붙이기

문자열을 이어 붙일 때 사용한다.

(concat "hello" " " "world!")
;; -> "hello world!"

substring: 내부의 문자열 잘라서 얻기

(substring "abcde" 2 4)  ;; -> "cd"

string-trim: 다듬기

문자열 좌우의 공백 문자(whitespaces)를 제거한다.

(string-trim "  blah blah ")
;; -> "blah blah"

format: C 스타일 문자열 포맷

특정 포맷으로 문자열을 생성한다.

(format "I am %d age %s" 19 "old")
;; -> "I am 19 age old"

string-remove-prefix, string-remove-suffix

문자열 앞 혹은 뒤쪽의 특정 문자열을 잘라낸다.

(string-remove-prefix "aaa" "aaabbbccc")
;; -> "bbbccc"
(string-remove-suffix "ccc" "aaabbbccc")
;; -> "aaabbb"

string-match and replace-match

문자열에서 일부분을 비교해서 위치를 반환한다. 정규표현식도 사용할 수 있다.

(string-match "bbb" "aaabbbccc")
;; -> 3

위 코드에 replace-match 를 이어서 사용할 수 있다.

(replace-match "BBB" nil nil "aaabbbccc")
;; -> aaaBBBccc

number-to-string: 숫자를 문자열로 변환하기

(number-to-string 3)

반대 용도로 string-to-number 도 있다.

split-string and string-join

split-string 은 문자열을 특정 문자열을 단위로 나눌 때 사용한다.

(split-string "my name is conrad" " ")
;; -> ("my" "name" "is" "conrad")

반대로 string-join 은 문자열 리스트를 하나의 문자열로 합친다.

(string-join '("my" "name" "is" "conrad") " ")
;; -> "my name is conrad"

조건문

if

(if (condition)
    (then-statement)
  (else-statement))

condition 값은 nil 혹은 () 인 경우에만 False이고 나머지는 True이다.

then-statement 와 else-statement는 둘 다 함수 하나만 쓸 수 있게 되어있다. 만약 여러 프로시저를 넣고 싶다면 progn 으로 블록을 정의해서 써야한다.

(if (condition)
    (progn
      (statement 1)
      (statement 2))
  (progn
    (statement 3)
    (statement 4)))

and, or, not 등은 함수로 쓸 수 있다.

(and cond1 cond2 ...)
(or cond1 cond2 ...)
(not (condition))

else 처리가 필요 없다면 좀 더 간단한 when 이나 unless 를 쓸 수도 있다.

(when (condition)
  (statement1)
  (statement2))

(unless (false-condition)
  (statement1)
  (statement2))

whenunlessprogn 을 쓰지 않아도 여러 프로시저를 나열할 수 있다.

비교 함수들

(equal 10 10)              ;; -> t
(equal "str" "STR")        ;; -> nil
(equal '(1 2 3) '(1 2 3))  ;; -> t
(equal 'symb 'symb)        ;; -> t
(> 10 1)                   ;; -> t
(string= "abc" "ABC")      ;; -> nil

타입 확인

(integerp 10)  ;; -> t
(floatp 3.14)  ;; -> t

루프

while

(while (condition)
  (statement1)
  (statement2)
  ...)

함수형 언어 스타일 열거 함수

mapcar

리스트를 각 아이템을 특정 함수로 평가해서 리스트를 작성한다. 다른 언어에서 map 이라는 불리는 메서드와 거의 동일하다.

(defun make-double (x)
  (+ x x))

(mapcar #'make-double '(1 2 3 4 5))
;; -> (2, 4, 6, 8, 10)

처리 함수가 공용이 아니라면 보통은 lambda 함수를 이용해 간단하게 구현하곤 한다.

(mapcar
 (lambda (x)
   (+ x x))
 '(1 2 3 4 5))
;; -> (2, 4, 6, 8, 10)

mapc

mapcmapcar 와 비슷한데, 입력된 값을 그대로 반환하기 때문에 map 용도 보다는 그냥 for each에 가까운 용도로 사용한다.

(mapc
 (lambda (x)
   (+ x x))
 '(1 2 3 4 5))

;; => (1 2 3 4 5)

돌아오는 결과가 전혀 가공되지 않았음을 주의해서 보자.

mapconcat

리스트를 하나의 문자열로 합칠 때 사용한다.

(mapconcat #'identity '("hello" "world" "!") " ")
;; -> "hello world !"

위 예제에서 마지막 매개변수는 각 문자열이 합쳐 질 때 사이에 들어가는 문자열이다.

참고로 위 예제에서 identity 는 입력된 값을 그대로 돌려주는 함수다.

seq-filter

보통 다른 언어에서 filter라 부르는 메서드와 비슷한 용도의 함수다.

(require 'seq)
(seq-filter
 (lambda (x)
   (= 0 (% x 2)))
 '(1 2 3 4 5 6))

;; => (2 4 6)

내장 함수가 아니라 seq 라는 패키지에 들어있기 때문에 별도의 require가 필요하다는 것에 주의하자.

함수 형식

기본적인 함수 정의 방법

(defun function-name (param1 param2 ...)
  "documentation"
  ... body ...)

함수를 Emacs 커맨드로 쓰려면 interactive 를 선언해 주어야 한다.

(defun function-name (...)
  "documentation"
  (interactive)   ;; <----
  ... body ...)

옵셔널 매개 변수

(defun function-name (a b &optional c d)
  ;; ...
  )

위의 경우 a, b는 필수 매개 변수지만, c와 d는 넘기지 않아도 되며 이 경우 nil 이 값으로 들어온다.

버퍼 관련

char-after

(char-after 1)

goto-char

특정 위치로 커서를 옮긴다.

(goto-char (point-min))  ;; start of buffer
(goto-char (point-max))  ;; end of buffer

insert

(insert "text")

generate-new-buffer

이름대로 버퍼를 생성한다.

(generate-new-buffer "buffer-name")

반환되는 값은 버퍼의 이름인데 꼭 지정된 이름과 일치하지는 않을 수도 있다.

switch-to-buffer

버퍼를 선택한다.

(switch-to-buffer (generate-new-buffer "some-buffer"))

with-current-buffer

현재 선택된 버퍼로 뭔가를 할 때 사용한다.

(with-current-buffer some-buffer
  (insert "sample text"))

with-temp-buffer

임시로 빈 버퍼를 만들어 뭔가 할 때 사용한다. 파일 내용을 읽어들일 때나 새로운 파일을 작성할 때 등등 의외로 자주 사용된다.

(with-temp-buffer
  (insert-file-contents "readme.txt")
  (insert "hello world!")
  (write-file "helloworld.txt"))

선택영역 관련

region-beginning and region-end

(goto-char (region-beginning))
(goto-char (regin-end))

커서 관련

파일/디렉토리 관련

expand-file-name: 완벽한 절대 경로 구하기

축약 경로를 절대 경로로 해석해 준다.

(expand-file-name "~/test.txt")
;; -> ex: /user/home/directory/test.txt

file-relative-name: 상대 경로 구하기

특정 경로를 기준으로 원하는 파일 경로의 상대 경로를 구한다.

(file-relative-name "/foo/bar/name" "./")

file-name-nondirectory: 경로에서 파일 이름 구하기

(file-name-nondirectory "/foo/bar/name.ext")
;; -> "name.ext"

file-name-directory: 경로에서 디렉터리만 구하기

(file-name-directory "/foo/bar/sample.txt")
;; -> "/foo/bar/"

file-name-sans-extension: 확장자 제거

파일 이름에서 확장자를 제거한다.

(file-name-sans-extension "sample.txt")
;; -> "sample"

file-name-as-directory: 디렉터리 경로 명확히 하기

(file-name-as-directory "/foo/bar")
;; -> "/foo/bar/"

끝에 / 가 붙었다는 것을 잘 보자.

file-expand-wildcards: 파일 목록 구하기

아래 커맨드는 ~/foo/bar 디렉터리 안의 모든 .org 파일 목록을 돌려준다.

(file-expand-wildcards "~/foo/bar/*.org")

file-exists-p: 파일 존재 여부 검사

파일이 존재하면 non-nil을 반환한다.

(when (file-exists-p "/foo/bar/file/name")
  (message "file exists!"))

체크섬(Checksum)

md5

(md5 "some-string")

이렇게 하면 MD5 해시를 문자열로 구할 수 있다.