erlang分散システム勉強会

参加してみました

たけまる / 第2回 Erlang 分散システム勉強会に参加しました.プログラム見ればわかりますが,分散システムがメインの勉強会なのでとっても難しかったです.LTネタを用意してましたが,eeepcがプロジェクタに接続できないとかのミスをやらかしてしまってすみません.時間も短かったのでここで資料を公開して,ついで解説もしておきます.

資料

最初,こんなコードをmac上で書きました.

2> io:format("~p~n",[[229,175,191]]).
"寿"
ok

寿という字を選んだのは偶然だったのですが,Eshellで表示されたので,utf-8を表示できると思ってしまいました.ところが,

#!/usr/local/bin/escript
main(_) ->
    Item0 = "寿",
    Item1 = "寿限",
    Item2 = "寿限無",
    Item3 = [[229,175,191],[233,153,144],[231,132,161]],
    io:format("~p~n", [Item0]),
    io:format("~p~n", [Item1]),
    io:format("~p~n", [Item2]),
    [io:format("~p~n", [X]) || X <- Item3].

を実行してみると,

"寿"
[229,175,191,233,153,144]
[229,175,191,233,153,144,231,132,161]
"寿"
[233,153,144]
[231,132,161]

となって寿という字だけが特殊だってことがわかりました.そのため,LTで話すネタとして日本語の正規表現を考えていたので大弱りという事態に陥りました.困ったー,どーしよー,と思ったときに思いついたのが正規表現を実行するために使おうと思ったErlang::Portで,これを表示だけにつかってみることにしました.こういうerlanのcode(perlsay.erl)を書いて,

%
% perlsay.erl
%
-module(perlsay).
-export([say/1]).
-export([start/1, stop/0]).
-import(perlport, [call/2, stop/1]).

say(String) ->
  call([say, String], perlsay).

start(Script) ->
  perlport:start(Script, perlsay).

stop() ->
  perlport:stop(perlsay).

これが呼び出すScriptとしてperlsay.plというスクリプトを用意してやれば

#!/usr/local/bin/perl
package Erlang::Port::Say;
use strict;
use warnings;
use Erlang::Port;

caller or __PACKAGE__->main(@ARGV);
1;

sub main {
    my $pkg = shift;
    Erlang::Port->new(
        sub {
            my $obj  = shift;
            my $port = shift;
            my $ret  = eval { _my_proc( $obj, $port ) };
            $ret = $port->_newTuple( [ $port->_newAtom('error') => $@, ] )
              if ($@);
            $ret;
        }
    )->loop();
}

sub _my_proc {
    my $obj  = shift;
    my $port = shift;

    if ( !UNIVERSAL::isa( $obj, 'ARRAY' ) ) {
        return $port->_newTuple( [ $port->_newAtom('badarg'), $obj ] );
    }
    my $key = _to_s( $obj->[0] );
    if ( !defined($key) || $key ne 'say' ) {
        return $port->_newTuple( [ $port->_newAtom('badarg'), $obj ] );
    }
    my $str = _to_s( $obj->[1] );
    if ( !defined($str) ) {
        return $port->_newTuple( [ $port->_newAtom('badarg'), $obj ] );
    }
    print STDERR $str, "\n";
    $str;
}

sub _to_s {
    my $obj = shift;
    if ( defined($obj) && !ref($obj) ) {
        $obj;
    }
    elsif ( $obj && ref($obj) eq 'ARRAY' && @$obj == 0 ) {
        "";
    }
    elsif ( ref($obj) && UNIVERSAL::isa( $obj, 'Erlang::Atom' ) ) {
        $$obj;
    }
    elsif ( ref($obj) && UNIVERSAL::isa( $obj, 'Erlang::Binary' ) ) {
        $$obj;
    }
    else {
        undef;
    }
}

日本語を引数として渡した時にそれを標準エラーに出力するerlangのcodeが書けます.ただし,エラー処理をよくチェックしていないので,もしかしたら間違いがあるかもしれません.使い方は以下のようになります.

#!/usr/local/bin/escript
main(_) ->
    perlsay:start("./perlsay.pl"),
    perlsay:say("寿限無、寿限無、五劫の擦り切れ、海砂利水魚、水行末、雲来末、風来末、食う寝る所に住む所、薮ら柑子のぶら柑子、パイポ、パイポ、パイポのシューリンガン、シューリンガンのグーリンダイ、グーリンダイのポンポコピーのポンポコナーの長久命の長助"),
    perlsay:stop().

このcodeを使うと,Erlangからperlを呼び出して正規表現処理を実行し,その結果をもう一度perlで標準エラーに出すという構成ができます.どんな結果がでるかはわかりますよね.ちなみに,perlre.plはErlang::Portのexampleディレクトリに入っていますが,そのままだとunicodeを処理できません.今回は

12,14d11
< use Encode qw(_utf8_on);
< use Unicode::RecursiveDowngrade;
< use utf8;
19a17
> 
68,69d65
<     _utf8_on($str);
<     _utf8_on($re); 
78,80c74
<     my $rd = Unicode::RecursiveDowngrade->new;
< #     [$str =~ $re];
<       $rd->downgrade([$str =~ $re]);
---
>       [$str =~ $re];

このような改造をして使っています.使い方は発表資料の中にもありますが,

#!/usr/local/bin/escript
main(_) ->
    perlre:start("./perlre.pl"),
    perlsay:start("./perlsay.pl"),
    F = perlre:match("赤とんぼ","(\\p{Hiragana}+)"),
    [perlsay:say(X) || X <- F],
    perlre:stop(),
    perlsay:stop().

のようになります.これを実行すると"とんぼ"と出力します.