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-06-20

無限小/大との比較

| 16:42 | 無限小/大との比較 - わだばLisperになる を含むブックマーク はてなブックマーク - 無限小/大との比較 - わだばLisperになる

bit別冊 Common Lisp オブジェクトシステム - CLOSとその周辺を眺めていたところ、無限小や、無限大を定義し、総称関数でラップすることによって、数値の無限大/小との比較ができるだろう、というような文を読んで、「なるほど!、面白そうだ」と思ったので、早速試してみました。

  • inf.0 +inf.0という表現は、R6RSから、拝借しましたが、なんとなくキーワードにしました。
(defgeneric binary> (object1 object2)
  (:documentation "object1 object2 ==> boolean"))

(defmethod binary> ((obj1 (eql :+inf.0)) (obj2 (eql :-inf.0))) 'T)
(defmethod binary> ((obj1 (eql :+inf.0)) (obj2 (eql :+inf.0))) NIL)
(defmethod binary> ((obj1 (eql :-inf.0)) (obj2 (eql :-inf.0))) NIL)
(defmethod binary> ((obj1 number)        (obj2 (eql :-inf.0))) 'T)
(defmethod binary> ((obj1 (eql :-inf.0)) (obj2 number))        NIL)
(defmethod binary> ((obj1 number)        (obj2 number))        (> obj1 obj2))
(defmethod binary> ((obj1 (eql :-inf.0)) (obj2 (eql :+inf.0))) NIL)
(defmethod binary> ((obj1 number)        (obj2 (eql :+inf.0))) NIL)

;; LEXPR版
(defmethod g> (obj &rest objs)
  (loop :for prev := obj :then item
        :for item :in objs
         :always (binary> prev item)))

という感じで、割とごちゃごちゃな定義です。binary>という名前は、S式Dylanから拝借しましたが、面倒なので他の述語を作る気にはなれませんでした(;´Д`)

使い方

(defun max-number (lst)
  (flet ((pick-greater (a b)
           (if (binary> a b) a b)))
    (reduce #'pick-greater lst :initial-value :-inf.0)))

(max-number (list  1 :-inf.0 most-negative-long-float))
;=> 1

という風に、使えるんだと思います。

関数版

Dylanは、=等は総称関数で、文字列との比較にも使えますが、数値に限定するなら、関数でも良いかなと思って試してみました。

(deftype number* ()
  `(or number (member :-inf.0 :+inf.0)))

(defun bin> (x y)
  (declare (number* x y))
  (cond ((eql x y) NIL)
        ((eql :-inf.0 x) NIL)
        ((eql :-inf.0 y) 'T)
        ('T (> x y))))

(bin> most-negative-fixnum :-inf.0)
;=> t

という感じで、あっさり書けてしまいました…。

総称関数の場合、定義を生成してくれるマクロを書くと良いのかもしれません。