2012年12月30日日曜日

「すごいHaskellたのしく学ぼう」9章 todo.hsのバグ修正など


「すごいHaskellたのしく学ぼう」でHaskellを勉強し始めてみた。通称すごいHな本と言われる本ですが、Hは変態言語の頭文字のHなのかなあと思うほどに自分にとっては新鮮な概念が多く、久しぶりに言語を勉強して脳がリフレッシュされていく感じがします。Hello Worldが3分の1くらい読み進まないと出てこない本です。説明が丁寧で初めてHaskellに触れる自分でも理解しやすいです。訳者が自分の大学学部時代の同じ学科の同級生らしく、感心している場合ではないなあと思う今日このごろ。

そして、原著はオンラインで読めたのだな。。。。
Learn You a Haskell for Great Good!

今日は、第9章のtodoリストのコードにバグらしきものをいくつか発見したので、誰かの役に立てばと書き記しておきます。

ちなみに、Haskellのシンタックスハイライトは下記をBlogのテンプレートに貼付けて使用しています。Bloggerで使用する場合は、CDATAセクションで囲まないとXMLエラーになります。
http://code.google.com/p/syntax-highlighter-brushes/source/browse/scripts/shBrushHaskell.js

本のコードで修正したのは以下の2点。ちなみに2点目はオンラインの原著ではすでに修正されていた。
  • removeで重複要素が存在する時に後の番号を指定すると、先の番号の方が削除されてしまう
  • 引数でファイル名を指定しているのに、"todo.txt"がハードコードされている

以下が修正コード。コメントアウト部分が元々の部分。

以下のコードだと最初に見つかった要素を削除してしまうため、todoTasksが[1,2,3,1,4,5]という状態の時に4番目にある"1"を削除した時に、1番目の1を削除してしまいます。
delete (todoTasks !! number) todoTasks

./todo remove todo.txt 4
-> Expected: [1,2,3,4,5]
-> Actual : [2,3,1,4,5]

読者への宿題になっているbumpも実装してファイル書き込み部分の重複をupdateTodoにまとめたのが以下。

2012年11月6日火曜日

(Amazon.comで)Kindle PaperWhite購入した

5ヶ月Blog書いてなかったんかいな。。。


日本に帰国している間に、Kindle PaperWhiteが会社に届いていた。9月の半ばに予約して10月下旬の配達。それを取りに行くためだけに土曜に会社に行ったのだけど、自席に置かれてなくてがっかりして帰ってきたのだけど、ようやく入手。配達追跡記録をWebで見てみると、サインした人の名前が表示されていたので(これ地味にすごい)その人をたずねたら受け取れた。寝る前に読むのに眼が疲れなさそうなのと、最初から日本語対応しているのでPaperWhiteを選択。U.S.で発売されてるKindle Fireは日本語入力するためにはちょっといじってroot権限取ったりしないといけないらしい。

買ったのは3Gモデル。3G無しが$119、3G付き$179(広告付き版。あとで$30払えば広告は除けて、最初に$20払っとけば最初から広告無し版になる)。3Gモデルを選択したのだけれど3GはAmazonのサービス全般とWikipedia(なぜWikipedia?)だけしかつながらない模様。これは、日本で発売された時も変わらない気がする。抜け道あったよ的な情報もあるけど、自分の環境ではもう使えなかった。
※ぐぬぬ。$1=80円とすると、11/19発売の日本版の方が安いではないか。。
  Wifi:(US:9520円、JP:8480円)、3G+Wifi:(US:14320円、JP:12980円)
  →Amazon「Kindle」を日本でも11月発売、「Kindleストア」は10月25日オープン


長年バックライト液晶の画面しか見ていなかったため、この画面はすごく新鮮。電源ボタンを押してロックすると、上記の広告が出てくるのだけどそれが時間がたっても消えない。液晶とちがって、静止画スクリーンセーバーは電力を消費しないかららしい。画面がマルチタッチ対応で、ホームボタンもなくて電源ボタンしかない点も男前。Amazonアカウントにひもづいているため、ワンタッチでぽんぽん本が買えてしまうのは危険。

しかし、漫画のカテゴリーで少女コミックや少年コミックに並んでボーズラブコミックス(なぜかこれだけ複数形)が堂々リストアップされているのはいかがなものかw。

とりあえず、この漫画が異常に評価高かったので買ってみた。
夕凪の街 桜の国 (Action comics) [Kindle版]
(でもこの記事書いててまだ読んでない)

