Hatena::Grouperlang

檜山正幸のErlang未確認情報 RSSフィード

 | 

2009-03-27 (金)

init内のコマンドライン解析

| 14:03

オリジナル。誤解しやすい。

check(<<"-extra">>) -> start_extra_arg;
check(<<"-s">>) -> start_arg;
check(<<"-run">>) -> start_arg2;
check(<<"-eval">>) -> eval_arg;
check(<<"--">>) -> end_args;
check(X) when is_binary(X) ->
    case binary_to_list(X) of
	[$-|_Rest] -> flag;
	_Chars     -> arg			%Even empty atoms
    end;
check(_X) -> arg.				%This should never occur

get_args([B|Bs], As) ->
    case check(B) of
	start_extra_arg -> {reverse(As), [B|Bs]};
	start_arg -> {reverse(As), [B|Bs]};
	start_arg2 -> {reverse(As), [B|Bs]};
	eval_arg -> {reverse(As), [B|Bs]};
	end_args -> {reverse(As), Bs};
	flag -> {reverse(As), [B|Bs]};
	arg ->
	    get_args(Bs, [B|As])
    end;
get_args(, As) -> {reverse(As),}.

意味を取りやすいように修正したヤツ。

is_end_params(<<"--">>) ->
  true;
is_end_params(_Other) ->
  false.

is_flag(X) when is_binary(X) ->
  case binary_to_list(X) of
    [$-|_Rest] -> true;
    _Chars     -> false
  end.

%% get_args/2 を書き換えた:
%% フラグパラメータを取得する
%% {フラグパラメータのリスト, 残余リスト} の形で返す

get_flag_params(Bs) ->
  get_flag_params(Bs, []).

get_flag_params([B|Bs], As) ->
  case is_end_params(B) of
    true -> {reverse(As), Bs};
    _ ->
      case is_flag(B) of
        true -> {reverse(As), [B|Bs]};
        _ ->
          get_flag_params(Bs, [B|As])
      end
  end;
get_flag_params(, As) -> {reverse(As),}.

リストのお尻に追加

| 14:07

%% -*- coding: utf-8 -*-

-module(append).
% -export([]).
-compile(export_all).

-define(TIMES, 100*100*2).

append_1(N, Limit, List) ->
  if (N =:= Limit) ->
      lists:reverse(List);
     true ->
      append_1(N + 1, Limit, [N|List])
  end.

append_2(N, Limit, List) ->
  if (N =:= Limit) ->
      List;
     true ->
      NewList = lists:reverse([N|lists:reverse(List)]),
      append_2(N + 1, Limit, NewList)
  end.

append_3(N, Limit, List) ->
  if (N =:= Limit) ->
      List;
     true ->
      NewList = lists:append(List, [N]),
      append_3(N + 1, Limit, NewList)
  end.

do() ->
  statistics(runtime),
  statistics(wall_clock),
  append_1(0, ?TIMES, []),
  {_, Runtime_1} = statistics(runtime),
  {_, Wall_Clock_1} = statistics(wall_clock),
  io:format("1. ~p (~p) milliseconds~n", [Runtime_1, Wall_Clock_1]),

  statistics(runtime),
  statistics(wall_clock),
  append_2(0, ?TIMES, []),
  {_, Runtime_2} = statistics(runtime),
  {_, Wall_Clock_2} = statistics(wall_clock),
  io:format("2. ~p (~p) milliseconds~n", [Runtime_2, Wall_Clock_2]),

  statistics(runtime),
  statistics(wall_clock),
  append_3(0, ?TIMES, []),
  {_, Runtime_3} = statistics(runtime),
  {_, Wall_Clock_3} = statistics(wall_clock),
  io:format("3. ~p (~p) milliseconds~n", [Runtime_3, Wall_Clock_3]).

3> c(append).
{ok,append}
4> append:do().
1. 10 (10) milliseconds
2. 10785 (15382) milliseconds
3. 7631 (10255) milliseconds
ok
5> append:do().
1. 10 (10) milliseconds
2. 10706 (14862) milliseconds
3. 5988 (8182) milliseconds
ok
6> append:do().
1. 0 (0) milliseconds
2. 9274 (13560) milliseconds
3. 5127 (6719) milliseconds
ok
7> append:do().
1. 0 (0) milliseconds
2. 9113 (13600) milliseconds
3. 7371 (10405) milliseconds
ok
8>

consで先頭に足していって一気にreverse、これは速い。毎回reverseを2回するとさすがに遅い。reverseはできるだけまとめてやるべき(当たり前だ)。lists:append/2もそんなには速くないなー、reverseを2回よりはマシだが。

更新が辛い、辛すぎる

| 15:13

データ構造の一部分をどんどん書き換えていくような操作が非常に辛いな。Erlangの普通のデータはイミュータブルだから、どんなに小さく書き換えても、データ全体を作り直さなくてはならない。パフォーマンスもさることながら、再構築のアルゴリズムもかなり面倒になる。特に入れ子のデータは扱いにくい。ポインタがないから、子から親を指すことができないし、循環的な構造も作れないし。

ポインタと破壊的代入ってなんて便利なんでしょ、、、、

[追記]まー、イミュータブルの宿命だと思ってあきらめよう。1バイト書き換えるのにも、メモリのなかじゃ大騒動![/追記]

[追記]ザッと書いてみた感じでは、これはもう死にそうに遅いだろう。実用になるのかいな? 使えても心理的に耐え難い、つうほどにトンデモナイ作業をしている。あーーー、このへんでErlangは嫌われそうだな。僕は我慢するけど。[/追記]

jj1bdxjj1bdx2009/03/28 11:33あまり細かいオペレーションは,別の言語で外部に出してしまう,というのが得策かもしれませんね.

m-hiyamam-hiyama2009/03/28 12:33jj1bdxさん、
> 別の言語で
そうですね。どうせ必要そうなので、リンクインドライバの書き方を習おうかと思ってます。それとは別に、Erlangによる非破壊的操作のノウハウも必要ですね。パフォーマンスが深刻じゃない状況では、Erlangで書く方がさすがに容易ですから。

 |