2006-01-10
■ [javascript] 連想配列の罠
javascriptでは、オブジェクトのプロパティを動的に定義することができ、また以下の2つが同じ意味となる。
- obj.prop
- obj["prop"]
これを利用して、空のオブジェクトを連想配列のように扱うことができる。
hash = new Object(); hash["Alice"] = "apple"; hash["Bob"] = "orange"; document.write("Bob has an" + hash["Bob"]);
…と、巷の「javascript入門」とかには書かれているのだが、ちょっとした罠があることに気づいた。
空のオブジェクトにも "constructor" や "toString" といったプロパティが定義されているため、 空のオブジェクトを使っても、厳密には「空でない」連想配列になってしまうのである。
hash = new Object(); function check(key){ if(hash[key] == undefined) error(); } check("constructor"); // => error()は実行されない
連想配列のキーが「任意の文字列」で、「未定義のキー」が意味を持つコードを書きたいときって、どうすれば良いのかな? *1
*1 ちなみにfor..in使えば?というのは駄目です('constructor'等は列挙されない)。
2006-01-23
■ [softs] 窓使いの憂鬱でEmacs風移動
mayuとKeyExtensionと両方常駐させてるのは何か無駄っぽい気がしたんで、 mayuで同等の機能を実現してみた。
■ [javascript] Dateの罠
「日」は1から始まるのに、「月」は0から始まる。
d = new Date(); document.write(d); //=> Mon Jan 23 19:39:48 UTC+0900 2006 document.write(d.getDate()); //=> 23 document.write(d.getMonth()); //=> 0
月が0 originとか仕様としてあり得ないだろ!と思ったのだが、 英語圏だと「1月」のように月に数字を付ける言い方はあまり使わないらしい (Januaryのように「月の名前」があるから。)
RubyのTime#monthが1 originなのは、実は「日本的」だったのか。
2006-01-24
■ [softs] TeXインストーラ3
本当に数クリックでplatexとかdvioutとかdvipdfmが動くようになったので驚いた。
TeXのインストールはとにかく面倒な印象があったのだが、これなら文句ないです。関係者に感謝。
2006-01-25
■ [vim] vim-latex (2)
vim-latexすごい!
入力中に「EIT」(element itemize)と入力すると、EITが
\begin{itemize} \item \end{itemize}<++>
に置き換えられて、\itemのあとにカーソルが移動する。
最後の<++>は何なのかというと、箇条書きの内容を書き終わったあとに「C-j」を押すと、 <++>が消えてそこにカーソルが移動するのである。 要するに、begin〜endから出るためにノーマルモードに戻ってカーソルを移動させる必要がない、ということ。
あと章を自動でfolding(折りたたみ)してくれたりするんだけど、まだ文章が長くないのでこっちの 有難みはまだよく分からない。とりあえず折りたたみの開閉をC-qに割り当ててみた。
nnoremap <C-Q> zA
2006-01-26
■ [scheme] Scheme:多値
関数から複数の値を返すことについて。
Cでは変数のアドレス(ポインタ)を渡すことで行う。
Rubyでは配列を返すこと(+多重代入)で行う。
Schemeでは「現在の継続に複数の値を渡す」ことで行う。
(values 1 2 3)のようにすると、関数から多値を返すことができる。valuesはR5RSで以下のように定義されている。
(define (values . things) (call-with-current-continuation (lambda (cont) (apply cont things))))
call/ccを使って、現在の継続(cont)に複数の値を渡す、と。
多値を受け取る側はrecieveとか、let-values等の構文/マクロを使う。
■ [javascript] VenkmanとFireBug
どう違うの?とか思ってたけど入れてみたら全然違った。
Venkmanはデバッガ。ブレークポイントを設定してステップ実行とか。
FireBugはJSコンソールとDOMインスペクタをくっつけたようなもの。 エラーを画面下に表示してくれる。 あとXMLHttpRequestの送受信データを表示したりとか。
2006-01-30
■ [prog] 遅延評価の実装 (from Scheme:たらいまわしべんち)
遅延評価を行うと計算が速くなる「たらいまわし関数」というものを、いろいろな言語で実装してみたという話。
Rubyによる最終版はこんな感じ。
def tarai( x, y, z = nil ) if x <= y then y else z ||= yield tarai(tarai(x-1, y, z), tarai(y-1, z, x)){ tarai(z-1, x, y) } end end puts tarai(ARGV[0].to_i, ARGV[1].to_i, ARGV[2].to_i)
taraiの中の再起呼び出しにおいて、第一、第二引数は先に計算してしまっている(遅延評価でない)。 第三引数だけ、値を計算するのではなく「値を計算するためのブロック」を渡すことによって、 実際に計算されるタイミングを遅らせている。こうすると、x <= yが成り立つ場合はzの値が計算されないので、 全てを先に計算する場合よりも速くなる。
Haskellで「遅延評価」という言葉を知ってから、何か得体の知れない黒魔術のようなものだと思ってたのだが、 意外と簡単に実現できるものだと知って驚いた。
Schemeではdelayとforceという機能で遅延評価を行う。
delayは、渡された関数から「プロミス」と呼ばれるオブジェクト(引数を持たない関数として 実装できる)を作る。forceは単純にプロミスを実行するだけである。 「値を計算して渡すかわりに、計算するための関数を渡す」という考えは上のRubyのコードに似ているが、 プロミスはそれに加えて一度実行するとその結果の値をキャッシュし、二回目の実行からはそれを返すという機能も持つ。
■ [ruby] 型チェックの弱さを補うもの (from RubyについてJavaプログラマが知るべき10の事柄)
どっかで見たような気がするが、もう一回読んでみた。
証言: […]2年程前に、私はあることに気が付きました。それは、私がどんどん安全のために存在する型付けの機構を当てにしなくなっていたことです。単体テストが型エラーの発生を防いでいてくれていたためです。[…] そしてPythonでアプリケーションをいくつか書き、続いてRubyでも書いてみました。型に関する問題が全く発生しないことがわかったときも、全然驚きませんでしたよ。
Rubyの場合、関数に間違ったクラスのオブジェクトを渡してもその瞬間にはエラーにならず、 存在しないメソッドを呼ぼうとした時点でエラーが起こる。 だからミスを犯した関数と実際にエラーになる関数が別だったり、いろいろなミス (例えば配列の範囲外へのアクセスや、変数名のタイプミス)が 全部「undefined method xxx for nil:NilClass」というエラーになったりして、 デバッグに手間取ることがある。
他の人はどうしてるんだろうな、と思っていたが、テストを書けばこの手のエラーは避けられるのか。
□ y.m. [お初にして亀レス。 if(!hash.hasOwnProperty(key)) error(); とすればいい。 c..]
□ y.m. [あと、Netscape/Mozilla の独自拡張だけど、 hash.__proto__ = null; としておけ..]