メモ ~ Clojureチュートリアル

前置き

Clojureチュートリアルを読んだときのメモです。☆印の箇所は独り言です。

Clojure - Functional Programming for the JVM

Introduction

Functional Programming

Clojure Overview

Clojureのoperationは、function、macro、special formの3種類。

special form:

catchdefdo.finallyfnifletloop
monitor-entermonitor-exitnewquoterecurset!throwtryvar

マルチスレッド環境でのデータ共有のため、STM(Software Transactional Memory)と3種類の参照型を提供。

同期/非同期用途
Ref同期複数のデータを共有
Atom同期1つのデータを共有
Agent非同期1つのデータを共有

コードは3つのフェーズで処理される。

  • read-time …構文解析
  • compile-time …バイトコード化
  • run-time …JVM上で実行

マクロが実行されるのはcompile-time(☆read-timeの間違いか?)。

Getting Started

Leiningenを使おう。

Clojure Syntax

構文:

PurposeSugarFunction
comment ;text(comment text)
character literal (uses Java char type) \char \tab \newline \space \uunicode-hex-value (char ascii-code) (char \uunicode)
string (uses Java String objects) "text" (str char1 char2 ...)
keyword; an interned string; keywords with the same name refer to the same object; often used for map keys :name (keyword "name")
keyword resolved in the current namespace ::name
regular expression #"pattern"(re-pattern pattern)
treated as whitespace; sometimes used in collections to aid readability ,
list - a linked list '(items)(list items)
vector - similar to an array [items] (vector items)
set #{items}(hash-set items) (sorted-set items)
map {key-value-pairs}(hash-map key-value-pairs) (sorted-map key-value-pairs)
add metadata to a symbol or collection ^{key-value-pairs} object(with-meta object metadata-map)
get metadata map from a symbol or collection (meta object)
gather a variable number of arguments in a function parameter list & name
conventional name given to function parameters that aren't used _
construct a Java object; |note the period after the class name (class-name. args) (new class-name args)
call a Java method (. class-or-instance method-name args) (.method-name class-or-instance args)
call several Java methods, threading the result from each into the next as its first argument; each method can have additional arguments specified inside the parens; note the double period (.. class-or-object (method1 args) (method2 args) ...)
create an anonymous function #(single-expression)(fn [arg-names] expressions)
dereference a Ref, Atom or Agent @ref (deref ref)
get Var object instead of the value of a symbol (var-quote) #'name (var name)
syntax quote (used in macros) `
unquote (used in macros) ~value (unquote value)
unquote splicing (used in macros) ~@value
auto-gensym (used in macros to generate a unique symbol name) prefix# (gensym prefix?)

分数をサポート。

Symbolオブジェクトは、何か(☆関数とか文字列とか)の名前として使う。Symbolは、その「何か」に評価される。Symbolオブジェクトそのものを得るにはクオートする必要がある。☆実際は、Symbolと「何か」の間にVarオブジェクトが入る。

コロン(:)で始まる名前はKeyword。☆Symbolの一種ではない。

REPL

user=> (def n 2)
#'user/n

Varオブジェクトを定義。userが名前空間で、nが名前。#'は、Varオブジェクトの評価結果(ここでは2)ではなくオブジェクトそのものを得るための構文。

(doc name)              ;nameのヘルプを見る。
(find-doc "text")       ;ヘルプ内の"text"を検索。
(source name)           ;nameのソースコードを見る。
(load-file "path")      ;ファイル"path"をロード。

☆doc、find-doc、sourceは、clojure.replで定義(clojure.coreじゃない)。

ソースファイルの拡張子は、clj。

Vars

変数のスコープは4種類。

  • グローバル
  • スレッドローカル
  • 関数内
  • フォーム内
(def ^:dynamic v 1)     ;グローバル変数。:dynamicは、メタデータ。
(let [v 1] ...)         ;フォーム内のローカル変数。
(binding [v 1] ...)     ;スレッドローカル。

letフォーム内で呼ばれる関数の中でvを参照した場合、それはletローカルのvではなく、グローバルのvのこと。☆let内でクロージャを定義する場合は別。

bindingは、グローバルなvを(カレントスレッド内に限り)一時的に上書きする。bindingフォームから抜けると元の値に戻る。この挙動のためには、:dynamicが必須(Clojure 1.3以降)。

☆Varについては、別記事「ClojureのVar」も参照。

Collections

immutableで、heterogeneousで、persistent。

persistentとは、あるCollectionを分割したり結合したりして別のCollectionを作ったとき、元のCollectionが不変ということ。

  • count
  • conj
  • reverse
  • map
  • apply
  • first
  • second
  • last
  • nth
  • next
  • butlast
  • drop-last
  • filter
  • nthnext
  • every?
  • not-every?
  • some
  • not-any?

Lists

(list "a" "b" "c")
(quote ("a" "b" "c"))
'("a" "b" "c")
  • remove
  • into
  • peek
  • pop
  • cons

Vector

(vector "a" "b" "c")
["a" "b" "c"]
  • get
  • assoc
  • subvec

Set

(hash-set "a" "b" "c")       ;not sorted.
#{"a" "b" "c"}               ;not sorted.
(sorted-set "a" "b" "c")
  • contains?
  • disj

Setを関数として使うことができる。

(def abc #{"a" "b" "c"})
(abc "a")                    ;"a"
(abc "d")                    ;nil
  • difference
  • index
  • intersection
  • join
  • map-invert
  • project
  • rename
  • rename-keys
  • select
  • union

Map

(hash-map :red "a", :green "b", :purple "c")
{:red "a", :green "b", :purple "c"}

カンマは省略可。

Setと同様、Mapも関数として使える。

(def abc {:red "a"})
(abc :red)                   ;"a"
(:red abc)                   ;キーがKeywordの場合、この順序でもOK。
  • keys
  • vals
  • dissoc
  • name
  • select-keys
  • get-in
  • doseq
  • destructuring
  • ->
  • reduce
  • -?>
  • assoc-in
  • update-in

StructMaps

deprecated.

Defining Functions

(declare g)             ;定義前の関数を使いたいとき。

(defn f
  "description"
  [a1 a2 a3]            ;[a1 a2 & rest]でオプション。
  (g ...))

オーバーロードも可能。

(defn f
  ([] (...))
  ([a] (...))
  ([a1 a2] (...)))
無名関数
(fn [a1 a2] (...) (...) ...)      ;複数フォームOK。
#(...)                            ;単一フォームのみ。引数は、1つなら%、複数なら%1、%2、…。
  • defmulti
  • defmethod
  • complement
  • comp
  • partial
  • range
  • memoize
  • time

Java Interoperability

インポート
(import '(java.util Calendar Date))
(ns my.domain.project (:import (java.util Calendar date)))
定数
(. java.util.Calendar APRIL)
java.util.Calendar/APRIL
(. Calendar APRIL)                ;インポート済みの場合。
Calendar/APRIL
staticメソッド
(. Math pow 2 4)
(Math/pow 2 4)
new
(new Date 2013 5 25)
(Date. 2013 5 25)
インスタンスメソッド
(. date setYear 2012)
(.setyear date 2012)
  • .. ;メソッドのカスケード。☆引数付きでもOK?
  • .?.
  • doto
  • memfn
  • proxy

Threads

全ての関数は、RunnableとCallableをimplementsする。☆引数付きの場合は? ←ArityExceptionになる

Exception Handling

  • try
  • catch
  • finally
  • throw

Conditional Processing

(if condition then-expression else-expression)   ;elseはオプション。
(when condition expression)
(when-not condition expression)
  • if-let
  • when-let
  • condp
  • cond

Iteration

  • dotimes
  • while

List Comprehension

  • for
  • doseq
  • dorun
  • loop

Recursion

  • next
  • dec
  • recur
  • reduce
  • apply
  • trampoline

Predicates

偽はfalseとnil。真の代表値はtrue。

  • class?
  • coll?
  • decimal?
  • delay?
  • float?
  • fn?
  • instance?
  • integer?
  • isa?
  • keyword?
  • list?
  • macro?
  • map?
  • number?
  • seq?
  • set?
  • string?
  • vector?
  • ancestors
  • bases
  • class
  • ns-publics
  • parents
  • <
  • <=
  • =
  • not=
  • ==
  • >
  • >=
  • compare
  • distinct?
  • identical?
  • and
  • or
  • not
  • true?
  • false?
  • nil?
  • empty?
  • not-empty
  • every?
  • not-every?
  • some
  • not-any?
  • even?
  • neg?
  • odd?
  • pos?
  • zero?

Sequences

lazy-sequenceを返す関数:

  • cache-seq
  • concat
  • cycle
  • distinct
  • drop
  • drop-last
  • drop-while
  • filter
  • for
  • interleave
  • interpose
  • iterate
  • lazy-cat
  • lazy-seq
  • line-seq
  • map
  • partition
  • range
  • re-seq
  • remove
  • repeat
  • replicate
  • take
  • take-nth
  • take-while
  • tree-seq

sequenceがlazyだと、評価するまでside-effectも起きない。sequenceの中身よりside-effectが重要な場合は、dorunやdoseqで明示的に評価する。

Input/Output

  • flush
  • print
  • println
  • newline
  • pr
  • prn
  • str
  • printf
  • format
  • print-str
  • println-str
  • pr-str
  • prn-str
  • with-out-str
  • with-open
  • line-seq
  • slurp
  • spit
グローバル変数
*in*
*out*
*err*
*flush-on-newline*
*print-meta*

Destructuring

(defn sum [[n1 _ n3]] (+ n1 n3))
(defn sum2 [[n1 _ n3 & rest]] (+ n1 n3))
(defn sum3 [[n1 _ n3 :as all]] (+ n1 n3))

(sum [4 5 6 7 8])                   ;10
(sum2 [4 5 6 7 8])                  ;restは[7 8]
(sum3 [4 5 6 7 8])                  ;allは[4 5 6 7 8]

Mapなら、[{val1 :key1 val2 :key2}]などとして、実引数Mapの:key1や:key2の値を仮引数val1やval2にバインドできる。仮引数名がキー名と同じなら、[{:keys [key1 key2]}]と受けてもOK。

Namespaces

住民は、Var、Ref、Atom、Agent、Function、Macro、Namespace。

カレント名前空間は、グローバル変数*ns*に入っている。デフォルトはuser。

  • in-ns
  • ns
  • refer
  • require
  • alias
  • use
  • create-ns
  • intern
  • ns-intern
  • all-ns
  • namespace
  • ns-aliases
  • ns-imports
  • ns-map
  • ns-name
  • ns-publics
  • ns-refers
  • ns-unalias
  • ns-unmap
  • remove-ns

Some Fine Print

  • Symbolオブジェクトは、名前文字列と名前空間文字列(ns)を持つ
  • Varオブジェクトは、Symbolオブジェクト(sym)、Namespaceオブジェクト(ns)、Objectオブジェクト(root)を参照する
  • Namespceはオブジェクトは、住民Map(mappings)と、名前空間Map(namespaces)を持つ
  • 住民Mapが、当該名前空間内のSymbol-Var対応を管理

「internする」とは、名前空間内に、SymbolとVarの対応を追加すること。

Metadata

SymbolかCollectionにアタッチされる。等価性を左右しない。

(def card ^{:bent true} card)
(def card (with-meta card {:bent true}))

(meta (var card))
(meta #'card)
private
doc
test
tag
file
name
macro
arglist
  • warn-on-reflection*

Macros

read-timeに展開される。☆compile-timeじゃなかったの?

  • defmacro
  • macroexpand-1

`()の中は、評価されずに、そのまま展開される。ただし、~nameでアンクオート。~@colでコレクションを展開。