参考にしたページいろいろ:
Kindle PaperWhite Official Site
海外からKindle日本の本を購入して、読む方法
アマゾンの電子書籍リーダーKindle Paperwhiteを速攻レビュー
日本版 Kindle Paperwhite・Fire/HD FAQ 【まとめ】

2012年6月3日日曜日

javascriptクイズ(解説編)

下記の記事の解説を試みてみる。

JavaScriptクイズ「x + 0 == x - 0」

筆者は7個まちがえて65/100点でございました。とはいえ、実際の動きを調べてみたら偶然結果が当たっていただけのこともあり、点数ほどは理解出来ていません。ちなみに、動作確認した処理系は、"node.js v0.7.5-pre"。

まずは、1問目の解説から。

まずは、左辺がどのように展開されるかの解説。
{}+0
  • ToPrimitive({}, PreferredType=Number)+0
    // No hint for PreferredType then default "Number".
    // See 8.6.2.6 [[DefaultValue]](hint).
  •     When the [[DefaultValue]] method of O is called with no hint, then it behaves as if the hint were Number
  • {}.toString()+0
    // See 8.6.2.6 [[DefaultValue]](hint)
        5. Call the [[Get]] method of object O with argument "toString".
    // ここ自信無い
  • '[object Object]'+0
  • '[object Object]'+0.toString()
    // Because Type(ToPrimitive({},PreferredType=Number)) is String.
    // See 11.6.1 The Addition operator (+ )
        7.If Type(Result(5)) is String or Type(Result(6)) is String, go to step 12
        12. Call ToString(Result(5)).
        13. Call ToString(Result(6)).
  • '[object Object]0'

うーん、しんどい。お次は、右辺。
{}-0

最後に、左辺と右辺の比較の解説
See 11.9.3 The Abstract Equality Comparison Algorithm.
'[object Object]0'==NaN
  • ToNumber('[object Object]0')==NaN
    // 1. If Type(x) is different from Type(y), go to step 14.
    // 17.If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x)== y.
  • NaN==NaN
  • false
    // 5.If x is NaN, return false.

うーん、これでもいくつかはしょってるけど、まじめに解説するとめっちゃ大変(どっか間違ってたら、ご指摘願います)。こっからはがしがしはしょっていきます。



[]+0==[]-0
  • [].toString()+0==ToNumber([])-0
  • ''+0==0-0
  • '0'==0
  • ToNumber('0')==0
  • 0==0
  • true
ポイントは、以下の2点かな。
  [].toString() returns ''
  ToNumber([]) returns 0



(function(){})+0==(function(){})-0
  • (function(){}).toString()+0==ToNumber((function(){}))-0
  • 'function (){}'+0==NaN-0
  • 'function (){}0'==NaN
  • ToNumber('function (){}0')==NaN
  • NaN==NaN
  • false
関数もオブジェクトのひとつ。あとは、1問目と同じ。



null+0==null-0
  • ToNumber(null)+0==ToNumber(null)-0
  • 0+0==0-0
  • 0==0
  • true
ToNumber(null) returns 0.
See 9.3 ToNumber.



undefined+0==undefined-0
  • ToNumber(undefined)+0==ToNumber(undefined)-0
  • NaN+0==NaN-0
  • NaN==NaN
  • false
ToNumber(undefined) returns NaN.
See 9.3 ToNumber.



Infinity+0==Infinity-0
  • Infinity==Infinity
  • true



Array+0==Array-0
  • Array.toString()+0==ToNumber(Array)-0
  • 'function Array() { [native code] }'+0==NaN-0
  • 'function Array() { [native code] }0'==NaN
  • ToNumber('function Array() { [native code] }0')==NaN
  • NaN==NaN
  • false
コンストラクタも関数オブジェクト。



0+0==0-0
  • true
何も言うまい。



false+0==false-0
  • ToNumber(false)+0==ToNumber(false)-0
  • 0+0==0-0
  • true
ToNumber(false) returns 0. (ToNumber(true) returns 1).
See 9.3 ToNumber.



NaN+0==NaN-0
  • NaN==NaN
  • false
See 11.6.3 Applying the Additive Operators ( +,- ) to Numbers
If either operand is NaN, the result is NaN.



""+0==""-0
  • ToString("")+ToString(0)==ToNumber("")-0
    // 7.If Type(Result(5)) is String or Type(Result(6)) is String, go to step 12.
    // 12. Call ToString(Result(5)).
    // 9.Call ToNumber(Result(6)).
  • ''+'0'==0-0
  • '0'==0
  • ToNumber('0')==0
  • 0==0
  • true
