流れるようなスタイル with With (VB.NET)
おはようございます!久しぶりに忙しくなかったので夕方に仮眠のつもりで寝て起きたら11時間くらい経過していたので早起きできました。…おはようございます…。
今日は「流れるようなインターフェース」のサンプルをじーっと見ているとVB.NETのWith文を使ったコードに見えてくるというお話です。
「流れるようなインターフェース」にある流れるようなスタイルのサンプルはこんな感じでした。
private void makeFluent(Customer customer) { customer.newOrder() .with(6, "TAL") .with(5, "HPK").skippable() .with(3, "LGV") .priorityRush(); }
これをVB.NETで書いてみましょう。
Private Sub MakeFluent(ByVal customer As Customer) With customer.NewOrder .With(6, "TAL") .With(5, "HPK") : .Skippable() .With(3, "LGV") .PriorityRush() End With End Sub
似ていませんか?
顧客と注文の定義はこうできます。With()やSkippable()などでCustomerを返す必要はありません(= FunctionではなくSubにできます)。
Public Class Order Public Sub [With](ByVal amount As Integer, ByVal productName As String) ' ... 注文明細を追加するコード ... End Sub Public Sub Skippable() ' ... 呼ばれた時点での最後の注文明細をスキップするコード ... End Sub Public Sub PriorityRush() ' ... 注文の状態を至急にするコード ... End Sub End Class Public Class Customer Public Function NewOrder() As Order Dim o As New Order ' ... 何かするコード ... Return o End Function End Class
なので良い点があるとすれば流れるようなインターフェースを定義しなくても(コマンド・クエリ分離の原則に則しつつ)流れるようなスタイルで書けるところでしょうか。流れるようなスタイルと流れるようでないスタイルのどちらでも理解できるメソッド名を考えるのがつらそうですが…。
Dictionaryの初期化 + With文ならこうですね(2個目のdict
が省略できればもっとナイスなんですけれど…。VB 10だとC#のコレクション初期化子に似た形で初期化ができるそうです)。
Dim dict As New Dictionary(Of String, Integer) : With dict .Add("boo", "nobuyo") .Add("foo", "katsue") .Add("woo", "tetsuko") End With
JavaScriptにもwith文がありますが、JavaScriptのwith文ではメソッド(プロパティ)を使うときにドットがいらないので、ぱっと見で誰(withで指定したオブジェクトなのかその上のスコープなのか)の持ち物を使っているのかがわかりづらそうです。
今日は流れるようなスタイルを実現するには言語レベルとかメタレベルで対応がされている方がうれしいかもしれませんというお話…ではありませんでした。単に流れるようなスタイルとVB.NETのWith文を使ったコードが似ているということでしたね!
それではー。
isset()と!is_null()の値は常に等しいか
こんばんは!今日はPHPのisset()と!is_null()(is_null()を反転したもの)に同じ値を与えたとき*1、返されるbool値は常に等しいかどうかという小さいお話です。小さい話なのに長くなってしまいましたが…。物分かりが悪く、物忘れが激しく、読解力がない、そんな自分に説明するとなればこれでもかというくらい丁寧に書くしかないですよね。はい。ありがとう。面目ねぇ。そんな感じでやっていきたいと思いますが、まずisset()とis_null()の返す値と条件についておさらいしましょう。
- isset() ([http
- //www.php.net/manual/ja/function.isset.php:title]):
変数がセットされており、それが NULL でない
ならTRUEを返す - is_null() ([http
- //www.php.net/manual/ja/function.is-null.php:title]):
指定した変数がNULL
ならTRUEを返す
こんな感じでした。これに加えて未初期化変数など(上の引用文に言葉を合わせるとセットされていない変数)は暗黙に変換される状況でなければNULLを返すようになっているので(PHP: 基本的な事 - ManualやPHP: 配列 - Manual #構文など)、isset()と!is_null()の値は等しくなるように思えました。
<?php // 未初期化変数の値を調べる場合 var_dump(isset($foo)); var_dump(!is_null(@$foo)); // 一応E_NOTICEが出ないようにエラー制御演算子(@)を付けています // array内の未定義の要素の値を調べる場合 $bar = array(); var_dump(isset($bar["x"])); var_dump(!is_null(@$bar["x"]));
この結果は次のようになります。
bool(false) bool(false) bool(false) bool(false)
同じですね。
さて、本題の「isset()と!is_null()に同じ値を与えたとき、返されるbool値は常に等しいか」についてですが、結論から書くと違いました(常に等しければ書き留める意味がないのですけれども!)。異なる値になるのは次のような場合がありました。
- 文字列に範囲外の配列アクセスをした場合
- __get()を意図的に定義したクラスのオブジェクトのアクセス不能プロパティ*2を読んだ場合
- __isset()を意図的に定義したクラスのオブジェクトのアクセス不能プロパティを読んだ場合
順番に確認しましょう。
文字列に範囲外の配列アクセスをした場合
マニュアルのisset()のUser Contributed Notesを見て知ったのですが、これは次のような場合です。
<?php $s = 'foo'; var_dump(isset($s[9])); var_dump(!is_null(@$s[9]));
PHPでは(PHPでも)文字列に配列のようにアクセスすることができ、指定した数値が文字列の範囲内の値であれば対応する文字(1byte)が返されます(PHP: 文字列 - Manual #文字列への文字単位のアクセスと修正)。
上の例は"foo"という長さ3の文字列に対して9という範囲外の値で配列アクセスを行っている式にisset()と!is_null()をかけて比べているものですが、結果は次のようになります。
bool(false) bool(true)
異なる値になりました。これは文字列への範囲外の配列アクセスがNULLではなく空文字を返すからです(意図はよくわかりませんが…)。
__get()を意図的に定義したクラスのオブジェクトのアクセス不能プロパティを読んだ場合
これは次のような場合です。
<?php class A { } // 通常のクラス class B // __get()を定義してNULL以外が返るようにしたクラス { public function __get($name) { return ""; } } $a = new A(); echo '-- A' . PHP_EOL; var_dump(isset($a->foo)); var_dump(!is_null(@$a->foo)); $b = new B(); echo '-- B' . PHP_EOL; var_dump(isset($b->foo)); var_dump(!is_null(@$b->foo));
文字列の例を踏まえると、未定義と判定されつつNULL以外の値が返る場面を作ればisset()と!is_null()は異なる値になるはずですね。
この結果は次のようになります。
-- A bool(false) bool(false) -- B bool(false) bool(true)
期待通り異なる値になりました。
__isset()を意図的に定義したクラスのオブジェクトのアクセス不能プロパティを読んだ場合
これは次のような場合です。
<?php class A { } // 通常のクラス class B // __isset()を定義して常にTRUEが返るようにしたクラス { public function __isset($name) { return true; } } $a = new A(); echo '-- A' . PHP_EOL; var_dump(isset($a->foo)); var_dump(!is_null(@$a->foo)); $b = new B(); echo '-- B' . PHP_EOL; var_dump(isset($b->foo)); var_dump(!is_null(@$b->foo));
そういえば__isset()…と思いisset()が常にTRUEを返すような__isset()を定義してみました。文章がややこしいな。この結果は次のようになります。
-- A bool(false) bool(false) -- B bool(true) bool(false)
異なる値に…当然と言えば当然ですね。
そういえばマニュアルには__isset() は、 isset() あるいは empty() をアクセス不能プロパティに対して実行したときに起動します
とありますが、そうすると__isset()がisset()によって呼ばれたかempty()によって呼ばれたかをどうやって__isset()内で区別するのでしょうか…。
まとめ
変数などが未定義であるときにNULLが返されなければ当然isset()と!is_null()は異なるわけですが、そういう場面がいくつか出来るということがわかりました。isset()をNULLチェックに使うのは少し危うい場面があるので考えて使いましょうということですね。
あれ、時間が…。それではー。
*1:!is_null()に値を与えるという書き方は変ですね
*2:アクセス不能プロパティの意味はPHP: オーバーロード - Manualに準じます
URIの一般的構文の正規化
こんばんは!そういえば日曜日に西本願寺に行ってきたんです。お寺っていいですよね。匂いとか。お金があったら寺買収したいです。
今日はURIの一般的構文の正規化のお話というかメモです。そういえばmailto:のtoは正規化せずに、どこかでtoのリストを持つようにして比較に使うようにすれば良いかもしれません。domainが同じでdisplay-nameが違う場合は…同値?
パーセントエンコーディング
scheme
- 小文字に変換する (6.2.2.1)
- HTTP://example.com/ → http://example.com/
authority
- authority(host)がschemeでの規定値ならauthority(host)を削除する。userinfoやportが空でないときにhostがschemeの規定値ならhostは削除しない (RFC 3986 section 6.2.3)
userinfo
host
- 小文字に変換する
- http://EXAMPLE.com/ → http://example.com/
path
query
PHPでURIを扱うものをつくろうの会
こんばんは!こっちでは最近寒かったり暑かったりしますが風邪など引いてないですか?最近本屋でどうぶつしょうぎを見つけて衝動買いしてしまったんですが、考えたらやってくれる相手が近くにいなくて悲しい思いをしています…。子供さんがいたら買ってあげるといいかもしれません。
あと最近PHPをやることになっているので、NetBeans 6.8 beta + PHPUnit 3.4.2の試用とPHPとURIの勉強をかねてPHPでURIを扱うものをテスト実装しようとしています…というのが今回のお話です。PHPの関数名がフリーダムでやりきれない思いになったりarrayとかempty()とかの挙動に引っかかったりしながらこんな感じのことを考えてつくっています。
- RFC 3986ベースでできるだけRFCに沿って実装する
- 一般的構文, http:, https:, ftp:, file:, mailto:, data: あたりに対応したい
- RubyのURIの生成手順*1は良いと思うので生成手順とクラスの構成はRubyのURIに近い形にする
- URI生成時にデフォルトでURIを正規化すると非正規形のURIが表現できないのでデフォルトで正規化しない (.NET FrameworkのSystem.Uriはデフォルトで正規化するみたいです)
今のところhost(IP-literalとIPv4address)以外の解析とチェック、一般的構文の正規化(ポートの正規化とかドットセグメントの削除とか)ができるようになっているところです。RFC 3986は親切なことに実装例が書いてあるのでありがたいです。
IRIをどうするかはまだ決まっていません。mailto:の正規化はできないかもしれません(そもそも正規形が何かについて書いていなさそうなのと、mailbox内のdomainの小文字化が出来そうかわからないので…)。
mailto:とdata:の謎のurlcとかurlcharとかは今のところ無視してRFC 3986の一般的構文として解析してからscheme固有の制限のチェックなどをすることにしています。ただurlcとかurlcharがRFC 2396のuricなら、queryの区切り子("?")がuricに含まれていること、RFC 2396のopaque_partの部分がRFC 3986だとpath-rootlessとqueryに収束する(と思う)関係で、例えばdata:text/plain,foo?bar
を解析するとpathが"text/plain,foo"でqueryが"bar"みたいに分かれてしまうのが何だか面白くないです…。
あとPHPのどのバージョンに合わせるかなんですが、遅延静的束縛を使わないといけない形になっているので今は5.3以上になってしまっています。割り切って完全に5.3以上にするならcreate_function()は面倒なので無名関数を使ったりしたいです。
ちょっと文章の整理ができていないですがもう夜遅くなってきたのでこのお話の続きは今度にして寝ます。ではー。
「NodeListがliveなものを返すってDOM3 Coreのバグじゃね?」と言われているのはなんで?
querySelectorAllがliveじゃないNodeList返すのはなんで? - vantguarde - web:gからの話なのですが、気になったので調べています。
今のところ「NodeListがliveなものを返すってDOM3 Coreのバグじゃね?」と言われている理由の予想としてはこんな感じかな、と思います。
- liveで嬉しいことがあまりないこと((liveで嬉しいことをどなたかいろいろ教えていただけると嬉しいです。自分としては「過去の慣習」、「
getElementsByTagName()
等の呼び出しが一回ですむ」、「childNodesを親Nodeから切り離しても切り離さなくても同じように扱える」くらいのものしか思いつきませんでした)) - 実装にかかる時間(実装者の問題)
- 処理速度(実装者、スクリプト作成者、ユーザーの問題)
- 繰り返し構文+インデックスアクセスでのDOM木への追加または削除のケース等で混乱を招きやすい(スクリプト作成者の問題)
- 仕様に使いにくい(策定者の問題)
仕様に使いにくいというのは、今のNodeListは「DOM木の変更によって自動更新されるNodeの文書順リスト」*1なので単に「Nodeの(文書順)リスト」として使いたい仕様の場合(例えば高度な検索条件を指定して結果をリストとして返すような仕様の場合、主に軽量機器向けの仕様の場合)、NodeListを敬遠せざるを得ないということです。もし単に「Nodeの(文書順)リスト」というだけであればDOM XPathはもう少し不細工でないように出来たんじゃないかと思います(Element Traversalなんかも?)。
Selectors APIのquerySelectorAll()
のNodeListをliveにする場合は、擬似クラス(特に:hover
や:active
)のことを考えると最悪「DOM木の変更とユーザーアクション(ポインティングデバイスの移動等)によって自動更新されるNodeの文書順リスト」になってしまって、スクリプト作成者にさらなる混乱を与えることになるかもしれません。また、CSS3セレクタはXPathには及びませんが、さまざまなセレクタやグループ化によって十分に複雑な検索が可能ですから速度的な面も心配です。
このままだと今後NodeListを嫌った高レイヤーの仕様が軒並み不細工になっていくおそれがあるので、今後のことを考えるとNodeList自体をデフォルトをstaticにしてオプションでliveにした方が良いような感じがします。
メモ*2
バグ発言関連
- http://lists.w3.org/Archives/Public/public-webapi/2008Feb/0080.html
Yeah, we should drop StaticNodeList and add a note that DOM Level 3 Core saying NodeList is "live" is a bug.
- http://lists.w3.org/Archives/Public/public-webapps/2008OctDec/0272.html
This is considered to be a bug in the DOM Core specs, which will hopefully be fixed by Web DOM Core, which is a proposal Simon Pieters is working on.
- http://simon.html5.org/specs/web-dom-core#interface-nodelist (Work in Progress, 2008-11-21版)
A NodeList is a collection, except that NodeLists can be static (as opposed to live) if a specification defines them to be static. [HTML5]
Element Traversal, SVG Tiny 1.2
- http://lists.w3.org/Archives/Public/public-webapi/2008Mar/0226.html, thread
- http://krijnhoetmer.nl/irc-logs/webapps/20081121#l-135
[16:28]
Lachy: just checked... SVG Tiny 1.2 doesn't use nodelist at all, which is why we din't include it in Element Traversal
*1:http://www.w3.org/TR/DOM-Level-3-Core/core.html#td-live等。あれ、ordered listsとはあるけどdocument order云々とは書いてないんですね。文書順じゃない順序つきのNodeListって何かあったかな…
*2:偏向有
SGML/XML処理命令, PHP short_open_tag on / off
SGML
処理指令 = pio, システムデータ, pic ―(44) システムデータ = 文字データ ―(45)SGMLsec7_0.html, Latest updated 1998.01.19
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"> <title></title><p></p> <? /* php code */ ?>
XML
[16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>' [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))Extensible Markup Language (XML) 1.0 (Fifth Edition)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><head><title></title></head><body><p></p> <?php /* php code */ ?> </body></html>
Number dot
1. // => 1
1.toFixed(2) // 1. toFixed(2) => syntax error
(1).toFixed(2) // (1) . toFixed(2)
1 .toFixed(2) // 1 . toFixed(2)
1..toFixed(2) // 1. . toFixed(2)
1["toFixed"](2)