スキップしてメイン コンテンツに移動

投稿

仕事で使わない Perl コーディングテクニック

Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it? -- Brian Kernighan(まずもって、デバッグがプログラミングの倍も難しいことは衆知である。だからもしあなたが能う限りの知力でコードを書いたとしたら、どうやってデバッグするのか?)言語のマイナ機能とか関数のデフォルトの挙動なんかを駆使してプログラムを書くと未来の自分や同僚が困るので、普段はいくらか制限して書くと思う。 僕は Perl 5 だとライブラリはともかくアプリケーションのコードは「続・初めてのPerl (Intermediate Perl)」程度の知識を前提して書いていた。要するに意図的に低俗なコードを書いているわけで面白くない。 だいたい頭の内にあるボンヤリとした「Perl 的な書き方」が実際に動くのがこの言語の良いところで、昔2ちゃんねるで見かけた「キモいが矛盾していない、それが Perl」というコメントはけだし慧眼だったと思う。そんなわけでマイナであったり環境の都合で自制していた言語機能をいくつか紹介したい。メソッドの事前解決Perl 5 のメソッド呼出しは遅い。まずもってメソッドの解決が遅い。繰り返し同じメソッドを呼ぶときはメソッドを一度だけ解決してそれを直接呼び出したい。要するに Objective-C で selector を取得してそれを実行するようなことをやりたい。名前のせいで述語と勘違いしがちだが UNIVERSAL::can はまさにこのメソッド解決を行う。例えば my $do_foo = $obj->can('do_foo'); とすれば $obj が属するパッケージないしその親クラスに存在する do_foo メソッドへのリファレンスが得られる。存在しない場合は undef である。そうとなれば後は簡単で、メソッドは第一引数にインスタンスないしクラス名を受けるただのサブルーチンなので、$do_foo->($obj, ...) と呼び出せば動くことは動…
最近の投稿

MacPorts の GHC が macOS Sierra でビルドできない件の回避策 (i.e. MacPorts のパッチの当て方)

多分じきに修正されて腐る内容だが、MacPorts の port にパッチする方法としてまとめておく。会社を辞めたので久方ぶりにブログを書こうと思い、pandoc をインストールしようとしたところ GHC のビルドがコケた。 ログをみると GetTime.c というファイルのコンパイルに失敗している。MacPorts の Trac を検索してみると果たして既知の問題だった。「Haskell 処理系とそれに依存する全パッケージがインストールできない」問題が四ヶ月前に報告されていまだに直っていないという大らかさ。幸いチケット内にパッチが提案されていたのでこれを適用してことなきを得た。まずローカルに GHC の port をコピーし、MacPorts の ports tree として登録する:$ sudo -i Password: # mkdir -p /usr/local/ports/lang/ && cd /usr/local/ports/lang/ # cp -r $(port dir ghc) ./ # cd ghc && port lint # cp /opt/local/etc/macports/sources.conf{,.orig} # vi /opt/local/etc/macports/sources.conf # 作成したディレクトリをデフォルトの ports tree の前に追記する # diff -u /opt/local/etc/macports/sources.conf{.orig,} --- /opt/local/etc/macports/sources.conf.orig 2017-02-10 14:10:00.000000000 +0900 +++ /opt/local/etc/macports/sources.conf 2017-02-10 14:11:04.000000000 +0900 @@ -29,4 +29,5 @@ # sites, etc.), the primary MacPorts source must always be tagged # "[default]", even if switched from the default &q…

libcoro で並行処理プログラムを書く

