The Common Lisp Cookbook – その他

Table of Contents

The Common Lisp Cookbook – その他

📢 🎓 ⭐ Learn Common Lisp efficiently in videos, by the Cookbook's main contributor. Learn more.

🖊️ Discover a new Common Lisp and Coalton editor for beginners: mine and a new VSCode extension for Common Lisp: OLIVE.

複雑なデータ構造を再利用する

関数を「関数型」らしく振る舞わせたい、つまり副作用なしにfreshな結果を返したい場合があります。一方で、既存のデータを破壊的に再利用して変更したい場合もあります。例として appendnconc の違いを考えてください。

実は、オプショナル引数(またはキーワード引数)を使えば、どちらも実現できます。例を見てみましょう。2つの行列 m1m2 を引数に取り、m1m2 に依存したサイズの結果行列を計算して返す complex-matrix-stuff という関数を書いているとします。新しい結果を返すには、たとえば (make-appropriate-result-matrix-for m1 m2) で作られる空の行列が必要です。

この関数を古典的な教科書風に実装すると、おおよそ次のようになります。

(defun complex-matrix-stuff (m1 m2)
  (let ((result (make-appropriate-result-matrix-for m1 m2)))
    ;; ... compute storing the results in RESULT
    result))

そして次のように使います。

(setq some-matrix (complex-matrix-stuff A B))

しかし、次のように書いてはどうでしょうか。

(defun complex-matrix-stuff (m1 m2
                             &optional
                             (result
                              (make-appropriate-result-matrix-for m1 m2)))
  ;; ... compute storing the results in RESULT
  result)

これで両方の使い方ができます。次のように、その場で「結果を作る」ことは今までどおりできます。

(setq some-matrix (complex-matrix-stuff A B))

しかし、以前に割り当てた行列を(破壊的に)再利用することもできます。

(complex-matrix-stuff A B some-appropriate-matrix-I-built-before)

または、関数を次のように使えます。

(setq some-other-matrix
      (complex-matrix-stuff A B some-appropriate-matrix-I-built-before))

この場合、最終的には次のようになります。

* (eq some-other-matrix some-appropriate-matrix-I-built-before)

T

SUBSEQ で新しいシーケンスを cons する代わりに ADJUST-ARRAY を使う

シーケンスを扱うほとんどの CL 関数は startend キーワードを受け付けるので、実際に部分シーケンスを作らずに、その部分シーケンス上で動作させることができます。つまり、次の代わりに

(count #\a (subseq long-string from to))

当然、次を使うべきです。

(count #\a long-string :start from :end to)

これは同じ結果を返しますが、不要な中間部分シーケンスを作りません。

しかし、新しいデータを作ることを避けられないように見える場合もあります。キーが文字列であるハッシュテーブルを考えてください。探しているキーが別の文字列の部分文字列である場合、おそらく次のように書くことになります。

(gethash (subseq original-string from to)
         hash-table)

しかし、そうする必要はありません。1つの displaced 文字列を作り、adjust-array で何度も再利用できます。

(let ((substring (make-array 0
                             :element-type 'character
                             :displaced-to ""
                             :displaced-index-offset 0)))
  ;; more code
  (gethash
   (adjust-array substring (- to from)
                 :displaced-to original-string
                 :displaced-index-offset from)
   hash-table)
  ;; even more code
  )

Page source: ja/misc.md

T
O
C