ToNumber("") returns 0.
See 9.3.1 ToNumber Applied to the String Type.
A StringNumericLiteral that is empty or contains only white space is converted to +0.



"a"+0=="a"-0
  • ToString("a")+ToString(0)==ToNumber("a")-0
  • 'a'+'0'==NaN-0
  • 'a'==NaN
  • ToNumber('a')==NaN
  • NaN==NaN
  • false



"0"+0=="0"-0
  • ToString("0")+ToString(0)==ToNumber("0")-0
  • '0'+'0'==0-0
  • '00'==0
  • ToNumber('00')==0
  • 0==0
  • true



"1"+0=="1"-0
  • ToString("1")+ToString(0)==ToNumber("1")-0
  • '1'+'0'==1-0
  • '10'==1
  • ToNumber('10')==1
  • 10==1
  • false



"0.1"+0=="0.1"-0
  • ToString("0.1")+ToString(0)==ToNumber("0.1")-0
  • '0.1'+'0'==0.1-0
  • '0.10'==0.1
  • ToNumber('0.10')==0.1
  • 0.1==0.1
  • true



"0x"+0=="0x"-0
  • ToString("0x")+ToString(0)==ToNumber("0x")-0
  • '0x'+'0'==NaN-0
  • '0x0'==NaN
  • ToNumber('0x0')==NaN
  • 0==NaN
  • false
'0x0' is HexIntegerLiteral.
See 9.3.1 ToNumber Applied to the String Type.



"."+0=="."-0
  • ToString(".")+ToString(0)==ToNumber(".")-0
  • '.'+'0'==NaN-0
  • '.0'==NaN
  • ToNumber('.0')==NaN
  • 0==NaN
  • false
ToNumber(".") returns NaN while ToNumber(".0") returns 0.
See 9.3.1 ToNumber Applied to the String Type.



" "+0==" "-0
  • ToString(" ")+ToString(0)==ToNumber(" ")-0
  • ' '+'0'==0-0
  • ' 0'==0
  • ToNumber(' 0')==0
  • 0==0
  • true
See 9.3.1 ToNumber Applied to the String Type.
StringNumericLiteral:::StrWhiteSpace(opt) StrNumericLiteral StrWhiteSpace(opt)
A StringNumericLiteral that is empty or contains only white space is converted to +0.



Array()+0==Array()-0
  • []+0==[]-0
  • あとは、2問目と同様。
  • [].toString()+0==ToNumber([])-0
  • ''+0==0-0
  • '0'==0
  • ToNumber('0')==0
  • 0==0
  • true
15.4.1 The Array Constructor Called as a Function
it creates and initialises a new Array object.
うーん、これは知らなかった。
See JavaScript: new無しで基本型のコンストラクタを呼んだ時の挙動.



(function(){}())+0==(function(){}())-0
  • undefined+0==undefined-0
  • あとは、5問目と同様。
  • ToNumber(undefined)+0==ToNumber(undefined)-0
  • NaN+0==NaN-0
  • NaN==NaN
  • false
12.9 The return Statement
ReturnStatement : return [no LineTerminator here] Expression(opt);
If Expression is omitted, the return value is undefined.


ここまで読んでくださったみなさま、ありがとうございました。
どっか間違ってるとこがあったら、ご指摘ください。
それにしても、20問の中にいろんなパターンが含まれていて、よくできてるなあ。

参考:
Standard ECMA-262 3rd Edition -December 1999
JavaScript: new無しで基本型のコンストラクタを呼んだ時の挙動
What is {} + {} in JavaScript?
[ ] == ![ ] の謎を自分でも解いてみた

2012年4月7日土曜日

Flexリストコントロールでのデザインパターン(とiOSのUITableViewとの比較)

最近、Flexのリストコントロール(データグリッド)をいじってて分かったことのメモ。

Flexのリストコントロール


Flexのリストコントロールとやらは、以下みたいなのがあります。
左から順番に「リストレイアウト」、「ドロップダウンリスト」、「データグリッド」。


んで、このリスト系コントロールというのが「データ駆動型コントロール」と呼ばれるもので使い方に癖があったりします。もうちょっと言うと、ひとつのコントロールの世界の中でMVCデザインパターンが適用されている。ざっくりとした関係は以下。


Viewコントロールの中に「レンダラー」と呼ばれる描画用オブジェクトが存在し、そこにControllerとしてのFlex君がModelオブジェクトを差し込んで描画する。ポイントとしては、レンダラーオブジェクトはユーザの可視領域分といくつかのバッファ分の数しか存在しない。