libcoro という C のライブラリがある。Perl Mongers にはおなじみ (だった) 協調スレッド実装である Coro.pm のバックエンドとして使われているライブラリで、作者は Coro と同じく Marc Lehmann 氏。coro というのは Coroutine (コルーチン) の略で、要するに処理の進行を明示的に中断して別の実行コンテキストに切り替えたり、そこからさらに再開できる機構のことである。言語やプラットフォームによって Fiber と呼ばれるものとほぼ同義。(ネイティヴ) スレッドとの違いはとどのつまり並行処理と並列処理の違いで、スレッドは同時に複数の実行コンテキストが進行し得るがコルーチンはある時点では複数の実行コンテキストのうち高々一つだけが実行され得る。 スレッドに対するコルーチンの利点は主に理解のし易さにある。スレッドの実行中断と再開は予測不可能なタイミングで起こるため、メモリその他の共有資源へのアクセスが常に競合し得る。一方コルーチンは自発的に実行を中断するまでプロセスの資源を独占しているため、コンテキスト・スイッチをまたがない限り共有資源の排他制御や同期などを考えなくて良い。同時に一つのコルーチンしか実行されないということは、プロセッサのコア数に対して処理がスケールアウトしないことを意味する。ただしシングルスレッドのプログラムでも IO などの間はプロセッサが遊んでいるため、非同期 IO とコルーチンを組み合わせるなどして待ち時間に別の処理を行わせることで効率を高められることが多い。 また1コアでの性能に関しては、コンテキスト・スイッチの回数が減り、またスイッチング自体もユーザモードで完結するため、スレッドよりも高速である場合が多い。このため「軽量スレッド」とも呼ばれることがある。libcoro の特徴C で利用できるコルーチン実装は複数あって、Wikipedia にある Coroutine の記事 を見ても片手では足りない数が挙げられている。libcoro がその中でどう特徴付けられるかというとポータビリティが挙げられる。実装のバックエンドは Windows の Fiber や POSIX の ucontext の他、setjmp/longjmp に pthread 果てはアセンブラによる実装が選択でき、API は共通…

Algorithm::LibLinear Tutorial

About this articleThis article is meant to be an introduction guide of Algorithm::LibLinear, a Perl binding to the famous LIBLINEAR machine learning toolkit.I've once written an article titled "Algorithm::LibLinear の紹介" ("Introduction to Algorithm::LibLinear,") in Japanese. Today, although some part of the article is outdated, Blogger's access analytics reported me that the article is still popular, and fairly large number of visitors are from English-speaking country. Thus I guessed I should prepare an updated tutorial in English.Notice that what I try to describe here is library usage, not a machine learning methodology. If you are new to machine learning, I recommend to read a practical guide by Chih-Wei Hsu, et al and try LIBSVM/LIBLINEAR using CLI commands at first.As you might see my English skill is not so great. Please don't hesitate to point/correct unclear part of this article and help me to fix it.InstallationAlgorithm::LibLinear is an XS lib…

Perl 6 Language Documentation を読む - Sets, Bags and Mixes

この記事について先日の稿で Perl 6 の解説を書こうと思っているなどと書いて言質を振り出してしまったので果たさねばならない。 Perl 6 の言語自体を解説するアップデートされた日本語資料は大変乏しいので、とりあえず Perl 6 Documentation を読んで紹介するだけでもいくらか価値があるだろうと思う。率直に言って僕の英語の読解力は甚だ貧しいので、近頃話題になった「腐った翻訳」になるのを避けるためにドキュメントを読んでから試行した結果に基いて解説を書くことにする。 翻訳ではないので文章自体は元の文書とは対応しないが、とにかく Rakudo Perl 6 上での挙動としては正しい解説になるはずである。読む順番は決めていないが、簡単そうな文書の中で面白そうなものから進めていきたい。本文の前書き近頃の言語は集合型が必要なことになっている。厳密にいえば数学的な集合というより、順序付けられておらず要素が重複しないコンテナがプログラムの構成部品として便利だということである。Ruby も Python も Swift も、C++ の STL にだって (それが本当に集合と呼べるかは別にして) [Ss]et がある。Sets, Bags, and Mixes は Perl 5 には標準で備わっていなかった集合の操作をサポートする Perl 6 のクラスとロール群について解説している。Set は単純な集合、Bag は重複可能な集合 (つまり multi set) で、Mix は要素に重みを持たせた集合を表す。 これらは一種の連想配列で、つまり要素に対して Set はそれが自身の要素かという真偽値、Bag はその要素をいくつ持っているかという整数値、そして Mixはその要素に割り当てられた重みを実数値で対応させる。真偽値を 0/1 と対応させれば Set の一般化が Bag だし、さらにその一般化が Mix とみなせる。Bag や Mix を使えば重みに応じて確率的に要素をサンプリングするような処理も可能なため、各種のアルゴリズムやビジネスロジックの記述にも便利だろう。Set、Bag そして Mix はすべて不変クラスである。対応する可変クラスとして SetHashBagHash そして MixHash というクラスが存在する。型システム上は不変クラスも可変クラスも…

