Hatena::Groupcadr

わだばLisperになる このページをアンテナに追加 RSSフィード

2004 | 12 |
2005 | 01 | 02 | 07 | 10 | 11 |
2006 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 11 |

2011-05-01

C.I.CLを眺める(14) TRANSPOSE

| 21:11 | C.I.CLを眺める(14) TRANSPOSE - わだばLisperになる を含むブックマーク はてなブックマーク - C.I.CLを眺める(14) TRANSPOSE - わだばLisperになる

今回は、C.I.CLのlist.lispから TRANSPOSE です。

名前からは具体的にどういう動作になるのかいまいち想像がつきませんが、ドキュメントによればツリーのCARとCDRを再帰的に交換するもののようです。

(import 'com.informatimago.common-lisp.list:transpose)

(transpose '(a b c d))
;=> ((((NIL . D) . C) . B) . A)

(transpose '((a . b) . (c . d)))
;=> ((D . C) B . A)

初心者の頃によく作りがちな失敗REVERSEの様な動作ですが、どういうところで使うのでしょうか。

定義は、

(DEFUN TRANSPOSE (TREE)
  "
RETURN: A tree where all the CAR and CDR are exchanged.
"
  (IF (ATOM TREE)
      TREE
      (CONS (TRANSPOSE (CDR TREE)) (TRANSPOSE (CAR TREE)))))

となっています。

2011-04-23

C.I.CLを眺める(13) MEMQ

| 22:29 | C.I.CLを眺める(13) MEMQ - わだばLisperになる を含むブックマーク はてなブックマーク - C.I.CLを眺める(13) MEMQ - わだばLisperになる

今回は、C.I.CLのlist.lispから MEMQ です。

MEMQ は CLより前のMacLISPには標準で存在していたものですが、CLでは、比較関数(:TEST)が別に取れるようになったため、比較関数ごとに特化したものはなくなりMEMBER一つとなりました。

それでも、EQに特化したMEMQは割と愛用されているようで、大抵の処理系では内部や拡張パッケージにはMEMQが存在しているようです。

ということで定義は

(DEFUN MEMQ (ITEM LIST)
  "
RETURN:   (MEMBER ITEM LIST :TEST (FUNCTION EQ))
"
  (MEMBER ITEM LIST :TEST (FUNCTION EQ)))

となっています。たまに#'ではなくFUNCTIONと書くのが、Bourguignon氏の特徴ですが何故なのかは謎。

動作は、

(import 'com.informatimago.common-lisp.list:memq)

(memq :bar '(foo :bar baz))
;=> (:BAR BAZ)

自作する際に注意したいのが最適化の指定ですが、C.I.CLでも

(DECLAIM (INLINE PLIST-PUT PLIST-GET PLIST-REMOVE MEMQ))

のようにインライン展開の指定があります。これがないと

  • memq (inline指定なし)
(dotimes (i 100000000)
  (memq :bar '(foo :bar baz)))
;⇒ NIL
----------
Evaluation took:
  0.961 seconds of real time
  0.960000 seconds of total run time (0.960000 user, 0.000000 system)
  99.90% CPU
  2,300,195,133 processor cycles
  0 bytes consed

Intel(R) Core(TM)2 Duo CPU     P8600  @ 2.40GHz
  • member
(dotimes (i 100000000)
  (member :bar '(foo :bar baz)))
;⇒ NIL
----------
Evaluation took:
  0.044 seconds of real time
  0.040000 seconds of total run time (0.040000 user, 0.000000 system)
  90.91% CPU
  104,561,685 processor cycles
  0 bytes consed

Intel(R) Core(TM)2 Duo CPU     P8600  @ 2.40GHz

のように大幅に逆効果になってしまうこともあるようなので注意が必要かもしれません。

2011-04-21

C.I.CLを眺める(12) PLIST-REMOVE

| 18:10 | C.I.CLを眺める(12) PLIST-REMOVE - わだばLisperになる を含むブックマーク はてなブックマーク - C.I.CLを眺める(12) PLIST-REMOVE - わだばLisperになる

今回は、C.I.CLのlist.lispから PLIST-REMOVE です。

PLISTから指定したキーを持つ(最初の)組を削除する、というものです。

REMFがあるじゃないかと思いましたが、REMFは削除の結果に応じて真偽値を返すので、その辺りの使い勝手を調整したもののようです。

(DEFUN PLIST-REMOVE (PLIST PROP)
  "
DO:      (REMF PLIST PROP)
RETURN:  The modified PLIST.
"
  (REMF PLIST PROP)
  PLIST)
  • REMF
(let ((plist (list :foo 1 :bar 2 :baz 3)))
  (remf plist :foo))
;=> T
  • PLIST-REMOVE
(let ((plist (list :foo 1 :bar 2 :baz 3)))
  (plist-remove plist :foo))
;=> (:BAR 2 :BAZ 3)

PLIST-REMOVEの返り値の方が使い道はありそうです。

2011-04-19

C.I.CLを眺める(11) PLIST-GET

| 21:04 | C.I.CLを眺める(11) PLIST-GET - わだばLisperになる を含むブックマーク はてなブックマーク - C.I.CLを眺める(11) PLIST-GET - わだばLisperになる

今回は、C.I.CLのlist.lispから PLIST-GET です。

前回はPUTでしたが今回はGET。

PLISTから情報を取り出す関数には標準でGETFがありますが、定義もPLIST-PUTとの整合性のために作られたエイリアスという感じです。

(DEFUN PLIST-GET (PLIST PROP)
  "
 Extract a value from a property list.
 PLIST is a property list, which is a list of the form
 (PROP1 VALUE1 PROP2 VALUE2...).  This function returns the value
 corresponding to the given PROP, or nil if PROP is not
 one of the properties on the list.
"
  (GETF PLIST PROP))

動作は、

(import 'com.informatimago.common-lisp.list:plist-get)

(plist-get '(:foo 0 :bar 1 :baz 2)
           :foo)
;=> 0

というところ

2011-04-18

C.I.CLを眺める(10) PLIST-PUT

| 20:36 | C.I.CLを眺める(10) PLIST-PUT - わだばLisperになる を含むブックマーク はてなブックマーク - C.I.CLを眺める(10) PLIST-PUT - わだばLisperになる

今回は、C.I.CLのlist.lispから PLIST-PUT です。

名前から想像できるようにPLISTにエントリーを加える(もしくは変更する)関数です。

定義は、

(DEFUN PLIST-PUT (PLIST PROP VALUE)
  "
 Change value in PLIST of PROP to VALUE.
 PLIST is a property list, which is a list of the form
 (PROP1 VALUE1 PROP2 VALUE2 ...).  PROP is a symbol and VALUE is any object.
 If PROP is already a property on the list, its value is set to VALUE,
 otherwise the new PROP VALUE pair is added.  The new plist is returned;
 use `(setq x (plist-put x prop val))' to be sure to use the new value.
 The PLIST is modified by side effects.
"
  (SETF (GETF PLIST PROP) VALUE)
  PLIST)

となっています。

使用上の注意が書かれた詳細なドキュメントが付き。

動作は、

(import 'com.informatimago.common-lisp.list:plist-put)

(let ((pl (list :foo 0 :bar 1 :baz 2)))
  (plist-put pl :baz 200)
  pl)
;=> (:FOO 0 :BAR 1 :BAZ 200)

というところです

2011-04-17

C.I.CLを眺める(9) HASHED-INTERSECTION

| 18:23 | C.I.CLを眺める(9) HASHED-INTERSECTION - わだばLisperになる を含むブックマーク はてなブックマーク - C.I.CLを眺める(9) HASHED-INTERSECTION - わだばLisperになる

今回は、C.I.CLのlist.lispから HASHED-INTERSECTION です。

(DEFUN HASHED-INTERSECTION (SET1 SET2)
  "
AUTHORS: Paul F. Dietz <dietz@dls.net>
         Thomas A. Russ <tar@sevak.isi.edu>
"
  (DECLARE (OPTIMIZE SPEED (SAFETY 0) (DEBUG 0))
           (LIST SET1 SET2))
  (LET ((TABLE (MAKE-HASH-TABLE :SIZE (LENGTH SET2)))
        (RESULT NIL))
    (DOLIST (E SET2) (SETF (GETHASH E TABLE) T))
    (DOLIST (E SET1) (WHEN (GETHASH E TABLE)
                       (PUSH E RESULT)
                       (SETF (GETHASH E TABLE) NIL)))
    RESULT))

となっています。

コメントからするとオリジナルの作者は、Paul F. Dietz、 Thomas A. Russの両氏のようです。

仕組みとしては、ハッシュテーブルを利用して共通する部分を抜き出すというもの。

動作は、

(import 'com.informatimago.common-lisp.list:hashed-intersection)

(hashed-intersection '(1 2 3 4)
                     '(1 2 3 4))
;=> (4 3 2 1)

(hashed-intersection '(1 2 3 4 5 6)
                     '(1 2 3 4))
;=> (4 3 2 1)

(hashed-intersection '("1" "2" "3" "4" "5" "6")
                     '("1" "2" "3" "4") )
;=> NIL

というところ。ハッシュの一致判定にデフォルトのEQLが使われているため文字列はスルーされてしまいます。

2011-02-16

C.I.CLを眺める(8) HASHED-DELETE-DUPLICATES

| 18:23 | C.I.CLを眺める(8) HASHED-DELETE-DUPLICATES - わだばLisperになる を含むブックマーク はてなブックマーク - C.I.CLを眺める(8) HASHED-DELETE-DUPLICATES - わだばLisperになる

今回は、C.I.CLのlist.lispから HASHED-DELETE-DUPLICATES です。

定義は、

(DEFUN HASHED-DELETE-DUPLICATES (SEQUENCE &KEY (TEST (FUNCTION EQL))
                                 TEST-NOT
                                 (START 0) (END (LENGTH SEQUENCE))
                                 (KEY (FUNCTION IDENTITY))
                                 (FROM-END NIL))
  (HASHED-REMOVE-DUPLICATES
   SEQUENCE :TEST TEST :TEST-NOT TEST-NOT :START START :END END
   :KEY KEY :FROM-END FROM-END))

ですが、一連のHASHED-*-DUPLICATESは、ハッシュに登録して重複を取り除くということから、原理的に元の配列を破壊することはない、ということなのか、REMOVEのバージョンをそのまま呼び出しています。

前回のものは、セットを作るものでリストとは違い順番等は無視できましたが、今回は、リストを対象にするもののようです。

動作はREMOVE系と同じなので書く意味もあまりないですが、

(import 'com.informatimago.common-lisp.list::hashed-delete-duplicates)
;=> T

(hashed-delete-duplicates "fooo")
;=> "fo"

(hashed-delete-duplicates #(f o o o o))
;=> #(F O)

(hashed-delete-duplicates '(f o o o o))
;=> (O F)

というところ

2011-01-26

C.I.CLを眺める(7) HASHED-REMOVE-DUPLICATES

| 20:23 | C.I.CLを眺める(7) HASHED-REMOVE-DUPLICATES - わだばLisperになる を含むブックマーク はてなブックマーク - C.I.CLを眺める(7) HASHED-REMOVE-DUPLICATES - わだばLisperになる

今回は、C.I.CLのlist.lispから HASHED-REMOVE-DUPLICATES です。

前回のものは、セットを作るものでリストとは違い順番等は無視できましたが、今回は、リストを対象にするもののようです。

定義は、

(DEFUN HASHED-REMOVE-DUPLICATES (SEQUENCE &KEY (TEST (FUNCTION EQL))
                                 TEST-NOT
                                 (START 0) (END (LENGTH SEQUENCE))
                                 (KEY (FUNCTION IDENTITY))
                                 (FROM-END NIL))
  (WHEN TEST-NOT
    (WARN ":TEST-NOT is deprecated.")
    (SETF TEST (COMPLEMENT TEST-NOT)))
  (LET ((TABLE (MAKE-HASH-TABLE :TEST TEST :SIZE (- END START))))
    (MAP NIL (IF FROM-END
                 (LAMBDA (ITEM)
                   (LET ((ITEM-KEY (FUNCALL KEY ITEM)))
                     (MULTIPLE-VALUE-BIND (VAL PRE) (GETHASH ITEM-KEY TABLE)
                       (DECLARE (IGNORE VAL))
                       (UNLESS PRE (SETF (GETHASH ITEM-KEY TABLE) ITEM)))))
                 (LAMBDA (ITEM) (SETF (GETHASH (FUNCALL KEY ITEM) TABLE) ITEM)))
         (IF (OR (/= START 0) (/= END (LENGTH SEQUENCE)))
             (SUBSEQ SEQUENCE START END) SEQUENCE))
    (IF (EQ (TYPE-OF SEQUENCE) 'CONS)
        (LET ((RESULT '()))
          (MAPHASH (LAMBDA (KEY VALUE) (DECLARE (IGNORE KEY)) (PUSH VALUE RESULT))
                   TABLE)
          (IF (OR (/= START 0) (/= END (LENGTH SEQUENCE)))
              (NCONC (SUBSEQ SEQUENCE 0 START) RESULT (SUBSEQ SEQUENCE END))
              RESULT))
        (IF (OR (/= START 0) (/= END (LENGTH SEQUENCE)))
            (LET ((RESULT (MAKE-SEQUENCE (TYPE-OF SEQUENCE)
                                         (+ START (HASH-TABLE-COUNT TABLE)
                                            (- (LENGTH SEQUENCE) END))))
                  (I START))
              (REPLACE RESULT SEQUENCE :END2 START)
              (MAPHASH (LAMBDA (KEY VALUE) (DECLARE (IGNORE KEY))
                               (SETF (AREF RESULT I) VALUE) (INCF I)) TABLE)
              (REPLACE RESULT SEQUENCE :START2 END :START1 I)
              RESULT)
            (LET ((RESULT (MAKE-SEQUENCE (TYPE-OF SEQUENCE)
                                         (HASH-TABLE-COUNT TABLE)))
                  (I 0))
              (MAPHASH (LAMBDA (KEY VALUE) (DECLARE (IGNORE KEY))
                               (SETF (AREF RESULT I) VALUE) (INCF I)) TABLE)
              RESULT)))))

となっています。

随分長いですが、シーケンス全般に対応していること、:test、:key、:start、:end、:from-end等CL標準の関数の作法に準拠して沢山のパラメータを取れるようにしてあるので長くなっているようです。

test-notが使われた時には、"
TEST-NOT is deprecated."と警告が出るという細かさ。

(EQ (TYPE-OF SEQUENCE) 'CONS)は、(CONSP SEQUENCE)でも良さそうですが、飽くまでSEQUENCEである、ということなのでしょうか。

大まかな処理の流れとしては、前回と同様、ハッシュ表で要素を記録することによって重複を排除しています。:from-endの場合は、後ろから見るわけではなくて、既出のものを優先。しかし、CLのMAPHASHは結果は入力の順序を保持しなかったと思うので、この辺りどういうことなのか少し分かりません(KEY次第で変ってくる?)

(hashed-remove-duplicates "41234321" :key (constantly #\x))
;=> "1"

(hashed-remove-duplicates "41234321" :key (constantly #\x) :from-end T)
;=> "4"

そして、:startと:endが取れるので、指定してあった場合は、重複を削除した結果とサンドイッチ。挟む方法としては、リストの場合は、NCONC、ベクター系の場合は、REPLACEを利用しています。

さて、上記の関数ですが、リスト系は問題なく動きますが、ベクター系がSBCLだとエラーになってしまうようです。

原因を追い掛けてみましたが、どうも、TYPE-OFで取れる情報だと、要素の数まで指定された型になってしまうようで、今回の様に結果の長さがオリジナルと違ってしまうと都合が悪いようです。

(type-of "ooo")
;=> (SIMPLE-ARRAY CHARACTER (3))

(make-sequence (type-of "ooo") 1)
;The length requested (1) does not match the type restriction in (SIMPLE-ARRAY CHARACTER (3))

ということで、TYPE-OFの箇所をCLASS-OFにしてみたところ問題ないようです。

HyperSpecによれば、

(subtypep (type-of object) (class-of object)) =>  true, true

が常に成り立つそうなので問題なさそうではあります。

動作は、

(import 'com.informatimago.common-lisp.list::hashed-remove-duplicates)

(hashed-remove-duplicates "1234321")
;=> "1234"

というところ。

2011-01-23

C.I.CLを眺める(6) HASHED-SET-REMOVE-DUPLICATES

| 23:30 | C.I.CLを眺める(6) HASHED-SET-REMOVE-DUPLICATES - わだばLisperになる を含むブックマーク はてなブックマーク - C.I.CLを眺める(6) HASHED-SET-REMOVE-DUPLICATES - わだばLisperになる

今回は、C.I.CLのlist.lispから HASHED-SET-REMOVE-DUPLICATES です。

定義は、

(DEFUN HASHED-SET-REMOVE-DUPLICATES (SEQUENCE &KEY (TEST (FUNCTION EQL))
                                     (KEY (FUNCTION IDENTITY)))
  (LET ((TABLE (MAKE-HASH-TABLE :TEST TEST :SIZE (LENGTH SEQUENCE)))
        (RESULT '()))
    (MAP NIL (LAMBDA (ITEM) (SETF (GETHASH (FUNCALL KEY ITEM) TABLE) ITEM)) SEQUENCE)
    (MAPHASH (LAMBDA (KEY VALUE) (DECLARE (IGNORE KEY)) (PUSH VALUE RESULT)) TABLE)
    RESULT))

となっています。

LISTをSETにするには、REMOVE-DUPLICATESすることになると思うのですが、一旦ハッシュテーブルに登録して読み出すことにより重複を削除しています。

シンボルはEXPORTされていない様子。

何故か#'ではなくFUNCTIONと書かれていますが、PJBさんは割とリーダーマクロを使わずFUNCTIONをそのまま書いたりするようです。

動作は、

(import 'com.informatimago.common-lisp.list::hashed-set-remove-duplicates)

(hashed-set-remove-duplicates "あかまきがみあおまきがみきまきがみ")
;=> (#\HIRAGANA_LETTER_O #\HIRAGANA_LETTER_MI #\HIRAGANA_LETTER_GA
;    #\HIRAGANA_LETTER_KI #\HIRAGANA_LETTER_MA #\HIRAGANA_LETTER_KA
;    #\HIRAGANA_LETTER_A)

(COERCE (nreverse
         (hashed-set-remove-duplicates "あかまきがみあおまきがみきまきがみ"))
        'STRING)
;=> "あかまきがみお"

というところ

2011-01-19

C.I.CLを眺める(5) CIRCULAR-LENGTH

| 20:30 | C.I.CLを眺める(5) CIRCULAR-LENGTH - わだばLisperになる を含むブックマーク はてなブックマーク - C.I.CLを眺める(5) CIRCULAR-LENGTH - わだばLisperになる

今回は、C.I.CLのlist.lispから CIRCULAR-LENGTH です。

名前のとおり循環リストの長さ(というか要素数)を数えるものです.

定義は、

(defun circular-length (list)
  "LIST must be either a proper-list or a circular-list, not a dotted-list.
RETURN: the total length ; the length of the stem ; the length of the circle.
"
  (let ((indexes (make-hash-table)))
    (loop
       :for i :from 0
       :for current :on list
       :do (let ((index (gethash current indexes)))
             (if index
                 ;; found loop
                 (return (values i index (- i index)))
                 (setf (gethash current indexes) i)))
       :finally (return (values i)))))

となっていて、コンスセルを先頭から一つずつ位置と一緒に記録しておいて、同じものが現われたら結果を返す、というようになっています。

結果は多値で返して、1値目は、全体の長さ、2値目は、循環が始まるところまでの長さ、3値目は、循環しているリストの長さです。

動作は、

(import 'com.informatimago.common-lisp.list:circular-length)

(circular-length '(1 2 3 4))
;=> 4

(circular-length '#0=(1 2 3 . #0#))
;=> 3
;   0
;   3

(circular-length '(1 . #0=(2 3 . #0#)))
;=> 3
;   1
;   2

というところ

2011-01-03

C.I.CLを眺める(4) MAKE-CIRCULAR-LIST

| 01:50 | C.I.CLを眺める(4) MAKE-CIRCULAR-LIST - わだばLisperになる を含むブックマーク はてなブックマーク - C.I.CLを眺める(4) MAKE-CIRCULAR-LIST - わだばLisperになる

今回は、C.I.CLのlist.lispから MAKE-CIRCULAR-LIST です。

前回の ENSURE-CIRCULAR との違いは、こちらの方はMAKE-LISTの様に新しくリストを指定した個数の要素で作成するというところです。

(defun make-circular-list (size &key initial-element)
  "
RETURN: a new circular list of length SIZE.
POST: (circular-length (make-circular-list size)) == (values size 0 size)
"
  (let ((list (make-list size :initial-element initial-element)))
    (setf (cdr (last list)) list)
    list))

動作は、

(import 'com.informatimago.common-lisp.list:make-circular-list)

(setq *print-circle* 'T)
;=> T
(make-circular-list 5)
;=> #1=(NIL NIL NIL NIL NIL . #1#)

というところ

2010-12-27

C.I.CLを眺める(3) ENSURE-CIRCULAR

| 00:15 | C.I.CLを眺める(3) ENSURE-CIRCULAR - わだばLisperになる を含むブックマーク はてなブックマーク - C.I.CLを眺める(3) ENSURE-CIRCULAR - わだばLisperになる

今回は、C.I.CLのlist.lispからENSURE-CIRCULARです。

名前からして循環リストを作成するものというのが分かります。定義は、

(defun ensure-circular (list)
  "
If list is not a circular list, then modify it to make it circular.
RETURN: LIST
"
  (if (proper-list-p list)
      (setf (cdr (last list)) list)
      list))

で、前に定義したPROPER-LIST-Pを使い引数が真正リストかどうかをチェックし、真正リストでない場合は、引数をそのまま返しています。

動作は、

(import 'com.informatimago.common-lisp.list:ensure-circular)

(setq *print-circle* 'T)

(ensure-circular '(1 2 3))
;=> #1=(1 2 3 . #1#)

(ensure-circular '(1 . 2 ))
;=> (1 . 2 )

というところ

2010-12-19

C.I.CLを眺める(2) PROPER-LIST-P

| 20:02 | C.I.CLを眺める(2) PROPER-LIST-P - わだばLisperになる を含むブックマーク はてなブックマーク - C.I.CLを眺める(2) PROPER-LIST-P - わだばLisperになる

今回は、C.I.CLのlist.lispからPROPER-LIST-Pです。

名前からして真正リストかどうかを判定するもののようですが、定義は、

(defun proper-list-p (object)
  "
RETURN: whether object is a proper list
NOTE:   terminates with any kind of list, dotted, circular, etc.
"
  (labels ((proper (current slow)
             (cond ((null current)       t)
                   ((atom current)       nil)
                   ((null (cdr current)) t)
                   ((atom (cdr current)) nil)
                   ((eq current slow)    nil)
                   (t                    (proper (cddr current) (cdr slow))))))
    (and (listp object) (proper object (cons nil object)))))

となっています。

SRFI-1で言えば、proper-list?ですが、循環リストを検出するために、properというローカル関数を定義して使っているようです。

動作は、

(import 'com.informatimago.common-lisp.list:proper-list-p)

(proper-list-p ())
;=> T

(proper-list-p 'a)
;=> NIL

(proper-list-p '(a))
;=> T

(proper-list-p '(a . b))
;=> NIL

(proper-list-p '#0=(even odd . #0#))
;=> NIL

というところ。

2010-12-09

C.I.CLを眺める(1) ENSURE-LIST

| 22:34 | C.I.CLを眺める(1) ENSURE-LIST - わだばLisperになる を含むブックマーク はてなブックマーク - C.I.CLを眺める(1) ENSURE-LIST - わだばLisperになる

KMRCLも230回に及び、残りは、1ファイル完結で長かったり、理解が難しかったりするので、KMRCL以外もちょこちょこ眺めて行こうかなと思います。

どのユーティリティにしようかなと思いましたが、以前からなんとなく気になっていたPascal J. Bourguignon氏のライブラリを眺めることに。

Bourguignon氏は、comp.lang.lisp等で質問に答えたりしていて良く目にするのですが、その際に自作のユーティリティを示すことが結構あり、しかも、それが面白いので気になっていました。

ユーティリティは、

からgitで取得できます。ちょっと導入に癖がありますが、ページの案内どおりにしていれば導入できるでしょう。

ところで、com.informatimago.common-lispだと非常に長いので、どういう風に略そうかと悩みましたが、PJBCLとか勝手な名前を付けるのもあれだし、comp.lang.lispをc.l.lと略すようにC.I.CLと略してみることにしました。

今回は、そのC.I.CLのlist.lispからENSURE-LISTです。

まず、list.lispファイル全体で、パッケージが切ってあって、:com.informatimago.common-lisp.listというパッケージになっています。

実装は、

(DEFUN ENSURE-LIST (ITEM)
  "
RETURN: item if it's a list or (list item) otherwise.
"
  (IF (LISTP ITEM) ITEM (LIST ITEM)))

となっています。

KMRCLだと、KMRCL:MKLISTで同じものが用意されています。

しかし、命名方としては、ENSURE-系の方が動作を的確に表現できているのではないかと。

そして、何故かコードが珍しく大文字です。

ドキュメント文字列の最初と最後の改行は何故なのか気になりますが、見易さへの配慮でしょうか。

コーディングスタイル的に色々と謎なこだわりが多そうなのが眺めていて面白いです。

動作は

(use-package :com.informatimago.common-lisp.list)

(ensure-list 'a)
;=> (A)

(ensure-list '(a))
;=> (A)

(kmrcl:mklist 'a)
;=> (A)

というところ。