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 |

2008-05-19

構造体と総称関数の組み合わせ

| 07:16 | 構造体と総称関数の組み合わせ - わだばLisperになる を含むブックマーク はてなブックマーク - 構造体と総称関数の組み合わせ - わだばLisperになる

bit別冊 Common Lisp オブジェクトシステム - CLOSとその周辺を読んでいて、defmethodとdefstructを組み合わせる例が紹介されていたので試してみます。

なんだか関係のないところに力が入ってしまいましたが、CLでは、クラスと型が融合していて、defstructで作った型も引数特定子として扱えるということが本題です…。

(setq ガガンボ (make-双翅目))
(setq オニヤンマ (make-蜻蛉目))
(setq なんらかの虫 (make-昆虫))

(昆虫の足の数 ガガンボ)
;=> 6

(昆虫の足の数 オニヤンマ)
;=> 6

(双翅目の足の数 ガガンボ)
;=> 6

(双翅目の翅の数 ガガンボ)
;=> 2

(虫博士 なんらかの虫)
;=> 昆虫は足が6本だけど、そんな虫知らない。

(虫博士 ガガンボ)
;=>昆虫は足が6本、双翅目は、翅が2枚なんだ。

(虫博士 オニヤンマ)
;=>昆虫は足が6本、蜻蛉目は、翅が4枚なんだ。

