新しいフォルダ (2)

2015-09-10

Clojure完全にマスターした話

TL;DR

  • LeiningenインスコしてIntelliJ IDEACursiveプラグイン入れるとLeiningenで生成したプロジェクト普通に読み込めるし括弧辺りの問題もだいぶ快適になるよ。
  • テスト環境何もしなくてもとりあえず整ってるっぽい
  • 完全にマスターとはHelloWorldやったり普通のFizzBuzzやったり出来た、を指す隠語

経緯

最初の一歩

Clojureのサイトからzip落としてきて展開、中に clojure-1.7.0.jar が入ってるので

java -cp clojure-1.7.0.jar clojure.main

するとREPLが起動していろいろ試せる

REPLがしょぼい

矢印キーが効かなかったり履歴機能が無かったりとにかく何を書くにもだるいなって思ってたらrlwrapというのがあるよと教えてもらいました。

よく分かんないですけどうまいことラップしていい感じにしてくれるとのこと。

rlwrap java -cp clojure-1.7.0.jar clojure.main

と言う感じでREPL起動すると履歴がついたり矢印キー使えたりしていい感じになりました。

rlwrapのインスコ方法は調べてください。だいたいパッケージマネージャで入ると思います。

Leiningenのインスコ

Javaで言う所のMavenScalaで言う所のactivator(sbt)みたいなものらしいです。ライブラリの依存関係やってくれたり、プロジェクトの雛形作ってくれたりコンパイルしたりしてくれる、Clojure界ではみんな当然使っている奴という感じっぽいです。

インスコは適当なディレクトリ

wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein

して、DLしたファイルにパス通せば終わりです

Cursiveインスコ

IntelliJ IDEAClojureプラグインです。

勝手に括弧の対応取ってくれたり対応する括弧に色んな色付けてくれたり補完出してくれたりLeiningenと連携してくれたりしてClojureコーディングライフを強力にサポートしてくれるとのこと。

インスコno title にある通り、自分のIntelliJ IDEAのバージョンに合ったplugin.xmlプラグインリポジトリとして登録し、いつもの様にプラグイン検索してインストーすればOKです。

で、こちらが様子です。

https://i.gyazo.com/467d36e14326c7179f1b2aeb6cf3193f.png

テスト

なんか最初からテストやる環境も整ってるらしいです。いろいろ言うより見たほうが早いのでこちらどうぞ。

D

動画みたいな感じでテストが書けます。Clojure REPL(ローカルサーバ)を起動しておいて、カーソル位置にあるコード(テスト)をREPLサーバに送ってちくいち実行しています。

REPLへのコードの送るのは、コードの適当な位置を右クリックしてREPL→Run 'hoge' in REPLを選ぶか、「Enter action or option name」のダイアログだして「run repl」とかで検索すると出てくる「Run test under caret in REPL」を実行するかすると出来ます。

関数書く→テスト書く→カーソル位置のテストをREPLに送る→失敗する→関数実装する→カーソル位置のテストをREPLに送る→イカ繰り返し

なんかかっこいいですね(括弧だけに)。

FizzBuzz

さっきの画像でチラ見えしてますが、出来たコードがこちらです。

(ns cljtest.core)

(defn peek' [x]
  (do (println x) x))

(defn gen [n]
  (range 1 (+ n 1)))

(defn fizzbuzz [x]
  (if (= (mod x 15) 0)
    "FizzBuzz"
    (if (= (mod x 3) 0)
      "Fizz"
      (if (= (mod x 5) 0)
        "Buzz"
        x))))

(defn doFizzBuzz [n]
  (map (comp peek' fizzbuzz) (gen n)))

との事です。

追記

コメント欄であやぴーさんに指摘もらいましたので修正(もらった指摘はコメント欄見てください)。

(ns cljtest.core)

(defn peek' [x]
  (do (println x) x))

(defn gen [n]
  (range 1 (inc n)))

(defn fizzbuzz [x]
  (if (zero? (mod x 15))
    "FizzBuzz"
    (if (zero? (mod x 3))
      "Fizz"
      (if (zero? (mod x 5))
        "Buzz"
        x))))

(defn do-fizzBuzz [n]
  (doseq [x (gen n)]
    (peek' (fizzbuzz x))))

doseq、よく分からないんですけどリスト内包表記みたいなのとなんかしらの関数引数にとって、関数を繰り返し実行するマクロ?っぽいです。副作用のある関数使う時に使うやつとのこと。

更に追記
(ns cljtest.core)

(defn gen [n]
  (range 1 (inc n)))

(defn fizzbuzz [x]
  (if (zero? (mod x 15))
    "FizzBuzz"
    (if (zero? (mod x 3))
      "Fizz"
      (if (zero? (mod x 5))
        "Buzz"
        x))))

(defn do-fizzbuzz [n]
  (doseq [x (gen n)]
    (println (fizzbuzz x))))

peek'関数いらないとのことです

まとめ

Clojure完全にマスターした

参考

ayato0211ayato0211 2015/09/10 16:16 一応幾つか指摘しておきますが
map は LazySeq を返すため、副作用と同時に使うのはやめたほうがいいです。
(take 3 (map println (range 10)))
こうすると println で 0~9 が出力されてしまいます(期待値ではない)。

詳細
http://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects

なので例えばまぁやるとするとこうですかね。
(defn do-fizzbuzz [n]
(doseq [i (range n)]
(println (fizzbuzz i))))
あとはキャメルケースじゃなくて lisp-case を使いましょう。

あとは細かいところで (+ n 1) は (inc n) でいいですし、 0 との比較は zero? の方がスマートですってくらいでしょうか。

kamekoopakamekoopa 2015/09/10 18:05 あざます!
doseqっていうのは副作用が起きる時用のmapって認識でいいんですかね?

> lisp-case
そういうのあるんですね、なるほどです

ayato0211ayato0211 2015/09/10 18:30 んー。 副作用持ちのループ用の関数(正確にはマクロ)が doseq で強いて言うなら副作用が起きるとき用の for ですね。 for もマクロですけど、これらは loop, recur の上に構築されてるものです( loop, recur は単純に再帰するような実装をするときに使うものですが、低レベルなのであまり使わず、単純にループさせたいだけなら for など loop, recur の上に構築されたものを使います)。
map の場合は特定の関数を特定のコレクションの要素全てに適用したいときに使うものなので、 doseq や for とはそもそも使うシーンが異なると思います(他の言語にある map 関数とかと同じだと思いますが、 Clojure では Lazy な実装がされているので無限長のコレクションを扱うことが出来るのが違うところでしょうか(他の言語でも出来るかもしれない))。

ayato0211ayato0211 2015/09/10 18:34 あと追記見たんですけど doseq は body 部を do (スペシャルフォーム)で囲むので明示的に do で囲む必要無くなります。つまり peek' 関数は不要です。

kamekoopakamekoopa 2015/09/10 19:06 なるほどです!

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/kamekoopa/20150910/1441860552