新しいプログラミング言語を覚えるときの壁
構文の文法が違うというのはまあ教科書があるから頑張ればどうにかなるとして、それ以外で難しかったこと。
個人的に思い出せる壁を並べてみます。
関数を(変数に入れて)渡し、それを使うという考えがない
クロージャも関数ポインタもコールバックもわかりません。関数が関数を返すこともありうることに思い至れません。
実行コンテキストが認識できない
マルチスレッドが使えません。コールバックの非同期呼び出しとかさっぱりです。ここがわからないとコード記述全体がバッチ実行のようにしか見えません。
再帰の記述がわからない
ローカル変数と引数が呼び出し前と呼び出し先で違う(同じこともありうるが)ということに思い至れず混乱します。
現在の実行位置で実行されないコード記述というものがわからない
無名関数、無名クラス、クロージャ、遅延評価がこれに当たります。そこにあるからここを通れば即実行されるんだろう、ではなく、その時点では定義されているだけ、ということです。
他にも、
- ローカル変数がわからない
- 関数の実引数と仮引数の違いがわからない
- 参照渡しと値渡しの違いがわからない
- ポインタがわからない
- 構造体(と共用体)がわからない
といったことがありうるとは思いますが、こちらは私にとってはそうでもありませんでした。
あー、、、ポインタはちょっと難しかったかな。ハコと中身とハコにつけたタグの比喩でどうにかしたかなぁ…タグをつけなおすと当然中身は変わるわけで。
Clojure でテキストファイルを読んでみる。(遅延評価)
遅延評価タイプにしながらも途中で閉じることができるようにすると、こういう感じになるようです。
(use '[clojure.contrib.duck-streams :only (reader)]) (defn file-reader [target] (let [r (reader target)] {:close (fn [] (.close r)) :seq (letfn [(readline [] (try (let [line (.readLine r)] (if line (lazy-seq (cons line (readline))) (.close r))) (catch java.io.IOException _ (try (.close r) (catch Exception ignore nil)))))] (readline))})) ;-- 開いて閉じる (def readme (file-reader "readme.txt")) ((readme :close)) ;-- 開いて3行読んで閉じる (def readme (file-reader "readme.txt")) (take 3 (readme :seq)) ((readme :close))
゚▽゚)なんか面白くなってきた。
Clojure でテキストファイルを読んでみる。
ファイルI/Oが少しなりともわかると応用が効きそうなので試してみました。まずは with-open を使ったものです。
;-- ファイルを行リストにする (use '[clojure.contrib.duck-streams :only (reader)]) (defn file-to-lines [target] (with-open [r (reader target)] (loop [buf () line (.readLine r)] (if line (recur (cons line buf) (.readLine r)) (reverse buf))))) ;-- 行番号をつける (map vector (iterate inc 0) (file-to-lines "readme.txt")) ;-- "this" を含む要素だけを取り出す (filter #(not= -1 (.indexOf (% 1) "this")) (map vector (iterate inc 0) (file-to-lines "readme.txt"))) ;-- "this" を含まない要素だけを取り出す (filter #(= -1 (.indexOf (% 1) "this")) (map vector (iterate inc 0) (file-to-lines "readme.txt")))
次に遅延評価でやってみます。途中で失敗したときに閉じてやらなければならないので try - catch を入れています。最後まで読まずに放置するとファイルが開きっぱなしになるので注意が必要です。
(use '[clojure.contrib.duck-streams :only (reader)]) (defn file-to-lines [target] (let [r (reader target)] (letfn [(readline [] (try (let [line (.readLine r)] (if line (lazy-seq (cons line (readline))) (.close r))) (catch java.io.IOException _ (try (.close r) (catch Exception _ nil)))))] (readline))))
こういうのを途中で読み終える場合はどうしたらいいんでしょうか。さらに一段ラップして close 用のメソッドを提供するような作りにすればいいのかな。
Closureによる部分リスト検索(遅延評価)
前エントリの部分リスト検索ですが、別に :all を使わなくとも遅延評価してしまえばいいやということでやってみたら、こんな感じになりました。
(defn indexof [coll subcoll & from] (let [sub (seq subcoll) length (count sub) head (first from) head (cond (nil? head) 0 (< head 0) 0 :else head) ] (letfn [(func [i coll] (lazy-seq (when (not (empty? coll)) (if (= sub (first (partition length coll))) (cons i (func (inc i) (next coll))) (func (inc i) (next coll))))))] (if (empty? from) (func 0 coll) (func head (drop head coll))) ))) (indexof "たけやのたけやぶにたけたてかけた" "け") =>(1 5 10 14) (class (indexof "たけやのたけやぶにたけたてかけた" "け")) =>clojure.lang.LazySeq
まだサクッとは書けないなぁ…
プログラミングClojureを読んでみた。これは良い
JVM上で動く言語にはいろいろと興味があって、覚えたり QuickReflector 上に載せてみたりといろいろやっていたのは過去の話。
他のことで忙しくなってしまって熱も冷めてしまったのですが、Clojure は気になっていたので日本語訳が出たと聞き早速購入して読んでみました。
まずは、とりあえず、Java と簡単に繋ぐことができて嬉しい。
部分文字列の検索などは以下のような感じで済みます。
(.indexOf "abcdefgabcdefg" "cd" )
これだけなら特に言うこともなし。でも、これが部分リストの検索ということになるとちょっと面倒で、見た限りでは見つけられなかったので自分で書いてみました。
(defn indexof "Returns position of subcoll in coll. if specified all option, returns list of all positions." [coll subcoll & all] (let [sub (seq subcoll) length (count sub) func (fn [coll subcoll from all] (let [parts (map vector (iterate inc 0) (partition length 1 (drop from coll))) result (map #(+ from %) (for [[idx elm] parts :when (= elm sub)] idx))] (if all result (first result)))) head (first all)] (cond (nil? head) (func coll subcoll 0 false) (= (count all) 1) (if (keyword? head) (func coll subcoll 0 true) (func coll subcoll head false)) :else (func coll subcoll head (first (next all))) ))) (defn subcoll? "Returns whether subcoll contained in coll." [coll subcoll] (not (nil? (indexof coll subcoll))))
呼び出すときは以下のような感じで。
(indexof "abcdefgabcdefg" "cd") (indexof "abcdefgabcdefg" "cd" :all) (indexof "abcdefgabcdefg" "cd" 4 :all)
とはいえ再帰の最適化の話がまだよくわかってなくて、:all をつけると
(indexof (range 1 20000) '(2000 2001) :all)
なんかが落ちてしまうわけですが。
それはともかく、SISC で頑張ってみたけどすでに忘れてしまった身としてはこれから楽しみが一つできた気分です。
*PHPとActionScriptにどっぷりな1年でした。
気が回らなくて、こちらいつのまにか1年近く放置していました。
仕事で PHP/ActionScript にどっぷり漬かりつつ REST やら OSGi やら
眺めていたら QuickReflector もすっかり忘れている今日この頃。
ところで PHP で、以下のようなコードを実行してみます。
$a = 1; $b = 1; for ( $i = 0; $i < 64; $i++ ) { $c = $a == $b ? "matched" : "unmatched"; echo "{$i}:{$a},{$b}:{$c}\n"; $a <<= 1; $b *= 2; }
$a と $b がずいぶん違うのですがどうにかなりませんかね。
JavaScriptでメソッド参照したときの奇妙な動作(解決?)
先日メソッド参照とメソッド呼び出しをした場合に this の値が違う、ということを書きました。(http://d.hatena.ne.jp/serene/20090319/1237451298)
状況が分かりやすいように整理して書き直したコードはこうなります。
function Foo() {}; Foo.prototype.test = function() { return this; }; var foo = new Foo(); var func = foo.test; alert([ '1'.concat(func == foo.test ), // true. '2'.concat(foo == foo.test() ), // true. '3'.concat(func() == foo.test()), // false. '4'.concat(func() == this ), // true. '5'.concat(this == foo.test() ), // false. ]);
1 と 2 は期待通りとして、3が変だと思っていたのですが、4 と 5 の結果を考えると意味が分かってきます。testメソッドの中の this は、実際に呼び出されるまでは確定しておらず、呼び出される時点のコンテキストを参照して上が何であるかを決定しています。
func() については呼び出されて初めて中の this が宣言されていることに気づいて、上位の this 即ちグローバルコンテキストを返すことになっているわけです。ただ、だからと言って 単純に func は実は this.func なんだ!と思うとそれは以下の結果のごとく、違います。
function Foo() {}; Foo.prototype.test = function() { return this; }; var foo = new Foo(); var func = foo.test; alert( func == this.func ); // false.
それでは上位の this をとるなら、以下のように書くとどうなるでしょうか?
function Foo() {}; Foo.prototype.test = function() { return this; }; var foo = new Foo(); var func = foo.test; function Bar() {}; Bar.prototype.test = function(lambda) { return lambda(); }; var bar = new Bar(); alert([ '1'.concat(bar.test(func) == bar ), // false. 上位は bar ではない '2'.concat(bar.test(func) == this ), // true. '3'.concat(bar.test(foo.test) == this), // true. 上位が foo ではない ]);
funcあるいはfoo.testが「参照された状況」でコンテキストを拾って this に割り当て、「実際に呼び出された状況」でそれを返すという動作をしていることがわかります。
それでは、あらかじめコンストラクタで設定した値を返すとどうなるか。
function Foo() { this.val = this; } Foo.prototype.test = function() { return this.val; }; var foo = new Foo(); var func = foo.test; alert([ '1'.concat(func == foo.test ), // true. '2'.concat(foo == foo.test() ), // true. '3'.concat(func() == foo.test()), // false. '4'.concat(func() == null ), // true. '5'.concat(foo == foo.test() ), // true. ]);
結局のところ、this が何を指すかというと今のブロックが「参照されたとき」のブロックコンテキスト、ってことになりますね。それが関数なのかグローバルコンテキストなのかによって違いが出てくるということです。
参照と実行で分けて考えるとちょっとすっきりしたかも。