Hatena::ブログ(Diary)

すにぺっと

2011-04-01

clojureプログラミング入門-4 フロー制御、メタデータ

| 21:31 | clojureプログラミング入門-4 フロー制御、メタデータを含むブックマーク

ではフロー制御。

if

ifは最初の引数を評価し、真であれば2つ目の引数を評価した結果を返す。

user=> (defn is-small? [num] (if (< num 100) "yes"))
#'user/is-small?
user=> (is-small? 10)
"yes"
user=> (is-small? 200)
nil

偽であればnil

第3引数を定義すれば、それが偽の際の結果になる。


do

ifには分岐先に1つの式しか書けないが、

doを使えば複数の式を書ける。

user=> (defn is-small? [num] (if (< num 100) "yes" (do (println "num=" num) "no")))
#'user/is-small?
user=> (is-small? 10)
"yes"
user=> (is-small? 200)
num= 200
"no"

loopとrecur

loopはletのように値を束縛し、フォームを評価する。

違いはrecurの対象になる再帰ポイントをつくるということ。

user=> (loop [result [] x 5]  ;1
      (if (zero? x) ;2
      	  result 
	  (recur (conj result x) (dec x)))) ;3
[5 4 3 2 1]

1.resultに空のベクタ、xに5を束縛する

2.3 xは0でないので、元のresultベクタにxの値が足される

xをデクリメントして、recurによって関数の先頭にもどる

xがデクリメントされて0になったらresultを返して終了。

ただ、シーケンスライブラリを使うとloop-recurを使わなくてもいけるらしい。

メタデータ

Javaコードを置き換えてる章はとばして、メタデータ

clojureではメタデータは、オブジェクトの論理的な値とは直行するデータ、とのこと。

with-metaを使えばメタデータを付加できる。

user=> (def person {:name "taro" :age 30})
#'user/person
user=> (def serialize-person (with-meta person {:serializable true})) ;1
#'user/serialize-person
user=> (= person serialize-person) 
true
user=> (meta person) ;2
nil
user=> (meta serialize-person)
{:serializable true}

1.with-metaでメタデータを付加

2.metaを使え、メタデータを取り出せる

リーダメタデータ

varは型情報やソース情報を含んだマップをメタデータとしてもっている。

user=> (meta #'conj)
{:ns #<Namespace clojure.core>, 
:name conj, :file "clojure/core.clj", :line 71, 
:arglists ([coll x] [coll x & xs]), 
:doc "conj[oin]. Returns a new collection with the xs\n   
 'added'. (conj nil item) returns (item).  The 'addition' may\n   
 happen at different 'places' depending on the concrete type.", :added "1.0"}

引数戻り値がともに文字列であることを:tagキーを使って記録してみる。

user=> (defn #^{:tag String} shout [#^{:tag String} s] (.toUpperCase s))
#'user/shout
user=> (meta #'shout)
{:ns #<Namespace user>, 
:name shout, :file "NO_SOURCE_PATH", :line 29, 
:arglists ([s]), :tag java.lang.String}

:tagメタデータは#^Classnameという省略記法も使える。

#^{:tag Classname} === #^Classname

user=> (defn #^String shout [#^String s] (.toUpperCase s))
;(defn ^String shout [^String s] (.toUpperCase s))でもOK
#'user/shout


user=> (shout "hello")
"HELLO"

メタデータリーダマクロコンパイラのためにメタデータを付加する。

with-metaはデータに対してメタデータを付加する。

varや引数メタデータを付加するときはメタデータリーダマクロ

データにメタデータを付加するときはwith-metaを。

ちなみに、#^metadataという記述は、clojure1.2からは

^metadataと書けるようになっているらしい。



プログラミングClojure
プログラミングClojure
posted with amazlet at 11.03.31
Stuart Halloway
オーム社
売り上げランキング: 174593

clojureプログラミング入門-3 無名関数、varと束縛

| 09:03 | clojureプログラミング入門-3 無名関数、varと束縛を含むブックマーク

今日は無名関数から。


無名関数

fnを使うと、無名関数を定義できる。

これを使うと関数が簡潔にかけるので、

filter関数とかでよく使われると。

3文字より大きい単語をカウントする関数

(use '[clojure.contrib.str-utils :only (re-split)])

;無名関数使わず
(defn indexible-word? [word] (> count word) 2))
(filter indexible-word? (re-split #"\W+" "a bb ccc dddd eeeee"))

;無名関数1
(filter (fn [w] (> count w) 2))  (re-split #"\W+" "a bb ccc dddd eeeee"))

;無名関数2
(filter #( [w] (> count %) 2))  (re-split #"\W+" "a bb ccc dddd eeeee"))

#(body)

のように、fnのさらに省略した形式も使える。

引数は%1,%2と参照できる。

最初の引数は%で参照可能。


注意点として、無名関数

それを使うことで読みやすいと判断したときだけ使うこと。



var 束縛 名前空間

オブジェクトをdef,defnで定義すると、オブジェクトclojureのvarに

格納される。

user=> (def foo 10)
#'user/foo
user=> foo ;値を評価
10
user=> (var foo) ;var自体を評価
#'user/foo
user=> #'foo ;var自体を評価
#'user/foo

var自体の参照もできる。

varは別名もったりメタデータをもったりスレッドごとに

状態をもったりできる。


束縛

letを使うとレキシカルな束縛が可能。

user=> (defn mycount[x y z] (let [tmpresult (+ x y z)] (* tmpresult 10)))
#'user/mycount
user=> (mycount 1 2 3)
60

コレクションの一部を束縛することもできる。(分配束縛)

(defn sample [{fname :first-name}]
  (println fname))

(sample {:age 10 :first-name "taro"})

マップを分解したり、コレクションの頭をとばしたり。

user=> (let [ [x y] [1 2 3] ] [x y])
[1 2]
user=> (let [ [ _ _ z] [1 2 3] ] [z])
[3]

名前空間

デフォルトではuserという名前空間に属する。

(resolve シンボル)

で、それがどこの名前空間に属するかわかる。

(in-ns 'myns)

とすれば新しい名前空間を定義可能。

デフォルトではjava.langi 以外を使うにはimportする。

(import "(package Class+))

他の名前空間にあるclojureのvarを使う場合、

(use 'clojure.contrib.math)


必要なvarだけ限定もできる。

(use '[clojure.contrib.math :only (round)])


変更を反映させたい場合

(use :reload '[clojure.contrib.math :only (round)])


クラスのimportや名前空間のuseをするとき、

nsマクロを使用すると便利。

:import、:require、:useが使える。

(ns sample.app
    (:use example.util)
    (:import (java.io.FIle))

次はフロー制御から。


プログラミングClojure
プログラミングClojure
posted with amazlet at 11.03.31
Stuart Halloway
オーム社
売り上げランキング: 174593