「レンダラーA」〜「レンダラーC」で「データ1」〜「データ3」が描画されているとして、例えばデータグリッドを下スクロールすると、それまで「データ1」を描画していた「レンダラーA」が使い回されて「データ4」を描画するようになったりする。ようするに、描画オブジェクトのリサイクルについてFlexが面倒を見てくれる。

それによって、100万のデータを画面で扱う時に見えてない99万9990ほど分のModelオブジェクト用の描画オブジェクトが節約される。

Flexのリストコントロールを独自拡張する時は、上記が分かってないとうまく描画されないことに悩んで、ことあるごとにFlexにとって天敵の全行再描画を実施して画面が重くなってしまう。

気をつけないといけないのは、Modelオブジェクトの中で描画用データも保持しておく必要があること。ユーザ操作で文字を赤くした場合に、その「文字が赤い」情報をModelオブジェクトで保持しておかないとそのデータを再描画した際に、文字色がデフォルト色に戻ってしまったりする。ModelというのはビジネスロジックのModelではなくあくまで描画アーキテクチャ上でのModelということ。

IListItemRendererインタフェースの"set data"メソッドをオーバーライドすることにより
ModelオブジェクトをViewコントロールに独自のロジックで設定することが可能になる。

このアーキテクチャのいま思いつくメリット/デメリットは以下。

メリット:
  • 描画オブジェクトのリサイクルをFlexに任せることができる
デメリット:
  • 設計思想を理解するのに時間がかかる
  • 同じリストコントロール上で複数種類のコントロールの使い分けがしづらい(1行目はラベルで2行目はチェックボックスとか)

Flexのレンダラーについては以下がよくまとまっていて勉強になりました。
itemRendererパート1:inline itemRenderer
itemRendererパート2:external itemRenderer
itemRendererパート3:Communication(データのやりとり)
itemRendererパート4:ステート&トランジション

あと、Adobeのヘルプ。
Flex3開発ガイド/Listコントロール

Adobeのヘルプは、量もAppleのほど多くなくてかつ分かりやすくてよく書かれていると思う。まあ、Appleはフレームワークのソースコードを公開してないからあんだけのドキュメントが要るのかもしれないけど。

iOSのUITableViewとの違い


一方で、iOSのUITableViewはちょっと違う。
UITableViewは以下みたいなiOS版リストコントロール。


こいつへのデータの割当ては以下のようにUITableViewDelegateプロトコルのtableView:cellForRowAtIndexPath:メソッドを実装したりする。


Flexのリストコントロールとの違いは、開発者がコードの中でヘルパーメソッドdequeueReusableCellWithIdentifierやらinitWithStyle:reuseIdentifier:やらを呼び出し自力で描画オブジェクトのリサイクルをすること。

意識する必要のあることはこちらの方が多いが、リサイクルするしないをオブジェクトの識別子文字列(上記の例だと@"MyIdentifier")によって開発者が決められるため、Flexリストコントロールのデメリットであげた「同じリストコントロール上で複数種類のコントロールの使い分け」が比較的しやすい(識別子文字列でそこを意識すれば良い)。

ようするに、少し手間がかかるけどこっちの方が好き勝手しやすそうだ。まあ自由には責任が伴うのが常なので、粗悪な設計の影響を受けやすいけど。

その他Flexについて


Flexは他にもプロパティ無効化(invalidateProperties)や描画無効化(invalidateDisplayList)などの非同期な描画アーキテクチャを採用していて、世の中的にはFlashオワコンな風潮だけどフレームワークの設計は非常におもしろい。iOSと違って、ソースが公開されているのも良いね。

あとは、以下を読んだりして勉強しました。
asでカスタムコンポーネント(1)
asでカスタムコンポーネント(2)
asでカスタムコンポーネント(3)

コアなのは、UIComponentクラスLayoutManagerクラス

2012年3月4日日曜日

ActionScript3のSetterがGetterを呼び出す?


「詳説 ActionScript 3.0」には下記の内容が書かれています。
setメソッドは、呼び出されると、それに対応するgetメソッドを常に呼び出し、getメソッドの戻り値を返します。
これによりプログラムでは、新しい値の設定後すぐにその値を使うことができます。
…<中略>…
値を返すsetメソッドの機能は便利に使える一方で、getメソッドに制限を加えています。
特にgetメソッドでは、内部的な変数の値の取得に必要な程度を超える作業は絶対に行うべきではありません。
…<中略>…
setメソッドによるgetメソッドの自動的な呼び出しによって、カウンタの記録の追跡が損なわれることになります。
引用:ActionScript3のsetメソッドの返り値

