Tue, 15 May 2007

erlangでたらい回し: memorize編

酒日記 はてな支店 - Erlangでたらい回し を見て、memorize版を書いてみた。

memorize_tarai(X, Y, _Z) when X =< Y -> Y;
memorize_tarai(X, Y, Z) ->
  case get({X, Y, Z}) of
    undefined -> put({X, Y, Z},
                        memorize_tarai(
                          memorize_tarai(X-1, Y, Z),
                          memorize_tarai(Y-1, Z, X),
                          memorize_tarai(Z-1, X, Y))),
                 get({X, Y, Z});
    A -> A
  end.

プロセスディクショナリを使ってみました。酒日記さんのと同じ測定方法で測定。

bench_memorize_tarai(X, Y, Z) ->
  {Time, Ans} = timer:tc(?MODULE, memorize_tarai, [X, Y, Z]),
      io:format("tak(~p, ~p, ~p) = ~p : time ~p msec~n", [X, Y, Z, Ans, Time/1000]).

結果は、

4> tarai:bench_memorize_tarai(100,50,0).
tak(100, 50, 0) = 100 : time 38.0000 msec

同じ環境で 酒日記 はてな支店 - Erlangでたらい回し 遅延評価版 にあった 遅延評価版も測定してみた。

5> tarai:bench_lazy_tarai(100,50,0).
tak(100, 50, 0) = 100 : time 19.0000 msec

memorize版はlazy版より2倍程度遅いという結果がでました。

ただしmemorize版はプロセスディクショナリに保存するため、 インタラクティブシェルで複数回実行する場合、2回目以降では 計算結果が残っているため、下記のような結果となってしまう。

6> tarai:bench_memorize_tarai(100,50,0).
tak(100, 50, 0) = 100 : time 1.00000e-3 msec

すげーはやっ。


Sat, 12 May 2007

lists:map

lists:mapには第一引数として関数を渡すんだけど、escriptで実行する場合、

  • funはOK
#!/usr/bin/env escript

main(_) -> erlang:display(lists:map(fun (X) -> X end, [1,2,3])).
  • 同じスクリプトファイルに定義した関数はNG
#!/usr/bin/env escript
main(_) -> erlang:display(lists:map(test, [1,2,3])).
test(X) -> X.

実行結果は、:

./test.erl:6: Warning: function test/1 is unused
escript: script failed with error reason {badfun,test}
  • 同じスクリプトファイルに定義した関数をfunから呼べはOK
#!/usr/bin/env escript
main(_) -> erlang:display(lists:map(fun (X) -> test(X) end, [1,2,3])).
test(X) -> X.

なんで直接呼んだら駄目なんだろ?moduleの場合には、下記のようにすればいいと どこかに書いてあった。

-module(hoge).
-export([test/1]).

test(X) -> lists:map(fun erlang:integer_to_list/1, [1,2,3]).

もしくは、同じモジュールファイル内で定義した関数は下記のようにすればいいらしい。

-module(hoge).
-export([test/1]).

test(X) -> lists:map(fun dummy/1, [1,2,3]).
dummy(Y) -> Y.

でも、escript用のスクリプトファイルではこのようにしてもやっぱり駄目みたい。 funを使わずに呼びだすにはどうしたらいいんだろう?


素直に内包表記を使ったらfunなしでもいけるんだけどね。

#!/usr/bin/env escript
main(_) -> erlang:display([test(X) || X <- [1,2,3]]).
test(X) -> X.

and/orやらandalso/orelseについて

ワンライナーを書こうとしていた時に、ひっかかった所。

pythonの感覚だと:

>>> 1 and 2
2
>>> 0 and 2
0
>>> 1 or 2
1
>>> 0 or 2
2

ってのを期待してたんだけど、どうにも動かない。よくよく調べて見ると、 and/orの引数(って言っていいのかな?)は、trueかfalseしか受けつけないようだ。:

> true and true.
true
> true and false.
false
> true or false.
true
> false or true.
true


Continue Reading...: "and/orやらandalso/orelseについて"



再考: fizzbuzz

すごい!パターンマッチングがなんだかすごいerlangっぽく感じる。

erlangでは、listやstring系の関数を使うには「module名:関数名」としないと いけないので、そもそもワンライナーで短かく書くには向いてないかなと 思って、どうすればerlangっぽいのかなと、書いてみた。

ほとんど上記サイトにあるのと一緒ですが。

こんなんとか、

#!/usr/bin/env escript

%fizzbuzz2
main(_) -> erlang:display([fizzbuzz(X) || X <- lists:seq(1, 100)]).

fizzbuzz(X) ->
  case {X rem 3, X rem 5} of
  {0, 0} -> "FizzBuzz";
  {0, _} -> "Fizz";
  {_, 0} -> "Buzz";
  {_, _} -> integer_to_list(X)
end.

こんなんとかどうかな。

#!/usr/bin/env escript

%fizzbuzz3
main(_) -> erlang:display([fizzbuzz(X, X rem 3, X rem 5) || X <- lists:seq(1, 100)]).

fizzbuzz(_, 0, 0) -> get_substr(1);
fizzbuzz(_, 0, _) -> get_substr(1, 4);
fizzbuzz(_, _, 0) -> get_substr(5);

fizzbuzz(X, _, _) -> integer_to_list(X).

get_substr(X) -> string:sub_string("FizzBuzz", X).
get_substr(X, Y) -> string:sub_string("FizzBuzz", X, Y).

Wed, 09 May 2007

うーん、もっと短かくならんかな

どうしてプログラマに・・・プログラムが書けないのか?

% 見やすくするため適当に改行を入れています。

> erlang:display(lists:map(fun (X) -> if X rem 15 == 0 -> "FizzBuzz";
        X rem  3 == 0 -> "Fizz"; X rem 5 == 0 -> "Buzz";
        true -> X end end, lists:seq(1,100))).

出力周りはかなりの手抜きですが、それにしても長い。 きっともっと短かく格好良く書けるに違いない。