JSerが比較コードを書きながらPerlのデータ構造を学ぶ
厳密に言うと間違ってるかもしれないことを感覚的に書いているので、あまり間に受けない方がいいかもしれません。
JavaScriptは、自分が触ったことがある言語の中では、最も仕様を理解している言語になると思います。
そろそろ真面目にサーバーサイドの言語も学んでいきたいと思っているところです。
初めて触れた言語であるPerlをサンプルコードを書きながら比較して理解していこという趣旨のもと、メモがてら完全なるひとりごとをエントリーとして起こしました。
以下、延々とサンプルコードと感想コメントです。
数字や文字列
Perl
my $hoge = 1; print $hoge;
JavaScript
var hoge = 1;
print hoge;
JavaScriptで言うvarはPerlでいうmy。Perlは他にもlocalとかourとかあるけど、JavaScriptにはそれに相当するものはない。
ご指摘いただきました
配列
Perl
my @hoge = (1, 2, 3); print $hoge[1];
JavaScript
対応なし
Perlでいう配列ってJavaScriptにはない気がする。
Perlの例はプリミティブな配列だけど、JavaScriptにプリミティブな配列ってないはず。(参照の配列はある)
代入時には@がプレフィクスだけど、参照時は$を使う。
ハッシュ
Perl
my %hoge = ( hoge => 1, fuga => 2, piyo => 3 ); print $hoge{hoge};
数字や文字列のリファレンス
Perl
my $hoge = 1; my $one = \$hoge; print $$one;
JavaScript
対応なし
PerlはJavaScriptでいうプリミティブなデータもリファレンスにできるみたい。JavaScriptだとコピーになる。
リファレンスを渡すときには、\を先頭に書いて、呼び出す時には、$の頭に$を付ける。
配列のリファレンス
Perl
my @hoge = (1, 2, 3); my $fuga = \@hoge; print $fuga->[0];
JavaScript
var hoge = [1, 2, 3]; var fuga = hoge; print fuga[1];
Perlの配列リファレンスは、JavaScriptの配列と同等と思った。
通常の配列は()で定義するけど、配列のリファレンスは[]で定義する。
リファレンスを渡すときには、\を先頭に書いて、呼び出す時には、$を付けて、アロー演算子の後にインデックスを指定する。
JSerはPerlの配列リファレンスを使えば、JavaScript感覚で操作できるかもしれない。
無名配列のリファレンス
Perl
my $hoge = [1, 2, 3]; print $hoge->[0];
ハッシュのリファレンス
Perl
my %hoge = (hoge => 1, fuga => 2, piyo => 3); my $fuga = \%hoge; print $fuga->{hoge};
JavaScript
var hoge = { hoge: 1, fuga: 2, piyo: 3 }; var fuga = hoge; print fuga.hoge;
配列のリファレンスと同じような感じ。
通常のハッシュは()で定義するけど、ハッシュのリファレンスは{}で定義する。
リファレンスを渡すときには、\を先頭に書いて、呼び出す時には、$を付けて、アロー演算子の後にkeyを指定する。
配列と同様に、JserはPerlのハッシュリファレンスを使えば、JavaScript感覚で操作できるかもしれない。
無名ハッシュのリファレンス
Perl
my $hoge = { hoge => 1, fuga => 2, piyo => 3 }; print $hoge->{hoge};
関数
Perl
sub hoge { # これの省略形↓ my $arg = shift @_; my $arg = shift; } hoge('hoge');
JavaScript
function hoge { var arg = Array.prototype.shift.call(arguments); return arg; } hoge('hoge');
JavaScriptは普通に引数指定できるけど、Perlに合わせるとこんな感じかなと思う。
JavaScriptでいうargumentsが、Perlだと@_で、shiftすれば先頭を取れる。
で、Perlはreturn書かなくても、最後に評価した変数が自動的に返る。
無名関数のリファレンス
Perl
my $hoge = sub { my $arg = shift; }; print $hoge->('hoge');
JavaScript
var hoge = function() { var arg = Array.prototype.shift.call(arguments); return arg; }; print hoge('hoge');
Perlでも関数を変数に格納できる。これで名前付き関数の定義ができるけど、あんまりこういう書き方は見たことがない。
JSer的にはしっくり来るけど、Perlでは気持ち悪い書き方なのかもしれない。
関数と文字列を同じ名前で定義
Perl
my $hoge = 'hoge'; sub hoge { print 'fuga'; } print $hoge; hoge();
Perl
my $hoge = 'hoge'; my $hoge = sub { print 'fuga'; }; print $hoge; $hoge->();
配列のコピー
Perl
my @hoge = (1,2,3); my @fuga = @hoge; print @fuga[0];
JavaScript
var hoge = [1,2,3]; var fuga = Array.prototype.slice.call(hoge); print fuga[0];
Perlだとプリミティブな配列があるからコピーは簡単そう。
JavaScriptは参照型だからめんどくさい。
無名関数リファレンスを定義してその場で実行
Perl
my $hoge = sub { my $num1 = 10; my $num2 = 100; return $num1 * $num2; }->(); print $hoge;
JavaScript
var hoge = (function() { var num1 = 10; var num2 = 100; return num1 * num2; })(); print hoge;
JavaScriptで使ったりする書き方だけど、Perlでも難なくできた。
まとめ
- Perlは変数の型(コンテキスト)を意識して使いわける必要があるけど、JavaScriptは変数の型を意識しなくても良い。
- Perlは変数の型(コンテキスト)によって名前空間が違う。
- Perlの型グロブを使って、すべての型をまとめて扱うこともできるらしい。
- JavaScriptの配列やオブジェクトは、Perlの配列やハッシュではなく、配列のリファレンス、ハッシュのリファレンスに近い。記法もすごく似ている。
- Perlでは値を参照する時にデリファレンスする必要があるが、JavaScriptではデリファレンスする必要がない。
- Perlでは文字列や数値もリファレンスとして渡すことができる。
JavaScriptな頭でPerlを理解するには、JavaScriptではPerlのリファレンス相当のものを常に触っているという意識を持つと良さそうです。
そうすると一見ややこしく感じる、リファレンスの理解が進む気がしました。
MovableTypeのプラグインを書いてみた
必要に迫られて、今までぼんやりとしか把握していなかったMovableTypeを触りはじめました。
Flickrの写真をサイトの中に埋め込みたかったんですが、せっかくなのでスクラッチでプラグインを書いてみようということで、「Flickr photos List」というプラグインを作ってみました。
※MTOS 5.02を使ってます。
MovableType(MT)のプラグインの練習
MovableTypeのプラグインは、MTタグを自作するもんだというあやふやな理解でいて、過去に買った以下の書籍を参考にコードを書いてみました。(最新バージョンじゃないのでご注意)
Movable Type プロフェッショナル・スタイル MT4.1対応 (Style for professional)
- 作者: CSS Nite,上ノ郷谷太一,蒲生トシヒロ,荒木勇次郎,藤本壱,関根元和,黒野明子,柳泰久,野田純生,丹羽章
- 出版社/メーカー: 毎日コミュニケーションズ
- 発売日: 2008/04/08
- メディア: 単行本(ソフトカバー)
- 購入: 3人 クリック: 66回
- この商品を含むブログ (18件) を見る
cheebowさんこと、関根さんの「プラグイン開発と応用」の章でまさに、プラグインの開発方法が載っていたので、ここを見ながらサンプル通りの簡単なMTテンプレートを作りました。
本番
今回作るFlickrの写真をサイトに埋め込むプラグインに似たようなのを、Plugin Directoryを使って探してみると、FlickrPhotosというプラグインがあり、詳細を見てみると、Requirementsという項目に、Flickr::APIが必要とあります。
サードパーティのモジュールってどうやって使うの?
サードパーティのモジュールをMTで使う方法がわからなかったので調べてみると、mt/extlibというディレクトリにサードパーティのCPANモジュールをインストールすれば使えるということがわかりました。
よくよく見ると、mt/extlibの中には、よく使うCPANモジュールがたくさんあって、今回作りたいプラグインは、元々入っているLWP::SimpleとXML::Simpleで十分と判断しました。
実際作ってみて
書籍やWebを見ると作り方がいろいろあって混乱してきました。
書籍では、テンプレートタグの定義をinit_registryというサブルーチンで定義しているし、元々入っているMultiBlogやTypePadAntiSpamでは、MT::Pluginコンストラクタのregistryというプロパティで定義しているし、SixApartの技術情報提供ブログでは、MT::Template::Contextのaddtagメソッドで定義してるし。
これが俗に言うTMTOWTDIってやつかorz...
最終的に、MovableType.orgのチュートリアルにある、YAMLで設定を記述する方法に行き着きました。
Movabletype.orgのドキュメント
Movabletype.orgには、Movabletype.jpでは見つからなかったDeveloper's Guideがあり、非常に丁寧なチュートリアルがあります。
このチュートリアルは、Makefiile.PLを作ったり、PODを書いたりなど、Plugin Directoryへの登録まで指南してくれます。新参者はここのドキュメント見るのが良いのかなと思ったりしました。
作ったプラグイン
管理画面のテンプレートの作り方は書籍を参考に、後はDeveloper's Guideを参考にして、簡単なFlickrのプラグインができました。
まとめ
自分を含めて、今からプラグイン開発に取り組もうとする始める新参者*1にとって、どこを見れば作れるのかが提示されていないような気がするので、Movable.jpにもデベロッパー向けのガイドが欲しいなと思いました。
TMTOWTDIはいいんですけど、すべての作り方を網羅的に載せて、開発者がどの実装方法を使うか選べるようなドキュメントも欲しいなと思います。
個人的には、Movabletype.orgのドキュメントが一番詳しくてわかりやすかったです。
mt/extlibの中には、Perlでよく使うモジュールがいくつもあるので、モジュールの追加インストールをしない状態でも、いろいろ遊べそうな気配があります。ディレクトリの中を覗いてみて、「おぉ、いいじゃん。」となりました。
なにはともあれ、Perlの勉強にもなりそうだし、MTのプラグインの開発は楽しいかもと思った次第であります。
JavaScriptのfor文の中で、カウンタ変数を利用する関数をジェネレートするいくつかの方法
for文の中で、カウンタ変数を利用する関数を作るとき、はじめは必ずハマるであろうことが予想できます。
私も実際にハマったことが多々あります。
本エントリーでは、for文の中で、カウンタ変数を利用する関数をジェネレートするいくつかの方法を提示したいと思います。
問題のあるコード
以下のコードがfor文の中で、カウンタ変数を利用する関数をジェネレートするコードです。for文を1から5まで繰り返し、配列の中にカウンタ変数を出力する関数を格納していきます。関数が格納された配列をさらにfor文で走査し、1から5まで出力することを意図しています。
素直にコーディングすると、まずうまくいきません。
var func_list = []; for (var i = 1; i < 6; i++) { func_list.push(function() { return console.log(i); }); } for (var j = 0; j < func_list.length; j++) { func_list[j](); }; // 結果: // 66666
関数をジェネレートする関数内にカウンタ変数がありますが、評価する際にはiのカウンタが回りきって、すべて6になってしまいます。
これを、以下のそれぞれの方法で意図する動きにします。
カウンタ変数をコピーしておく
最も簡単な解決方法が、カウンタ変数をコピーしておくという方法です。
var func_list = []; var counter_list = []; for (var i = 1; i < 6; i++) { counter_list.push(i); func_list.push(function() { return console.log(counter_list.shift()); }); } for (var j = 0; j < func_list.length; j++) { func_list[j](); };
couter_listという配列にカウンタ変数をあらかじめコピーしておきます。そうすれば問題なく意図するコードとなります。でも、これは根本的な解決になってないし、タイトルの定義ともずれるので、微妙です。
evalを使う
evalを使うとうまくいきます。
var func_list = []; for (var i = 1; i < 6; i++) { func_list.push(eval('(function() { return console.log(' + i + '); })')); } for (var j = 0; j < func_list.length; j++) { func_list[j](); };
evalを使うことでカウンタ変数の束縛に成功します。id:perlcodesampleさんの記事を見て初めて知ったんですけど、evalって関数オブジェクトを直接引数にいれることはできないんですね。関数のオブジェクトを()(丸括弧)で囲んで、式化しておかないといけないようです。
参考: サンプルコードによるPerl入門
new Function()を使う
evalと似てます。
var func_list = []; for (var i = 1; i < 6; i++) { func_list.push(new Function('return console.log(' + i + ');')); } for (var j = 0; j < func_list.length; j++) { func_list[j](); };
カウンタ変数を束縛することに成功します。
基本的に、evalされる文字列に変数を含める場合は、束縛ができるっぽいですね。
無名関数を使う(クロージャを使う)
最も一般的な方法が無名関数を使う方法のようです。他の方法に比べてパフォーマンスが良いみたいです。私も最近はこれで解決しています。
var func_list = []; for (var i = 1; i < 6; i++) { (function(i) { func_list.push(function() { return console.log(i); }); })(i); } for (var j = 0; j < func_list.length; j++) { func_list[j](); };
無名関数の位置を変えた以下のパターンでもいけます。
var func_list = []; for (var i = 1; i < 6; i++) { func_list.push( (function(i) { return function() { return console.log(i); } })(i) ); } for (var j = 0; j < func_list.length; j++) { func_list[j](); };
楽だから無名関数を使いますが、本質的にはクロージャを使うということです。以下のコードでもカウンタ変数を束縛できます。
var func_list = []; var func = function(i) { return func_list.push(function() { return console.log(i); }); } for (var i = 1; i < 6; i++) { func(i); } for (var j = 0; j < func_list.length; j++) { func_list[j](); };
最終的に配列に格納する関数がクロージャになっているので、外側で定義された環境(カウンタ変数)を使うことができます。for文の中に定義してもパフォーマンスの無駄なので、外に出しています。クロージャってほんとに不思議。
letを使う
JavaScript1.7以降限定ですが、letでもOKです。サポートするブラウザが増えれば、簡単だしパフォーマンスもいいみたいなので、今後、これが主流になると思われます。(問題がある書き方だったので最後の追記参照)
まずはlet文を使う方法。
var func_list = []; for (var i = 1; i < 6; i++) { let (i = i) { func_list.push(function() { return console.log(i); }); } } for (var j = 0; j < func_list.length; j++) { func_list[j](); };
簡潔にかけて素敵。
次はlet式。
var func_list = []; for (var i = 1; i < 6; i++) { func_list.push(let (i = i) function() { return console.log(i); }); } for (var j = 0; j < func_list.length; j++) { func_list[j](); };
で、最後はlet定義。
var func_list = []; for (var i = 1; i < 6; i++) { { let j = i; func_list.push(function() { return console.log(j); }); } } for (var j = 0; j < func_list.length; j++) { func_list[j](); };
個人的にはlet文が見やすく好きかな。let文とlet定義は、スコープが変わることを明示するために、省略可能な{}(ブレース)も書いといた方がいいと思います。早く対応して欲しい。
補足: let定義のハマりどころ
let定義を使った時にハマった箇所について紹介します。
上のコードではなく、最初は以下のコードを書いていました。
var func_list = []; for (var i = 1; i < 6; i++) { let i = i; func_list.push(function() { return console.log(i); }); } for (var j = 0; j < func_list.length; j++) { func_list[j](); }; // 結果: // undefined(5つ分)
let文やlet式のように、iにiを束縛しようとしたら、なぜかundefinedになってしまいました。何でだろうと思ってMDCをよく読むと同じようなことが書いてあったので理解できました。
参考: New in JavaScript 1.7 - MDC
let定義の右辺は、左辺で束縛する変数名と同じスコープに属するらしく、この場合、右辺のiは左辺のiを参照してしまいます。
左辺をjとした場合は、外側のスコープのiを参照します。難しい。。。
イメージとしてはこんな感じです。
let i = i; // 新しいブロック | 内側のブロックのi(内側にiがあるから) let j = i; // 新しいブロック | 外側のブロックのi(内側にiがないから)
let使う時は宣言なのか宣言じゃないのかを意識しないといけないですね。
with文を使う方法
with文でも実現できるよう。
var func_list = []; for (var i = 1; i < 6; i++) { with ({i:i}) { func_list.push(function() { return console.log(i); }); } } for (var j = 0; j < func_list.length; j++) { func_list[j](); };
letを調べている時に、id:nanto_viさんの記事を見て知りました。
参考: JavaScript でブロックスコープを実現する: Days on the Moon
無名オブジェクトを束縛させて、結果的に無名オブジェクトのプロパティに格納したカウンタ変数も束縛できているという魔法です。
おまけ
Perlも勉強しているので、なんとなくPerlでも書いてみました。
use strict; use warnings; my $func_list = []; for my $i (1..5) { { my $i = $i; push(@{$func_list}, sub { return print $i }); } } for my $j (0..$#{$func_list}) { $func_list->[$j]->(); }
Perlだと{}(ブレース)だけで新しい環境を作ることができるので、簡単に実現できます。JavaScriptのlet文に近い感じです。
まとめ
以上の方法をつかって実現できましたが、setTimeout関数だとか、addEventListnerのcallback関数に引数を渡したい時には同じ要領で応用ができます。なので、意外と使う頻度が高かったりします。
ちゃんとまとめたんだから同じことでハマるんじゃないぞ俺。
追記
letは、今後主流の書き方になると思いますと書いたんですが、そう言い切るのはまずいということを知りました。
Twitterにて@taku_eofさんと、@edvakfさんからご指摘いただきました。
@taku_eofさんのつぶやき
http://tinyurl.com/ygbrlww @taiju 氏の記事で let について「サポートするブラウザが増えれば、簡単だしパフォーマンスもいいみたいなので、今後、これが主流になると思われます」と言っているが、ES5 には盛り込まれなかったからなぁ……。
link: http://twitter.com/taku_eof/status/5849137709
@edvakfさんのつぶやき
@taku_eof そうですね。僕もあの書き方はどうかなと思いました。(パフォーマンスを測ってない(?)ところも…)
link: http://twitter.com/edvakf/status/5850725122
英語がわからないなりにECMAScript5の仕様書を読んでみると、letは、将来の予約語としてリストアップされているものの、仕様自体には記載されていないようです。なので主流になるかどうか以前に導入されない可能性もあるので、訂正させていただきました。
MooseでAdapterパターン
前回に引き続きMooseでAdapterパターンを書いてみました。
教科書は結城さんのデザパタ本。
コード
Banner.pm
package Banner; use Moose; use Perl6::Say; has 'string' => ( is => 'rw', isa => 'Str' ); no Moose; sub BUILDARGS { my ($self, $string) = @_; return { string => $string }; } sub showWithParen { my $self = shift; say '(' . $self->string . ')'; } sub showWithAster { my $self = shift; say '*' . $self->string . '*'; } 1;
PrintBanner.pm
package PrintBanner; use Moose; extends 'Banner'; with 'Print'; sub BUILDARGS { my ($self, $string) = @_; super(); return { string => $string } } no Moose; sub printWeak { my $self = shift; $self->showWithParen; } sub printStrong { my $self = shift; $self->showWithAster; } 1;
main.pl
use strict; use warnings; use PrintBanner; my $p = PrintBanner->new('Hello'); $p->printWeak; $p->printStrong;
Adapterパターンは簡単でわかりやすかったです。Web APIを使ったプログラムを作る時とか結構使いそうな気配。
MooseでIteratorパターン
最近、Perlでとあるプログラムを作成しているのですが、動くコードは書けるものの、納得できるコードが書けないというジレンマに陥っています。オブジェクト指向で作りたいとは思いつつ、手続きで書く→オブジェクト指向っぽく直す→これでいいの本当に?みたいな悪循環にハマります。要するに経験不足なので、設計ができないし、良い設計がどんなものかもわかんないんですよね。
ということで、一旦脇道にそれて、良い設計のお手本とも言うべきデザインパターンに入門してみることにしました。
教科書はいろんな人が教科書にしているid:hyukiさんのデザパタ本です。ずっと手元には置いていたものの、実装したことがなかったので、紹介されているパターンを前から順番に実装してみることにしました。
- 作者: 結城浩
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2004/06/19
- メディア: 大型本
- 購入: 38人 クリック: 630回
- この商品を含むブログ (377件) を見る
さらに、Mooseの使い方が詳しく載っているid:lestrratさんのモダンPerl入門も教科書にしました。本書にもIteratorパターンは載っているのですが、ちょっと高度で私には難しかったので、Mooseの使い方は牧さんのモダンPerl入門、Iteratorの実装方法は結城さんのデザパタ本を参考にしています。
- 作者: 牧大輔
- 出版社/メーカー: 翔泳社
- 発売日: 2009/02/10
- メディア: 大型本
- 購入: 23人 クリック: 461回
- この商品を含むブログ (112件) を見る
その他、id:chaichanPaPaさんの以下の記事も参考にさせていただきました。
コード
Book.pm
package Book; use Moose; has 'name' => ( is => 'rw' ); no Moose; sub BUILDARGS { my ($self, $name) = @_; return { name => $name }; } sub getName { my $self = shift; $self->name; } 1;
BookShelf.pm
package BookShelf; use Moose; use BookShelfIterator; with 'Aggregate'; has 'books' => ( is => 'rw' ); has 'last' => ( is => 'rw' ); no Moose; sub BUILDARGS { my $self = shift; return { books => [], last => 0 }; } sub getBookAt { my ($self, $index) = @_; $self->books->[$index]; } sub appendBook { my ($self, $book) = @_; $self->books->[$self->last] = $book; $self->{last}++; } sub getLength { my $self = shift; return $self->last; } sub iterator { my $self = shift; BookShelfIterator->new($self); } 1;
BookShelfIterator.pm
package BookShelfIterator; use Moose; with 'Iterator'; has 'bookShelf' => ( is => 'rw' ); has 'index' => ( is => 'rw' ); no Moose; sub BUILDARGS { my ($self, $bookShelf) = @_; return { bookShelf => $bookShelf, index => 0 }; } sub hasNext { my $self = shift; if ($self->index < $self->bookShelf->getLength ) { return 1; } else { return (); } } sub next { my $self = shift; my $book = $self->bookShelf->getBookAt($self->index); $self->{index}++; return $book; } 1;
main.pl
use strict; use warnings; use Perl6::Say; use BookShelf; use Book; my $bookShelf = BookShelf->new; $bookShelf->appendBook(Book->new('Around the World in 80 Days')); $bookShelf->appendBook(Book->new('Bible')); $bookShelf->appendBook(Book->new('Cinderella')); $bookShelf->appendBook(Book->new('Daddy-Long-Legs')); my $it = $bookShelf->iterator; while ($it->hasNext) { my $book = $it->next; say $book->getName; }
感想
ぶっちゃけ、これを写生しただけではよくわかりませんでした。なんで抽象メソッド化すると幸せなのか?とかしっくりとはわかりません。
型チェックとか、アクセス指定(privateとかpublic)は何も考えてません。
あと、Mooseでコンストラクタを書く時に結構ハマりました。BUILDARGSのとこです。newの引数をハッシュにすれば悩む必要はないみたいなんですが、引数に値を直接渡す時にはMooseじゃない方がむしろ簡単なのかもと思いました。たぶん慣れの問題だとは思いますけど。
こんな感じで結城さんのデザパタ本を進めていくと少しはオブジェクト指向の設計がわかってくるのかなー。。。それとも、第三者にコードを利用してもらうことを前提としない限り、オブジェクト指向の設計の勘所はわからないのかしら。
オブジェクト指向のプログラミングって、自分だけが利用するプログラムを書くことがほとんどなアマグラマーには、無用の長物のような気がしないではないですが、自分の書いたコードが納得できないんだから勉強しなきゃはじまらないですね。
追伸
ついでに、これを機にgithubのアカウントを取ってみたり。このサンプルコードを上げてあります。今後有効活用できるといいなと思っています。
でも、修正の履歴が残るのはなんだか恥ずかしいですね。。。