読者です 読者をやめる 読者になる 読者になる

Programmer's Note

コード読み書きの備忘録。

Clojureの標準入力とファイル読み込み(文字列カウント)

Clojure

ファイルのリードと標準入力の扱いのメモ。 「プログラミング言語C」の例題にもあるword count的なものを作ってみる。

ソース

leiningen環境にて

$ lein new app t_stdin

とやったあとにsrc/t_stdin/core.cljを編集。

(ns t-stdin.core
  (:require [clojure.java.io :as io]
            [clojure.string :as str])
  (:gen-class))

(defn count-words
  [line]
  (let [w (-> line
              (str/replace #"^\s+" "")
              (str/split #"[\s\t]+"))]
    (if (or (= line "") (= w [""]))
      0
      (count w))))

(defn -main
  [& args]
  (let [f (if-not (empty? args)
            (io/file (first args))
            *in*)
        dat (line-seq (io/reader f))]
    (println (str "lines: " (count dat)))
    (println (str "words: " (reduce + (map count-words dat))))))

実行結果

このソースコード自体を入力に与えてみる。

t_stdin $lein run < src/t_stdin/core.clj
lines: 32
words: 85

上記はプログラムに引数を与える形で、lein run src/t_stdin/core.cljと打っても同じ。

wcコマンドの結果で答え合わせ。

t_stdin $wc src/t_stdin/core.clj
      32      85     713 src/t_stdin/core.clj

メモ

Clojureのファイルのリードは、下記のようにすれば

(line-seq (clojure.java.io/reader (clojure.java.io/file "file-name")))

行に分割したシーケンスとして返してくれる。 "file-name"はファイル名の文字列。 (ファイルが見つからないときのエラー処理はまったく無視してるが・・・)

標準入力の場合は、

(line-seq (clojure.java.io/reader (clojure.java.io/file *in*)))

とするだけ。

とりあえず、今回は

  (let [f (if-not (empty? args)
           (io/file (first args))
           *in*)
        dat (line-seq (io/reader f))]

引数が与えられいればそれをファイル名として使う。 そうでない場合*in*を使う。

一行の中の文字数のカウントは関数count-wordsで行っている。

文字列のカウントは結局、空白で文字列を分割して得たシーケンスの要素の数を数える。

(count (str/split line #"[\s\t]+"))

的な感じでやるのだが、split関数は行の先頭に空白があった場合に、""が要素に追加されてしまう。

t-stdin.core=> (str/split "  aa bb" #"[\s\t]+")
["" "aa" "bb"]

ので、splitに与える前にreplaceで先頭の空白を除いている。

     (-> line
              (str/replace #"^\s+" "")
              (str/split #"[\s\t]+"))

一方で、空行の場合文字列が""で、上記の正規表現のフィルターにひっかからない。 また、先頭が空白だけの行は結果が[""]になるので、

    (if (or (= line "") (= w [""]))
      0
      (count w))))

で特別扱いしている。 もっとうまいやり方ありそうだけど、まあとりあえず。

Clojureの楽しい入門書

Clojure 読書

いやあClojure楽しす。 "Clojure for The Brave and True" (紙本)を購入して読んでいるのだが、この本はすばらしいね。

ノリが軽いが、内容はよく整理されていて、構成がしっかりしている。 あとから目次頼りにリファレンスとしても使える。著者の奥さんのイラストも魅力的だしな。 直近は、Macroの仕組み&書き方の章を読み終えたばかりだが、Lispのエレガントなところが実に良く理解できた。

自分が他人に勧めるとしたら、まずは「7つの言語7つの世界」でClojureを試してみて、面白いと思ったなら、次はこの本だな。 オンラインで無料で公開しているのが信じられないくらいだ。(Learn to Program the World's Most Bodacious Language with Clojure for the Brave and True

著者はClojureへの想いがよほど強いのか、どしどしeverybodyに使った欲しいからかな。

そして、この本を読んで良かったこと。 なんといっても、いまいちClojure (Lisp)のプログラミングをどう手を付けたらいいか分からなかったのが、この人のプログラムの組み方で要領がつかめたことだな。

「そうか、まずはデータ構造を考えればいいのか。特に難しく考えることはないんだな。」と。

プログラミングの基本は、結局、 UNIX哲学の”パイク: Cプログラミングに関する覚え書き”の

ルール5: データはすべてを決定づける。もし、正しいデータ構造を選び、ものごとをうまく構成すれば、アルゴリズムはほとんどいつも自明のものになるだろう。プログラミングの中心は、アルゴリズムではなくデータ構造にある。

に尽きるかもしれん。

この本の中でも、以下の格言を引用している。(参照:"Chapter 3: Do Things")

It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures. —Alan Perlis

Alan Perlisはその時代の何を指して言っているの分からんが、Clojureの文脈で語るなら "10 functions on 10 data structures"の方はOOPを指してるだろうなと。

巷のブログなどを読むと、LISPerの人はよくOOPのだめな点と対比してLispの素晴らしさを強調すること多い。 そして、それが納得できて実に面白い。 つまるところOOPのいちいち無駄なところがどうしても我慢ならん人はClojureが楽しいと思う。

ClojureとSwingでボール落ちるアニメーション

Clojure GUI Swing

ClojureからSwingを使って簡単な描画アニメーションを試してみるテスト。 ClojureJavaの世界をかなり透過的にたたけるが、書き方のパターンさえ覚えてしまえば、コード量が全然少なくなるので、はっきり言ってJavaで書くよりラクだ(笑)。

とりあえず、上から下に小さなボールが落ちていくアニメーション作ってみた。

f:id:hifistar:20160413235942p:plain

ソースコード

(ns test-paint.core
  (:import [javax.swing JFrame JPanel Timer]
           [java.awt.event ActionListener])
  (:gen-class))

(def py (atom 0))

(defn next-y
  [y h]
  (if (<= y (- h 10)) (inc y) y))

(def panel
  (proxy [JPanel ActionListener] []
    (paintComponent [g]
      (let [w (proxy-super getWidth)
            h (proxy-super getHeight)
            x (/ w 2)
            y (next-y @py h)]
        (doto g
          (.setColor java.awt.Color/BLACK)
          (.fillOval x y 10 10))
        (reset! py y)))
    (actionPerformed [e]
      (.repaint this))))

(defn -main
  [& args]
  (let [frame (JFrame. "Test Paint")
        timer (Timer. 10 panel)]
    (doto frame
      (.add panel)
      (.pack)
      (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
      (.setSize 400 400)
      (.setVisible true))
    (.start timer)))

Clojureでjavax.swing.JFrameを使ってみる

Clojure GUI Swing

Clojureからjavax.swingを使い、GUIプログラミングをかじり始めつつ、Javaメソッド呼び出しのやり方も練習もする。

Javaメソッドの呼び出し方

例えば、文字列Helloの長さを返す。
repl=> (.length "Hello")
5

これはJava"Hello".length()を呼びだしたと同じ。

staticメソッドを呼び出す
repl=> (java.lang.Math/abs -1)
1

これはそのままで分かりやすい。 デフォルトの状態でjava.langはnamespaceにはimport済みのようなので、下記も同様の動きをする。

repl=> (Math/abs -1)
1

JFrameを使ってウィンドウを出す

いつものとおり、leinで新しいプロジェクトを作って試す。

swing $lein new app hello_frame

単純に400x400の大きさのフレームを出してみる。

以下ソース。

(ns hello-frame.core
  (import (javax.swing JFrame))
  (:gen-class))

(def frame (new JFrame))

(defn -main
  [& args]
  (.setSize frame 400 400)
  (.setTitle frame "Hello,JFrame")
  (.setVisible frame true))

JFrameのインスタンス(new Frame)で生成し、以降(.setSize ...)などでメソッド呼び出しをしている。

(.setSize frame 400 400)とかは、実はマクロを使っていて、内部的にはドットスペシャルフォームに変換される。(.を使った特殊形式)

macroexpand-1を使って展開してみると、以下のようになる。

repl=> (macroexpand-1 '(.setSize frame 400 400))
(. frame setSize 400 400)

逆に言うと、.特殊フォームを使っても書ける訳で、下記も同じ動作をする。

(ns hello-frame.core
  (import (javax.swing JFrame))
  (:gen-class))

(def frame (new JFrame))

(defn -main
  [& args]
  (. frame setSize 400 400)
  (. frame setTitle "Hello,JFrame")
  (. frame setVisible true))

こっちの方が分かりやすいよな。と個人的に思ったが・・・。

さて、Javaのクラスのインスタンスを作るのは (new JFrame)でできるが、こちらは(JFrame.)という表記でも可。

引数も与えることができて、 (JFrame. "Heeloooo")とするとタイトルの文字列を与えてインスタンス生成する。

同じオブジェクトのメソッドを連続して呼び出したい時に、便利なマクロdotoが用意されている。

今回の場合では、frameに対して、setSizesetTitlesetVisibleを呼びたい。以下のように書ける。

  (doto frame
    (.setSize 400 400)
    (.setTitle "Hello,JFrame")
    (.setVisible true)))

うむ、便利。

とりあえず、JFrameをnewするときにタイトルも渡すようにすると、最終ソースは下記のようになる。

(ns hello-frame.core3
  (import (javax.swing JFrame))
  (:gen-class))

(def frame (JFrame. "Heeellooo!!"))

(defn -main
  [& args]
  (doto frame
    (.setSize 400 400)
    (.setVisible true)))

参考文献

Working with the JVM | Clojure for the Brave and True

fireplace使用メモ

Clojure vim

vimClojureの開発が便利になるプラグイン vim-fireplace を導入してみた。

インストール

参照したサイト: http://blog.ieknir.com/blog/beginning-clojure-with-vim/

自分はNeoBundleを使っているので、以下の2つのプラグインを.vimrcに追加しただけ。

 call neobundle#begin(expand('~/.vim/bundle/'))
...
NeoBundle 'tpope/vim-fireplace'
NeoBundle 'tpope/vim-classpath'
...
call neobundle#end()

fireplaceは裏でREPLと通信して、vimでエディットしながら、その場で式の評価などができるので、かなり便利だ。 とりあえずメモ。

vimでコマンド実行するまでの手順

先にREPL環境を立ち上げないとvimでのコマンド実行できない。

(1) leiningen を使ってプロジェクトを作成

$ lein new test

(2) プロジェクト直下に入りlein replを実行する

$ cd test $ lein repl

(3) vimを立ち上げプロジェクト内のファイルを編集する

$ vim src/test/core.clj

関数の評価

vimコマンドラインでREPLの機能を呼び出せる。これだけでかなり便利だ。

下記例は標準関数を使用しているが、自分が作成した関数でも可能だ。 その場合、一旦:wなどでファイル保存してから、関数を使用する必要がある。

vimのコマンド入力で:Evalを使う。

:Eval (println "hello")

結果:

hello
nil

関数のdocを見る

:Doc println

結果:

clojure.core/println
([& more])
  Same as print followed by (newline)

ソースを見る

:Source println

結果:

(defn println
  "Same as print followed by (newline)"
  {:added "1.0"
   :static true}
  [& more]
    (binding [*print-readably* nil]
      (apply prn more)))

キーバインド

cpp

カーソルの行を評価する

例:

(println "hello")

のときに、括弧の中のどこにカーソルを置いてもよく、cppと打てば、helloと実行結果が最下行のバッファに表示される。

cppは複数行にまたがる関数の実行にも使える。→cp)の項目を参照。

cp)

カーソルの下の(と対になる)までの関数を実行する。 例えば

  (loop [x 1]
     (if (> x 11)
       [x]
       (recur (+ x 1))))

があったときに、(loop ...のはじめの(にカーソルを合わせて、cp)と打つと、結果として[12]が最下行のバッファに表示される。

上記は(loop ...)の最初の(と最後の)にカーソルを合わせて、cppと打っても、同じ結果になる。(こちらの方が簡単か)

cpr

カレントnamespaceに対して(require :reload)を実行する

例:

(defn boo []
   (take 6 (iterate inc 0)))
(boo)

としたエディットした直後だと、(boo)にカーソルを置いてcppを打っても、シンボルbooが見つからないと怒られてしまう。 この場合に、 cprを打ってからcppと打つとうまく行く。

K

カーソルの下の関数の(doc ..)の結果を表示する

[d

カーソルの下の関数のソースを表示する

[C-d

カーソルの下の関数のソースに飛ぶ

cqp

REPLのプロンプトが出てくる。

Clojureのプログラミング本とか

Clojure

先週あたり、ひととおり「プログラミングClojure」を読み終えた。

この本は正直、分かりやすくない。 用語が統一されてない部分があったり、用語の説明がいまいち足りなく、 ネットで検索せんといかんかったり。

サンプルコードも面白い訳でもなく、かといってリファレンスとしても使えないし、 とまあ結構さんざんな言いようだが・・・原著はRich Hickeyさんが ブックリスト に追加しているくらいだから、結構評価されている本なんでしょうね。 (自分には合わんかったというだけかな)

さて、もうちょっとLispのプログラミングを知ろうと思って、 SICP を読み始めた。英語のペーパーバック。 (以前、しょっぱなの数ページだけ読んで止めていた)

一方で、たまたまオンラインでフリーで読める本を見つけた。 Learn to Program the World's Most Bodacious Language with Clojure for the Brave and Trueは、なかなか面白そうだ。

おちゃらけた感じで書かれてるが、こーいうのは好きだな。 (Joel on Softwareもそんな感じだしな。) でもしょっぱなからClojureのクールなところを感じさせる部分を紹介していたり、 なかなかセンスよさげ。これもあとで紙本を買って読みたいところだ。

Clojureメモ: 再帰を使うとか使わないとか

Clojure

再帰呼び出し。 練習がてら下記のような関数を作ってみる。

(myfunc 5 [])
=> [1 2 3 4 5]

考え方としては、

(myfunc 5, [])
  -> (myfunc 4, [5])
     -> (myfunc 3, [4 5])
        -> (myfunc 2, [3 4 5])
           ...
           -> (myfunc 0, [1 2 3 4 5])
                => 最後に[1 2 3 4 5]を返す

実装すると以下のようになる。

(defn myfunc [n coll]
  (if (<= n 0)
    coll
    (myfunc (dec n) (cons n coll))))
t-recur-func.core=> (myfunc 5 [])
(1 2 3 4 5)

これは、引数にコレクション(ベクター)を渡してしまうが、 コレクションを渡さない以下のやり方も考えられる。

(defn myfunc2 [n]
  (if (<= n 1)
    [n]
    (conj (myfunc2 (dec n)) n)))
t-recur-func.core=> (myfunc2 5)
[1 2 3 4 5]

こっちは、呼び出した関数の返り値を、どんどんくっつけていくパターン。

どちらかというと、個人的にはこちらの方が実装のイメージがしやすかった。

ただし、こちらの方が末尾再帰ではないので、(recur ..)に置き換えることができず最適化できない。 (再帰呼び出しの回数が多いとスタックオーバーフローを起こす)

とはいえ、このパターンでフィボナッチ数列を求めることもできる。

(defn fib [a b n]
    (if (<= n 1)
      [a] 
      (cons a (fib b (+ a b) (dec n)))))
t-recur-func.core=> (fib 0 1 7)
(0 1 1 2 3 5 8)

てな感じだが、 最後の一行がごちゃごちゃしてて、あまり分かりやすくない…。

フィボナッチ数列の求め方は、Clojure的にはやはり、再帰を使わないやり方がエレガント。 以下のような感じ。

(defn fib2 [n]
  (letfn [(_fb [[a b]]
            [b (+ a b)])]
    (take n
        (map first (iterate _fb [0 1])))))
t-recur-func.core=> (fib2 8)
(0 1 1 2 3 5 8 13)

これは「プログラミングClojure」に載っていた解法。 (ポイントを思い出して書いたので、そのままではない)

一旦、

([0 1] [1 1] [1 2] [2 3] [3 5]...)

のシーケンスを求めといて、 それぞれの最初の要素を取り出してしまう。という考え方。

まあ、mapとかiterateとかの仕様を知らないと読めないのだが。 でも、Clojureレバレッジを最大に生かすこの解法には膝を打ったな。