name#で、自動的に一意なSymbolが生成される。

Concurrency

futureマクロ:

  • 別スレッドで非同期実行(Cached Thread Poolを使用)
  • 結果を参照型で返す
  • 非同期処理が完了する前にdereferenceすると、そこでブロック
  • shutdown-agents
  • pmap
  • par
  • pdistinct
  • pfilter-dupes
  • pfilter-nils
  • pmax
  • pmin
  • preduce
  • psort
  • psummary
  • pvec

Reference Types

  • derefか@でdereferenceする
  • validatorで変更時に検証できる
  • watcherで変更を監視できる
種類目的生成変更
Var同期、スレッドローカル(def ^:dynamic name val)def, alter-var-root, set!
Ref同期、STMと共に複数データを排他制御(def name (ref val))ref-set, alter, commute
Atom同期、単一データ(def name (atom val))reset!, compare-and-set!, swap!
Agent非同期、単一データ(def name (agent val))send, send-off

Vars

(def ^:dynamic name val)
(binding [name val] ...)
(set! name val)

Refs

STM(Software Transactional Memory)で排他制御。dosyncフォームでトランザクション開始。例外なしにdosyncが抜けるときコミットされる(例外で抜けたらロールバック)。トランザクション内で値の不整合が起きたら、dosyncを再評価する。不特定回数のリトライを伴うので、side-effectを持った処理を行うべきでない。そんな処理は、Agentに頼んで(sendやsend-off)、コミットできたときのみ実施してもらうようにする。