(虫博士 'foo)
;=> それは虫じゃないね。

;; 定義
(defstruct (昆虫 (:conc-name "昆虫の"))
  (足の数 6))

(defstruct (双翅目 (:include 昆虫) (:conc-name "双翅目の"))
  (翅の数 2))

(defstruct (蜻蛉目 (:include 昆虫) (:conc-name "蜻蛉目の"))
  (翅の数 4))

(defmethod 虫博士 ((虫 双翅目))
  (format t "昆虫は足が6本、~Aは、翅が~D枚なんだ。~%" 
	  (type-of 虫) (双翅目の翅の数 虫)))

(defmethod 虫博士 ((虫 蜻蛉目))
  (format t "昆虫は足が6本、~Aは、翅が~D枚なんだ。~%" 
	  (type-of 虫) (蜻蛉目の翅の数 虫)))

(defmethod 虫博士 ((虫 昆虫))
  (format t "昆虫は足が6本だけど、そんな虫知らない。~%"))

(defmethod 虫博士 (虫)
  (format t "それは虫じゃないね。~%"))

CLで学ぶ「プログラミングGauche」 (8.3〜8.4.5)

| 05:45 | CLで学ぶ「プログラミングGauche」 (8.3〜8.4.5) - わだばLisperになる を含むブックマーク はてなブックマーク - CLで学ぶ「プログラミングGauche」 (8.3〜8.4.5) - わだばLisperになる

今回は、8.3から再開です。

8.3 真偽値を扱う手続き

8.3.1 等価述語以外の述語

SchemeもCLも大体同じです。というか、SchemeとCLが分岐する前から定番になっていたところなので、似ているのも当然といえば、当然。

  • 型を判定するもの
schemeCL
pair?consp
null?null
boolean?(typep x 'boolean)
symbol?symbolp
number?numberp
char?characterp
string?stringp
  • 性質を判定
odd?oddp
even?evenp
zero?zerop
char-alphabetic?alpha-char-p

CL(というか伝統的LISPでは)最後に付くpは、predicateの略で、述語には、-pを付ける伝統があります。最初期の述語には、atom、null等、語尾にpはついてませんが、60年代初頭のLISP 1.5から、既に-pという述語が定着しているようなので、LISP1から、1.5の間になんらかの出来事があったんでしょう。

8.3.2 否定

notの使い方。

CLのnotは、NILが多様な意味を持つために、nullと全く同じ働きをするのですが、両者は意味的に使いわけられています。

8.3.3 述語を取る手続き

これは、SRFIとCLでは名前の付け方にちょっと違いがあり

SRFICL
anysome
everyevery

のようになっています。

(some #'oddp '(2 4 8 1))
;=> T

(every #'oddp '(1 3 5))
;=> T
  • every-pred

every-predはないので作成

(defun every-pred (&rest fns)
  (lambda (&rest arg)
    (every (lambda (f) (every f arg))
           fns)))

(defun some-pred (&rest fns)
  (lambda (&rest arg)
    (some (lambda (f) (some f arg))
          fns)))

なんとなくanyじゃなくて、CLっぽくsome-predにしました。

every-pred応用例

(setf (symbol-function 'positive-integer?) 
      (every-pred #'integerp #'plusp))

(positive-integer? 4)
;=> T

(positive-integer? -3)
;=> NIL

(positive-integer? 2.4)
;=> NIL

(setf (symbol-function 'nonnegative?) (complement #'minusp))

(nonnegative? 2)
;=> T

(nonnegative? 0)
;=> T

(nonnegative? -2)
;=> NIL

[練習問題]

上で書いてしまってました…。

8.4 条件判断

CLとschemeと違うところは、

(if nil 'foo)
;=> NIL

となり、未定義にはならない所。

  • cond

CLには、Schemeのようにcondで=>は使えません。

setqが代入時の結果の値を返すことと、代入を組み合わせて

(let ((lst '(55 77 99)))
  (let (=>)
    (cond ((setq => (find-if #'oddp lst)) (* => 2))
          (:else 0))))
;=> 110

というのもたまに見かけます。(主に80年代位のコードで)

condでは、もともと=>が使えたりはしないので、いつから=>が使えるようになったのかと調べてみると、どうも、1978年のThe Revised Report on SCHEME(AIM-452)位からのようです。

P17に定義があるのですが、CLのマクロに翻訳してみました。

(import 'kmrcl:with-gensyms)

(defmacro test (pred fn alt)
  (with-gensyms (p f a)
    `(let ((,p ,pred) 
           (,f (lambda () ,fn))
           (,a (lambda () ,alt)))
       (if ,p 
           (funcall (funcall ,f) ,p)
           (funcall ,a)))))

(defmacro rs-cond (&rest clauses)
  (with-gensyms (v r)
    (cond ((null clauses) ''nil)
          ((null (cdar clauses))
           `(let ((,v ,(caar clauses))
                  (,r (lambda ()
                        (rs-cond ,@(cdr clauses)))))
              (if ,v ,v ,r)))
          ((eq (cadar clauses) '=>)
           `(test ,(caar clauses) ,(caddar clauses)
                  (rs-cond ,@(cdr clauses))))
          (t `(if ,(caar clauses)
                  (progn ,@(cdar clauses))
                  (rs-cond ,@(cdr clauses)))))))

(test 33333 #'list nil)
;=> (33333)

再帰的に展開され、ifに全部変換されます。

testマクロが肝のようですが、良くこんなの考えつくなーと感心します。

SRFI-61の拡張は、主に述語部が多値を返して、それを結果として受け取りたいような場合に使うようです。

CLでも、

(cond ((values 1 2))) ;=> 1

のようにcondの述部は、多値を返しません。

SRFI-61に相当するものもないので、欲しい場合は作ることになります。

自分も前に汚ないのを作ってみたことがあります。

8.4.2 CASE

caseはelse節のキーワードが、elseか、otherwiseかの違い位で同じです。

SRFI-87のような拡張はありません。

(case 1
  (0 'zero)
  (otherwise 'one))
;=> ONE

これも前に作ってみました。

8.4.3 andとor

andとorについても同じです。

8.4.4 whenとunless

これまた、CLと、Schemeとで違いはありません。

8.4.5 and-let*

and-let*はCLにないので、作るほかありません。

(defmacro and-let* ((&rest binds) &body body)
  (reduce (lambda (c res)
            `(let ((,(car c) ,(cadr c)))
               (and ,(car c) ,res)))
          binds
          :initial-value `(progn ,@body)
          :from-end 'T))

(and-let* ((a :a)
           (b :b)
           (c :c))
  (print (list a b c)))

;=> (:A :B :C) 

CL界隈には、and-let*的なものはないので便利なときには便利かもしれません。

次回9章から再開です。

ゲスト



トラックバック - http://cadr.g.hatena.ne.jp/g000001/20080519