けれども、上記の引用元でも書かれているように、必ず呼ばれる訳ではなさそう。
自分の環境でも、SetterからGetterは呼ばれていなかった。
それは、OreillyのP79の例のようにSetterの戻り値を何かの値に代入したりして結果を評価しても同じだった。

この辺、FlexSDKのレイヤーとFlashPlayerのレイヤーとどちらに依存しているかはよく分からない。
ちなみに自分の環境は以下(Windows版でも今回書いているすべての結果は同じだった)。
Flex4.1A
Macintosh Flash Player 10.3 Plugin content debugger (Intel-based Macs)
FlashBuilder4.6(体験版)

ここでは、ブレイクポイント設定時にFlashBuilderがおかしな動きをして
あたかもSetterがGetterを呼んでいるかのような挙動に見えてはまったので
そこも含めて確認した結果をメモとして残しておく。

まずは、下記のコードを用意。
ポイントは、以下の2点。
・プロパティpropを用意し、そのGetterでpropCountをインクリメントしてコンソール出力
・execute()でGetterとSetterをそれぞれ呼び出し


まず、ブレイクポイントを設定しない時のexecute()実行時のコンソールは、以下となる。
start
GetterCalled:1
callGetter:0
callSetter
SetterCalled
end
やはり、Setterが呼ばれた時もGetterは呼ばれていない。
(自分の環境では、Oreillyの記述は嘘だったということになる)

ただし、ブレイクポイントを23行目に設定して
1行ずつ28行目まで下記のように変数値を確認しながら動かしてみる。

コンソール出力結果は、下記。Getter呼び出しが増えている(赤字が増えた分)。
GetterCalled:1
start
GetterCalled:2
GetterCalled:3
callGetter:0
GetterCalled:4
callSetter
GetterCalled:5
SetterCalled
GetterCalled:6
end
GetterCalled:7

これ、ちょうどブレイクポイントを設定して変数値を確認した回数だけGetterが呼ばれている。
プロパティpropの値をデバッガ上で見せたくて、FlashBuilderが値を表示するたびにGetterを呼び出しているようだ。
しかも、変数参照ビューをスクロールして画面に表示された瞬間にGetterを呼び出すという手の込みよう。

この時点ではまだGetterは呼び出されていない。
表示領域を少しずつ大きくして・・・propが表示された下記の瞬間に初めてGetterが呼び出される(いないいないばぁ的な)
(ここではいないいないばぁするためにダミーの変数を追加しています)

結論としては、このような変な動きをするケースがあるため、Oreillyにも書いてあるように
Getterの中ではオブジェクト内部で保持する値を更新するような副作用を伴う処理は厳禁ということで。

2012年2月26日日曜日

javascriptの配列sliceコピー

使い捨てコード用メモ。
javascriptで以下の方法で、配列を別インスタンスにして(いくつか注意点があるけど)sliceメソッドを使ってコピーできる。

単なる代入との違いは、以下で確認できる。
sliceコピーした配列には要素の削除が反映されていない。
別インスタンスとしてコピーされている。

ただし、あくまで配列が別インスタンスになっているだけであって
deep copyではないので、object型の要素は同じインスタンスを指している。
以下の3つ目のobject型の要素はvar bでも"hoge"から"fuga"へと変更されてしまっている。
他のプリミティブ型はimmutableなのでたまたま変更が跳ねていないだけ。

sliceメソッドを本来の目的でない使い方をしているのと制約があるのとで
使い捨てコードを書くとき以外は使わない方が良さそうだけど、便利そうなので備忘録としてメモっておく。

■参考:
http://www.gucch.net/programming/javascript/jqueryやprototype-jsでオブジェクトをディープコピーする/

2012年2月17日金曜日

node.jsをgithub経由でローカルにインストール


consoleでjavascriptを手軽に扱えるようにしようとnode.jsをインストール。
せっかくなので、githubからforkしてローカルにcloneしてみた。
環境:Mac OS X Lion, Xcodeインストール済み

上記のnodeのページで右上の「fork」ボタンをクリック。
画像は既にfork済み状態でキャプチャしたので、「Your Fork」ボタンになってしまっている。

ちょいと待ったら、自分のGithub上にリポジトリが出来るので
あとはそれをローカルにcloneしてインストール。コマンドは以下。


インストールは、結構時間かかった。
最後にsudoでmake installしないと、自分の場合はPermission Deniedになった。

nodeコマンドが使えるようになった!