(def name (ref val))
(dosync
  ...
  (ref-set name val2)
  ...
)
  • alter
  • commute

STM内でcommuteを使うと、コミット時に、update-functionが呼ばれる。☆update-functionって何?

(apply update-function last-committed-value-of-ref args)

Validation

(def name (ref val :validateor f))

Atoms

  • reset!
  • compare-and-set!
  • swap!

Agents

(def name (agent val))
  • sendで、アクション関数をAgentへ送る
  • アクションは独立したスレッド上でコールバックされ、その結果がAgentの新しい値になる
  • sendはFixed Thread Poolを使うが、send-offはCached Thread Poolを使う
  • 1つのAgentに対して、同時に2つ以上のアクションが動くことはない(シリアライズされる)
  • STM内でsendやsend-offした場合、アクションが動くのはSTMがコミット成功したときのみ
  • アクション内では、グローバル変数*agent*にAgentオブジェクトが入っている
  • await
  • await-for
  • agent-errors
  • clear-agent-errors
  • shutdown-agents

Compiling

☆プロジェクトのソースファイル構成例を挙げているが、あんまりオススメできない。

Automated Testing

Editors and IDEs

Desktop Applications

Web Applications

Databases

Libraries

Conclusion

References

Last modified:2013/05/25 15:25:04
Keyword(s):
References:[FP: 関数型プログラミング]
This page is frozen.