トップ 差分 一覧 ソース 検索 ヘルプ RSS ログイン

Clojureの文法

[Clojure]

文法

  制御構文

if
if-elseを書きたい場合はこれがよい
(if true
  ;; true節
  (println "true")
  ;; false節
  (println "false"))
if-let
when
when-let
case
cond
switch文を書きたい場合はこれがよい
condp

  コレクション操作

Ruby
見慣れた形、データが先で関数で回す
# listの中身をトレース
[1, 2, 3].each do |n|
    puts n
end

# mapの中身をトレース
{ 1 => "a", 2 => "b", 3 => "c" }.each do |k, v|
    puts "#{k}, #{v}"
end
Clojure
Lispなのでデータが後で関数が先
# listの中身をトレース
(map #(println %) [1 2 3])
(map #(prn %) [1 2 3])

# mapの中身をトレース(doseqでやる、これはなんかいまいち…)
(doseq [[k v] {1 "a" 2 "b" 3 "c"}] (prn k v))

# mapの中身をトレース(reduce-kvでやる、こっちは関数名は良いがinit値(=0)を挟まなきゃいけないのがかっこ悪い)
(reduce-kv #(println %2 %3) 0 {1 "a" 2 "b" 3 "c"})
# Rubyだと…見慣れたブロック記法
[1, 2, 3].each_with_index do |n,idx|
    puts "#{idx}:#{n}"
end

# むう…
(doseq [[idx n] (map-indexed vector [1 2 3])] (println (str idx ":" n)))

  Javaとの連携・移植

Java
JavaでMD5をとってみる
import java.util.Arrays;
import java.util.StringJoiner;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class prog {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        byte[] bytes = md5.digest("password".getBytes());
        StringJoiner joiner = new StringJoiner("");
        joiner.setEmptyValue("");
        for (byte b : bytes) {
            joiner.add(String.format("%02x", b));
        }
        // Java8でもbyte[]にはStreamが使えない?
        // Arrays.stream(bytes).forEach(s -> joiner.add(String.format("%02x", s)));
        System.out.println(joiner.toString());
    }
}
Clojure
がんばってClojureに移植
(import java.util.Arrays)
(import java.util.StringJoiner)
(import java.security.MessageDigest)
(import java.security.NoSuchAlgorithmException)

; letでMessageDigestのインスタンスをmd5に束縛
(let [md5 (MessageDigest/getInstance "MD5") joiner (new StringJoiner "")]
  (. joiner setEmptyValue "")
  ; ここで map を使うと遅延評価されてうまくいかない
  (doseq [eb (. md5 digest(.getBytes "password"))] (. joiner add(format "%02x" eb)))
  (println (. joiner toString)))
Clojure - クラスインスタンスのメソッドを呼ぶ
(. obj method) の形式、引数が無い時は (.method obj) でもいけるっぽい
Clojure - クラスメソッド(staticメソッド)を呼ぶ
(Class/method) の形式

  ビルド

  • requireをちゃんと書いてやらないと、依存関係の解決ができてないように見える
  • gen-classでJavaのクラスを生成しているときは、gen-classより先にrequireを書くのがよい

  イディオム

アルゴリズム用

  木の操作

;; HTML用のベクタ型木構造を作成してみる

   (def html [:html [:title "Title"] [:body]])
=> #'user/html
   (require '[clojure.zip :as zip])
=> nil

;; rootの場所を取得

   (def root-loc (zip/vector-zip html))
=> #'user/root-loc

;; ノードの移動

   html
=> [:html [:title "Title"] [:body]]
   (zip/node (zip/down root-loc))
=> :html
   (-> root-loc zip/down zip/right zip/node)
=> [:title "Title"]
   (-> root-loc zip/down zip/right zip/right zip/node)
=> [:body]
   (-> root-loc zip/node)
=> [:html [:title "Title"] [:body]]
   (-> root-loc zip/children)
=> (:html [:title "Title"] [:body])

  • 木を巡回してk->vの値変更
    • ご丁寧に pre-walk, post-walk がある

https://clojure.github.io/clojure/clojure.walk-api.html

  子のあるVectorを順にみていく

;; HTMLデータ
(def html [:html [:title "Title"] [:body [:p "Body"]]])

;; 再帰
(defn recursive-print [src depth]
  (doseq [[idx item] (map-indexed vector src)]
    (do
      (clojure.pprint/pprint item)
      (if-not (vector? item)
        (println (str depth " = " item))
        (recursive-print item (+ 1 depth)))
      (- 1 depth))))

;; 結果
(recursive-print html 0)
:html
0 = :html
[:title "Title"]
:title
1 = :title
"Title"
1 = Title
[:body [:p "Body"]]
:body
1 = :body
[:p "Body"]
:p
2 = :p
"Body"
2 = Body
お名前: コメント: