YAMAGUCHI::weblog

海水パンツとゴーグルで、巨万の富を築きました。カリブの怪物、フリーアルバイター瞳です。

Erlang R14Bのビルド

はじめに

こんにちは、エビオス飲んでますか?最近 id:kuenishi とか id:VoluntasSkypeチャットでErlangでヒャッハーしてる話を横目に見てて、さらに @shibukawa までもがProject EularをErlangで解きまくってるという状況で「俺もヒャッハーしたい!」と思ったのでした。

R14Bをインストール

執筆時はR14Bが最新だったのでインストールします。公式からtarball引っ張ってこようとおもったらメチャ重かったのでid:Voluntasのミラー使いましたよ。ありがとうございます!

    • 公式の
    • Vの兄貴のミラー

とりあえずINSTALL.mdに書いてある通りにビルドしてみる

$ export LANG=C
$ export PATH=/usr/bin:$PATH
$ gcc -v
Using built-in specs.
Target: i686-apple-darwin10
Configured with: /var/tmp/gcc/gcc-5659~1/src/configure --disable-checking --enable-werror --prefix=/usr --mandir=/share/man --enable-languages=c,objc,c++,obj-c++ --program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib --build=i686-apple-darwin10 --program-prefix=i686-apple-darwin10- --host=x86_64-apple-darwin10 --target=i686-apple-darwin10 --with-gxx-include-dir=/include/c++/4.2.1
Thread model: posix
gcc version 4.2.1 (Apple Inc. build 5659)
$ ./configure --prefix=/opt/erlang/R14B --enable-darwin-64bit --without-javac --enable-smp-support --enable-kernel-poll
$ make
$ make install

おし、これで準備ができたのでErlangでHTTP並列アクセスとかしてみようか!

追記

GCCのバージョンが違ってビルドに失敗

「よーし、パパビルドしちゃうぞ」って思ったらこけた

$ make
...
gcc: unrecognized option '-no-cpp-precomp'
cc1: error: unrecognized command line option "-mdynamic-no-pic"
make[2]: *** [obj/i386-apple-darwin10.4.0/opt/plain/hipe_mkliterals.o] Error 1
make[1]: *** [generate] Error 2
make: *** [depend] Error 2

あれ、おかしいなと思ったので見てみたらMacPortsでビルドしたgcc使ってたので、上のようにalias使って回避した。

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/opt/local/libexec/gcc/x86_64-apple-darwin10/4.5.0/lto-wrapper
Target: x86_64-apple-darwin10
Configured with: ../gcc-4.5.0/configure --prefix=/opt/local --build=x86_64-apple-darwin10 --enable-languages=c,c++,objc,obj-c++,fortran,java --libdir=/opt/local/lib/gcc45 --includedir=/opt/local/include/gcc45 --infodir=/opt/local/share/info --mandir=/opt/local/share/man --datarootdir=/opt/local/share/gcc-4.5 --with-local-prefix=/opt/local --with-system-zlib --disable-nls --program-suffix=-mp-4.5 --with-gxx-include-dir=/opt/local/include/gcc45/c++/ --with-gmp=/opt/local --with-mpfr=/opt/local --with-mpc=/opt/local --enable-stage1-checking --disable-multilib --enable-fully-dynamic-string
Thread model: posix
gcc version 4.5.0 (GCC)

file:pwirte/3 と etsを使ってみた

一昨日書いたコードに追記して、結果をファイルに記録してみるようにしてみた。複数のプロセスで1つのファイルに書き込むのもMutexとか使わずに簡単に書けたのでびっくりした。