YAPC::Asia 2015 の感想文

これまでブログではデスマス調を用いていたけれども、推敲が要って面倒に思ったのでときどきはデアル調で書くことにする。YAPC::Asia Tokyo 2015 が終わった。既に終了から二ヶ月が経っているが「ブログに書くまでが YAPC」とのことで、これには期限が指定されていないので結局書かなかった昨年の分もまとめて終わらせても良かろうとて本稿を書いている。 目を開けねばいつまでも不思議の国にあるような感じで、ブログを書かねばいつまでもお祭りナノダとブンガク的怠惰的主張を述べようかとも思ったけれど、アラサー男がアリスなど引用しても薄ら寒いばかりなので止めにする。落とし物私的には今回最大の事件は前夜祭のちの二次会みたいなものに向かう途中の電車でマネークリップを落としたことだった。結局翌日 JR に4度ほど問合せたところ何故か渋谷駅に現金も手つかずで届けられていてことなきを得た (オモテナシはあながちでまかせとも言えないことが分かった) が、クレジットカードを再発行したために (僕はカードを一枚しか持っていないので) 一週間ほどオンラインの決済ができなかったり大変に生活に支障があった。これまでの YAPC とのかかわり (フリーライダとして)東京に越してきてからは毎年 YAPC::Asia に参加していた。特に熱心だったわけではなく、勤務先がスポンサーなのでチケットがただで手に入るし平日は業務扱いで参加できたからである。 2012年だったか岡山の院生だったときに旅費の補助があるというので一度 Lightning Talk (LT) をしたことがあるが、そのときを別にすれば見る方ばかりの参加だった。 ちなみにそのときの LT (動画が YouTube に上がっているが見たくないのでリンクもしない。スライドは以前のブログ記事のどこかにある) は要するに研究室の愚痴で、いかにも Perl 4 然としたコードを矯正するといった趣旨だった。今年のトークについてRakudo 及び MoarVM 開発者の Jonathan Worthington 氏の "Parallelism, Concurrency, and Asynchrony in Perl 6" は Perl 6 の {並列, 平行, 非同期} 処理機能の紹介で大変に面白かった。二日目は昼過ぎから会場に向…

Algorithm::LibLinear アップデート

LIBLINEAR の Perl バインディングである Algorithm::LibLinear ですが、以前の記事で紹介した時点ではバージョン 0.04 だったのが現在では 0.10 になっており、非互換な変更もあったので当該記事のサンプルコードも最早動かなくなっています。 案外と今でもアクセスがある記事なので、現在までの変更を補足します。LIBLINEAR 1.94 ベースになったAlgorithm::LibLinear 0.08 より LIBLINEAR 1.94 をバンドルしています。配布サイトには内部処理のどうでもいい変更点しか載っていませんがメモリリークの修正が入っています。機能的には同一です。API の非互換な変更Algorithm::LibLinear::ScalingParameter クラスはバージョン 0.07 で非推奨になり、0.09 で廃止されました。同時に Algorithm::LibLinear::DataSet の scale() メソッドも削除されました。 今後は新しい Algorithm::LibLiner::FeatureScaling を使ってください。ScalingParameter は設計ミスで、テストデータをスケーリングする場合に極めて周りくどい記述が必要でした:use Algorithm::LibLinear::DataSet; use Algorithm::LibLinear::ScalingParameter; my $scaling_parameter = Algorithm::LibLienar::ScalingParameter->new(...); my $feature = +{ attr1 => 0.2, attr2 => 1, ...}; # データ1個の DataSet を作って scale し配列として取り出した最初の要素の feature... my $scaled_feature = Algorithm::LibLinear::DataSet->new( data_set => [ +{ feature => \%feature, label => 42 } ], )->scale(parameter => $scaling_p…

Algorithm::SVM の注意点

