Common Lisp 自作関数・マクロ紹介

# plot
```common-lisp
(ql:quickload :plot/vega)

(defun plot (lst)
    (plot:plot
     (vega:defplot plot
       `(:data ,(plot-list-to-plist lst)
         :mark line
         :width 480
         :heigth 320
         :encoding (:x (:field :x
                        :type :quantitative)
                    :y (:field :y
                        :type :quantitative))))))

(defun plot-list-to-plist (lst)
  `(:x ,(make-array (length lst) :initial-contents (qutimes (i (length lst)) i))
    :y ,(make-array (length lst) :initial-contents lst)))

(plot '(1 2 3 4 5)) ; plotが表示される
```

pythonで言うところの`matplotlib.pyplot`に相当する関数。`plot/vega`というパッケージを利用。listをplistに変換しないといけない点に注意。シンプルなプロット以外にも、ヒストグラム等[いろいろな種類](https://lisp-stat.dev/docs/examples/plotting/)がある。コード内の`qutimes`も自作マクロで、後述。
vegaパッケージ内で利用できるデータ数がコード内で指定されているため、大量のデータを扱うときに`setq`等で扱えるデータ数を変更するということができない。`(ql:where-is-system :plot)`で返るディレクトリ配下の、`src/vega/plot.lisp`の109行目あたりにある`50000`という定数を大きくすることを推奨 (例えば`500000`くらいに)。
py4clからpyplotの呼び出しも試したのだけれど、プロットウィンドウが閉じなくなる等の不具合があったので、利用を諦めた。

# qutimes
```common-lisp
(defmacro qutimes (count &body body)
  (let ((i (car count))
        (stop (cadr count))
        (acc (gensym)))
    `(labels ((rev (,i ,acc)
                (if (< ,i ,stop)
                    (rev (1+ ,i) (cons (progn ,@body) ,acc))
                    (reverse ,acc))))
       (rev 0 nil))))

(qutimes (i 5)
  (+1 i)) ; (1 2 3 4 5)
```
pythonの`np.arange(0, x, 1)`のような挙動をするマクロ。与えられた数まで0から1つずつ値を取っていって何かしらの処理を行い、その返り値で構成されたリストを返す。上記の例で言えば、pythonの`[1 + x for x in np.arange(0, 5, 1)]`と同じ。
挙動が`dotimes`と似ていて、各ステップの返り値を溜め込むという要素がqueueっぽいので、`qutimes`という名前にした。

# qulist
```common-lisp
(defmacro qulist (lst &body body)
  (let ((symbol (car lst))
        (for (cadr lst))
        (ls (gensym))
        (acc (gensym)))
    `(labels ((rev (,ls ,acc)
                (if (consp ,ls)
                    (rev (cdr ,ls)
                         (let ((,symbol (car ,ls)))
                           (cons (progn ,@body) ,acc)))
                    (reverse ,acc))))
       (rev ,for nil))))

(qulist (elm '(1 2 3 4 5))
  (1+ elm)) ; (2 3 4 5 6)
```
pythonのリスト内包表記のような挙動をするマクロ。与えられたリストから1つずつ値を取っていって何かしらの処理を行い、その返り値で構成されたリストを返す。上記の例で言えば、pythonの`[elm + 1 for elm in [1, 2, 3, 4, 5]]`と同じ。
挙動が`dolist`と似ていて、かつリストの返り値を溜め込むという要素がqueueっぽいので、`qulist`という名前にした。

# fstr
```common-lisp
(ql:quickload :cl-ppcre)

(defmacro fstr (str)
  (labels ((peel (str) (subseq str 1 (1- (length str)))))
    (let ((symbols (mapcar #'(lambda (str)
                               (let ((val (read-from-string str)))
                                 (cond ((stringp val) str)
                                       ((symbolp val) val)
                                       (t val))))
                           (mapcar #'peel (cl-ppcre:all-matches-as-strings "{.*?}" str))))
          (format (cl-ppcre:regex-replace-all "{.*?}" str "~A")))
      `(format nil ,format ,@symbols))))

(let ((hoge "hOgE")
      (piyo 'pIyO)
      (fuga (+ 1 2)))
  (fstr "{hoge}/{piyo}/{fuga}")) ; hOgE/PIYO/3
```
pythonのf文字列のような挙動をするマクロ。シンボルが大文字で表示される点と、`:`を用いた制御ができない点には注意。

# let-if
```common-lisp
(defmacro let-if (test symbols bindings &body body)
  `(multiple-value-bind ,symbols
       (if ,test
           (values ,@(car bindings))
           (values ,@(cadr bindings)))
     ,@body))

(let-if (> 10 0) (x y) (("Plus" (+ 1 2)) ("Minus" 'hoge))
  (print x)
  (print y)) ; "Plus" 3
```
`bindings`のcar部分に条件が真の場合の値を、cdar部分に条件が偽の場合の値を入れる。
python等の手続き型言語であれば
```python
if 10 > 0:
    x = "Plus"
    y = 1 + 2
else:
    x = "Minus"
    y = "hoge"
print(x)
print(y)
```
のように一般的に書ける処理でも、Lispだと
```common-lisp
(if (> 10 0)
  (let ((x "Plus")
        (y (+ 1 2)))
    (print x)
    (print y))
  (let ((x "Minus")
        (y 'hoge))
    (print x)
    (print y)))
```
のように、if分岐ごとにletで束縛して、それぞれに処理 (`(print x)` `(print y)`) を書かないといけない (はず)。このlet-ifを利用すれば、条件ごとの束縛を一括して記述できる。引数の渡し方に少しクセがあるのはご愛嬌。

# let-case
```common-lisp
(defmacro let-case (keyform symbols bindings &body body)
  `(multiple-value-bind ,symbols
       (case ,keyform
         ,@(qulist (bind bindings)
             `(,(car bind) (values ,@(cdr bind)))))
       ,@body))

(let ((hoge 10))
  (let-case hoge (x y) ((5 0 0) (10 100 100))
    (print (+ x y)))) ; 200
```
`let-if`の`case`版。`bindings`の各リストの最初の値が判定用。それに続く値が`,@`で分解・分配されて、束縛される。

# let-cond
```common-lisp
(defmacro let-cond (symbols bindings &body body)
  `(multiple-value-bind ,symbols
       (cond ,@(qulist (bind bindings)
                 `(,(car bind) (values ,@(cdr bind)))))
     ,@body))

(let-cond (x y) (((= 3 5) 3 (+ 2 3))
                 ((= 1 1) "Hoge" 'hoge))
  (list x y)) ; ("Hoge" HOGE)
```
`let-if`の`cond`版。`bindings`の各リストの最初の値が判定用。それに続く値が`,@`で分解・分配されて、束縛される。