% 
% grep.erl -- find patterns from multiple files
% 
% usage: 
%      (erl)>grep:main({filename, pattern, [file1, file2, file3, ..])
%
% version 0.2
%
% todo:
%     1. exception processing for invalid arguments.
%     2. launch grep() as a server
%     3. show line number if option is handed to arguments
%


-module(grep).
-export([main/1]).
-compile(export_all).


main({LogFile, Pattern, Files}) ->
    ets:new(tfile, [public, named_table, bag]),
    ets:insert(tfile, {"logfile", LogFile}),

    lists:map(fun(F) -> filep(Pattern,F) end, Files).


filep(Pattern, File) ->
    case file:open(File, [read]) of
        {ok, Fd} ->
            ets:insert(tfile, {Fd, File}),
            spawn(grep, loop, [Pattern, Fd]);
        {error, Reason} ->
            io:format("~s : ~s", [error, Reason])
    end.
	

loop(Pattern, Fd) ->
    case file:read_line(Fd) of
        {ok, Line} -> 
            case re:run(Line, Pattern) of
                {match, _} ->
                    [Head | _] = ets:lookup(tfile, "logfile"),
                    {_, LogF} = Head,
                    {ok, Logd} = file:open(LogF, [append]),

                    [Filet | _] = ets:lookup(tfile, Fd),
                    {_, File} = Filet,
                    Log =  File ++ " -> " ++ Line,

                    {ok, TailPosition} = file:position(Logd, eof),
                    file:pwrite(Logd, TailPosition, Log),
                    file:close(Logd);
                nomatch ->
                    ok
            end,
            loop(Pattern, Fd);
        eof ->
            file:close(Fd);
        _ ->
            ok
    end.

若干気になるのはets:lookup/2では{key, value}のObjectを返してしまうので、いちいちパターンマッチでvalueを取り出してるけど、これはすごくスマートじゃないし、絶対f(key) -> valueってなるような関数があるはずだよなあ。
ご指導願います。

erlang.elでインデントにスペースを使うようにする

Erlangを3日書いたんでいよいよちゃんと環境を整えることにしました。取り急ぎMacでErlangの開発環境を構築する最小ステップを記載します。

MacPortsからErlangの環境をインストール

$ sudo port install erlang

以上。凄く簡単。

Emacsにerlang.elを設定

Erlangの環境を入れるとソースの中にEmacs用のLispファイルが入っています。自分の場合は /opt/local/lib/erlang/lib/tools-2.6.4/emacs/erlang.el にありました。これをEmacsのsite-lispとかにコピー。
そして.emacsに下記を追加。

(require 'erlang-start)
(add-hook 'erlang-mode-hook '(lambda() (setq indent-tabs-mode nil)))

2行目を書かないとインデントがタブになって気持ち悪いので設定。

初めてErlangのコードを書いてみた

きっかけ

自分が何か新しい言語を学ぶときにHello Worldの次にするのは

  • 何かのAPIを叩いてみる(Twitter APIなんかは簡単に試せて良い)
  • Shellコマンドを真似た何かを書いてみる

のどちらかが多いです。今回Python温泉に参加して自分がScalaではまってたときに、隣にいた@Voluntasと@kuenishiが「Erlangいいよ、Erlang」と言って、贅沢にも二人がかりで入門Erlang講座をしてくださいました。せっかくなんで帰りの東海道線でgrep的にファイルから文字列を検索して表示するというようなコードを書いてみました。

コード

こんな感じで書いてみた。

% 
% grep.erl -- find patterns from multiple files
% 
% usage: 
%      (erl)>grep:main({pattern, [file1, file2, file3, ..])
%
% version 0.1
% 
% todo:
%     1. exception processing for invalid arguments.
%     2. launch grep() as a server
%     3. logging results to a file from multiple processes
%     4. show line number if option is handed to arguments
%

-module(grep).
-compile(export_all).


main({P,T}) ->
    lists:map(fun(F) -> filep(P,F) end, T).

filep(P, F) ->
    case file:open(F, [read]) of
        {ok, Fd} ->
            Pid = spawn(grep, loop, [P, Fd]),
                io:format("~p", [Pid]);
        {error, Reason} ->
            io:format("~s : ~s", [error, Reason])
    end.
	

loop(Pattern, Fd) ->
    case file:read_line(Fd) of
        {ok, Line} -> 
            case re:run(Line, Pattern) of
                {match, _} ->
                    io:format("~p : ~s", [Pid, Line]);
                nomatch ->
                    ok
            end,
            loop(Pattern, Fd);
        eof ->
            file:close(Fd);
        _ ->
            ok
    end.

なんかすごく簡単にプロセス(グリーンスレッド)をたてられるのがすごい。このコードでは使ってないけどメッセージパッシングとかも簡単にできちゃう。(Actor Modelなのでがんがんメッセージパッシングするようになっている)
いままでこういう並列処理を前庭とした言語に触れたことがなかったので、いま触っているScalaとあわせて遊んでみたい。しかしScalaはどうしても「Javaを関数型っぽく書く」というのが前面に押し出されている気がするのでその辺を念頭に入れて遊んで行こう。