要旨Algorithm::SVM は極めて有用だけど API がなんか変なので注意が必要。詳説CPAN に Algorithm::SVM1 というモジュールがあります。これ Support Vector Machine (SVM)2 を提供する LIBSVM3 という有名なライブラリの Perl バインディングなのですが、なんか API に癖があるので注意点を解説します。まず使い方を簡単に紹介します:use strict; use warnings; use Algorithm::SVM; my @data_set; while (<DATA>) { chomp; my ($label, $vector) = split /:\s+/, $_, 2; my @vector = split /,\s+/, $vector; my $data = Algorithm::SVM::DataSet->new( DataSet => \@vector, Label => $label, ); push @data_set, $data; } # 本当はパラメータ調整が要るけど省略。全部デフォルトなのでガウスカーネル利用の C-SVC になる。 my $svm = Algorithm::SVM->new; # 分類器を訓練する。 $svm->train(@data_set); # ラベル1に分類されるべき未知のデータ。 my $test_data = Algorithm::SVM::DataSet->new( DataSet => [ 4.6, 3.2, 1.4, 0.2 ], # ラベルは未知なので仮に 0 とする。単に無視されるので -1 でも 65536 でも 42 でも良い。 Label => 0, ); # 未知データを分類。1 が返るはず。 my $label = $svm->predict($test_data); print "$label\n"; # Iris Data Set (http://archive.ics.uci.edu/ml/datasets/Iris) より一部抜粋の上形式を変更。 # <la…

大規模なデータをそれなりに効率良く計数できる Algorithm::LossyCount を書いた

要旨 Algorithm::LossyCount というモジュールを書きました。これを使うとそこそこメモリ効率良く大規模なデータの計数ができます。アクセスランキング集計とかに使えるんじゃないでしょうか。
GithubMetaCPAN 動機 例えばブログホスティングサービスで HTTP サーバのアクセスログを集計して人気のあるブログ記事ランキングを出したいとします。
Perl でデータの出現頻度を計数するのはハッシュを使うのが鉄板なので、適当に書くとだいたいこんな感じのコードになると思います:
#!/usr/bin/env perl use v5.18; my %access_counts; while (<>) { chomp; my $access_log = parse_access_log($_); next if is_article_request($access_log); ++$access_counts{$access_log->{requested_article}}; } my @popular_articles = ( sort { $b->[1] <=> $a->[1] } map { [ $_ => $access_counts{$_} ] } keys %access_counts )[0 .. 49]; say "Rank\tURL\tFreq."; for my $i (0 .. $#popular_articles) { say join "\t", $i + 1, @{ $popular_articles[$i] }; } sub is_article_request { ... } sub parse_access_log { ... } シンプルですね。
しかしブログの記事数はサービス全体で数千万から数億のオーダになります。一定期間に全記事にアクセスがあるわけではないにしろ、逐次計数していくとハッシュのキーが数千万件になってメモリが貧弱なマシンだと残念なことになります。
ところで Web ページのアクセス傾向に関しては Zipf の法則1が当てはまることが知られています。要するにアク…

京大テキストコーパスのパーサを書いた

要旨CaboCha やなんかの出力形式であるところの京大テキストコーパス形式のパーサモジュールを Perl で書いたので紹介します。GithubTarball on Github Ppagesこれを使うと例えば CaboCha の出力した係り受け関係を Perl のオブジェクトグラフとして取得できます。使用例単なる文節区切りの例。#!/usr/bin/env perl use v5.18; use utf8; use IPC::Open3; use Parse::KyotoUniversityTextCorpus; use Parse::KyotoUniversityTextCorpus::MorphemeParser::MeCab; use Symbol qw//; my ($in, $out, $err); my $pid; BEGIN { ($in, $out, $err) = (Symbol::gensym, Symbol::gensym, Symbol::gensym); $pid = open3($in, $out, $err, cabocha => '-f1'); } END { close $out; close $err; waitpid $pid => 0 if defined $pid; } binmode STDOUT, ':encoding(utf8)'; binmode $in, ':encoding(utf8)'; binmode $out, ':encoding(utf8)'; my $parser = Parse::KyotoUniversityTextCorpus->new( morpheme_parser => Parse::KyotoUniversityTextCorpus::MorphemeParser::MeCab->new, ); say $in '星から出るのに、その子は渡り鳥を使ったんだと思う。'; say $in '出る日の朝、自分の星の片付けをした。'; close $in; my $sentence_trees = $parser->…