clojureを分解する(5)


昔から何でも分解してみたくなる性分の私は、今咲き誇る 桜の花を、1片、2片...
と、何とも風情のない事をしてしまって、ちょっと反省してます。

でも、こんな事は許してもらえますよね。

[sakae@fb ~]$ gosh-rl
gosh> (define (foo n)
..... (* n n))
foo
gosh> (disasm foo)
main_code (name=foo, code=0x81af640, size=4, const=0, stack=1):
args: #f
     0 LREF0-PUSH               ; n
     1 LREF0                    ; n
     2 NUMMUL2                  ; (* n n)
     3 RET
#

とか

[sakae@fb ~]$ sbcl
* (defun foo(n)
        (+ n n))

FOO
* (disassemble 'foo)

; disassembly for FOO
; 598538B4:       8BD6             MOV EDX, ESI               ; no-arg-parsing entry point
;       B6:       8BFE             MOV EDI, ESI
;       B8:       E87BC87AA7       CALL #x1000138             ; GENERIC-+
;       BD:       7302             JNB L0
;       BF:       8BE3             MOV ESP, EBX
;       C1: L0:   8B75F4           MOV ESI, [EBP-12]
;       C4:       8D65F8           LEA ESP, [EBP-8]
;       C7:       F8               CLC
;       C8:       8B6DFC           MOV EBP, [EBP-4]
;       CB:       C20400           RET 4
;       CE:       CC0A             BREAK 10                   ; error trap
;       D0:       02               BYTE #X02
;       D1:       18               BYTE #X18                  ; INVALID-ARG-COUNT-ERROR
;       D2:       4D               BYTE #X4D                  ; ECX
NIL


さて、clojureのapiを見ていたら、面白いものを発見

user=> (class 123)
java.lang.Integer
user=> (class 1.23)
java.lang.Double
user=> (class (/ 1 3))
clojure.lang.Ratio
user=> (class 11111111111111111111111111111111111111111)
java.math.BigInteger
user=> (class false)
java.lang.Boolean
user=> (class nil)
nil
user=> (class ())
clojure.lang.PersistentList$EmptyList
user=> (class '(1 2 3))
clojure.lang.PersistentList
user=> (class :a)
clojure.lang.Keyword
user=> (class [])
clojure.lang.PersistentVector
user=> (class {})
clojure.lang.PersistentHashMap
user=> (class #{})
clojure.lang.PersistentHashSet
user=> (class (def n 3))
clojure.lang.Var
user=> (class (fn [] nil))
user$eval__40$fn__42
user=> (class +)
clojure.core$_PLUS___2959
user=> (class (range 1 3))
clojure.lang.Range
user=> (class (defstruct foo :a :b))
clojure.lang.Var
user=> (class foo)
clojure.lang.PersistentStructMap$Def
user=> (class (struct foo "aa" "BB"))
clojure.lang.PersistentStructMap

きりがないので、これぐらいにしますが、これって、rubyのあれと一緒ですね。

[sakae@fb ~]$ irb
irb(main):001:0> 3.class
=> Fixnum
irb(main):002:0> 33333333333333333333333333333.class
=> Bignum
irb(main):003:0> [1,2,3].class
=> Array

clojureの方は、Javaで賄えるのは、Javaに丸投げ(継承って言うでしたっけ?)
してて、楽そうな感じがします。その点、Rubyって、ちょっと可哀想。
rubyには、riなんて言う素敵な機能がついていますが、clojureではどうかな?

user=> (doc +)
-------------------------
clojure.core/+
([] [x] [x y] [x y & more])
  Returns the sum of nums. (+) returns 0.
nil
user=> (doc cons)
-------------------------
clojure.core/cons
([x seq])
  Returns a new seq where x is the first element and seq is
    the rest.
nil

これは、emacs-lispから輸入したものですね。
今、ふとapiを見ていたら、inspectorなるものが目に入ってきました。
物は試しとばかり

user=> (use 'clojure.inspector)
nil
user=> (inspect 1)
#<JFrame javax.swing.JFrame[frame0,0,0,400x400,layout=java.awt.BorderLayout,title=Clojure Inspector,resizable,normal,defaultCloseOperation=HIDE_ON_CLOSE,rootPane=javax.swing.JRootPane[,4,30,392x366,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]>

窓が出てきましたよ。きっとこれは、JavaBeansあたりと結合するための、予行
演習に違いない。でも、(def x '(1 2 (3 4 5) 6 (7 8 (9)))) しておいて
(inspect-tree x) すると、笑える。どうせなら、和田先生に対抗して、
cellの箱をきちんと書いて欲しいぞと。

それじゃ、またまた面白いものがあったので、、、

user=> (doc cycle)
-------------------------
clojure.core/cycle
([coll])
  Returns a lazy (infinite!) seq of repetitions of the items in
  coll.
nil
user=> (def a (cycle '(1 2 3)))
#'user/a
user=> a
(1 2 3 1 2 3 1 2 3 .......... 
java.lang.OutOfMemoryError: Java heap space
user=>

こんなの初めてみましたよ。ちなみに、cycleは、どんな定義かと言うと

(defn cycle
  "Returns a lazy (infinite!) seq of repetitions of the items in
  coll."
  [coll]
    (when (seq coll)
      (let [rep (fn thisfn [xs]
                    (if xs
                      (lazy-cons (first xs) (thisfn (rest xs)))
                      (recur (seq coll))))]
        (rep (seq coll)))))

ちなみに、肝は

(defmacro lazy-cons
  "Expands to code which produces a seq object whose first is
  first-expr and whose rest is rest-expr, neither of which is
  evaluated until first/rest is called. Each expr will be evaluated at most
  once per step in the sequence, e.g. calling first/rest repeatedly on the
  same node of the seq evaluates first/rest-expr once - the values they yield are
  cached."
 [first-expr & rest-expr]
 (list 'new 'clojure.lang.LazyCons (list `fn (list [] first-expr) (list* [(gensym)] rest-expr))))

のようです。更に、遡って、clojure.lang.LazyConsを見ると

package clojure.lang;

final public class LazyCons extends ASeq{
final private static ISeq sentinel = new Cons(null, null);
IFn f;
Object _first;
ISeq _rest;

public LazyCons(IFn f){
        this.f = f;
        this._first = sentinel;
        this._rest = sentinel;
}
 :

のように定義されてました。ちなみに、Javaの事を何も知らない私は、
浅煎り珈琲Java アプリケーション入門のお世話になっております。