Hatena::Grouperlang

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

2010-01-13 (水)

lists:merge, lists:umerge にはソート済みリストを渡す

| 10:18

listsモジュールにある merge って名前が付く関数は、引数は "must be sorted prior to evaluating this function. " とマニュアルに書いてある。

それを守らなかった僕が悪いのです、ハイッ。

VoluntasVoluntas2010/01/13 11:57Erlang 自体のソースコードも列挙系が多いですよね。読みやすさと言うよりは速度の面もあるのかもしれません。
if の使いどころが未だによくわかってないです :-P

m-hiyamam-hiyama2010/01/13 12:07Voluntasさん、
> 読みやすさと言うよりは速度の面もあるのかもしれません。
定数列挙ならジャンプテーブルのような最適化が出来そうですね。パターンマッチの多方向分岐でも最適化手法があるんかしら?
> if の使いどころが未だによくわかってないです :-P
ifはなんだかワカランですね。whenガードと同じ感じの条件で多方向分岐ってことでしょうが、そんなん、あんまり出てこないし。

2009-04-17 (金)

デバッガー関連のコマンド(関数)

| 11:10

シェルからモジュールi(interpretのi)の関数を実行できる。i: は不要で、直接呼べる(コマンド)。

  1. im() -> pid()
  2. ii(AbsModules) -> ok
  3. ii(AbsModule) -> {module, Module} | error
  4. nini(AbsModules) -> ok
  5. ini(AbsModule) -> {module, Module} | error
  6. iq(AbsModule) -> ok
  7. inq(AbsModule) -> ok
  8. il() -> ok
  9. ip() -> ok
  10. ic() -> ok
  11. iaa(Flags) -> true
  12. iaa(Flags, Function) -> true
  13. ist(Flag) -> true
  14. ia(Pid) -> ok | no_proc
  15. ia(X,Y,Z) -> ok | no_proc
  16. ia(Pid, Function) -> ok | no_proc
  17. ia(X,Y,Z, Function) -> ok | no_proc
  18. ib(Module, Line) -> ok | {error, break_exists}
  19. ib(Module, Name, Arity) -> ok | {error, function_not_found}
  20. ir() -> ok
  21. ir(Module) -> ok
  22. ir(Module, Line) -> ok
  23. ir(Module, Name, Arity) -> ok | {error, function_not_found}
  24. ibd(Module, Line) -> ok
  25. ibe(Module, Line) -> ok
  26. iba(Module, Line, Action) -> ok
  27. ibc(Module, Line, Function) -> ok
  28. ipb() -> ok
  29. ipb(Module) -> ok
  30. iv() ->

随分イッパイある。しかし、名前が短すぎるよ。憶えるヒント。

im Monitor
ii Interpret
ini Net Interpret
iq Quit
inq Net Quit
il List modules
ip Processes
ic Clear information
iaa Set Attaching
ist Set Saving
ia Attach
ib Breakpoint
ir Remove breakpoint
ibd Breakpoint Disable
ibe Breakpoint Enable
iba Breakpoint Action
ibc Breakpoint Condition
ipb Print Breakpoints
iv Version

2009-04-16 (木)

ETSのテーブルの種類

| 16:40

ETSテーブルは:

  1. セット型
  2. 順序付きセット型
  3. バッグ型
  4. 重複可能バッグ型

がある。この分類が分かりにくい。なんで分かりにくいんか? と考えたら、対称性が欠けているからだろう、と思った。

どんなコレクションであれ、コンピュータでは列(リスト)になる。順序がないとか言っても有るよ、必ず! で、テーブルつうかマップの場合、データのリストとキーのリストがある。それで分類して:

重複なし 重複を許す
キーリスト セット型 バッグ型
データリスト - 重複可能バッグ型

というわけで、2×2分類で一箇所が無意味で3種となる。重複のチェックをするとパフォーマンスは落ちる。

順序付きセット型とは、そもそも違うデータ構造を使って、セット型が常に一定の順序で並んでいることを保証する。これを入れて4種ってことね。

2009-04-04 (土)

JSON in Erlnag

| 15:22

本編に以前書いたけど、繰り返す。

  1. YAWS付属のjson.erl (http://yaws.hyber.org/
  2. LShft社提供のrfc4627.erl (http://hg.opensource.lshift.net/erlang-rfc4627/
JSON YAWS LShift
true, false, null true, false, null true, false, null
number number() number()
string string() binary()
array {array, element_list()} array()
object {struct, proplist()} {obj, [{key(), value()}]}

// yw_ YAWS版
// ls_ LShift版

number() = integer() | float()
element_list() = [yw_value()]
proplist() = [{yw_key(), yw_value()}]
yw_key() = string() | atom()
yw_value() = (true | false | null) | 
             number() |
             string() |
             {array, element_list()} |
             {struct, proplist()}

array() = [ls_value()]
ls_key() = binary() | atom() 
ls_value() = (true | false | null) |
             number() |
             binary() | 
             array() | 
             {obj, [{ls_key(), ls_value()}]}

Erlangに標準で入ったら追加しよう。

2009-03-27 (金)

リストのお尻に追加

| 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回よりはマシだが。

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

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

2009-03-26 (木)

Erlangデバッガ

| 15:29

使い方の説明は、PDF文書 http://www.erlang.org/doc/pdf/debugger.pdf が便利。ただし、P.18までで十分。P.19以降はmanページと同じだから、http://www.erlang.org/doc/apps/debugger/ とか http://www.erlang.org/doc/man/ を見ても同じ。

kernelの動きを追えるかと思ったがそりゃ無理だった。けっこう高水準な所でデバッグ専用インタプリタエミュレータとは別)が動いている模様。まー、デバッガ使うこともあるだろうから知っておいて損はない。本編に書くかも知れないが、今日はメモだけ。

  1. コンパイル時に debug_infoオプションがないとダメ。
  2. .beamファイルと.erlファイルの両方にアクセス可能じゃないとダメ。
  3. im()またはdebugger:start()で起動。iモジュールシェルインポートされている。
  4. GUI以外に、i, intモジュール関数インターフェースを提供する。
  5. 使うウィンドウは、Monitor(メイン)、ViewModule(ソースコード表示)、Attach Process(個々のプロセスと接続)の3種。以下、ウィンドウ名をブラケットに入れて[Monitor]などと記す。

手順:

  1. EShellからデバッガ起動。[Monitor]が出る。
  2. Module > Interpret でモジュール(.erl, .beam)を選択・追加。デバッグ対象は、モジュール群。
  3. [Monitor]の左にモジュールリスト、主領域にプロセスグリッド
  4. モジュールリストのモジュールエントリーダブルクリックで[ViewModule]
  5. ソース行でダブルクリック。その行にブレークポイントを設定できる。
  6. EShellで普通に操作。プロセスブレークポイントに達すると当該プロセスが停止する。
  7. [Monitor]のプロセスグリッドの当該の行をダブルクリックすると[Attach Process]ウィンドウが出る。
  8. [Attach Process]のボタンを使って実行を制御できる。

GUIGSで作ってあるからショボイし、ウィンドウが何枚も出て使いにくい。とはいえ、必要な機能は揃っているかな。デバッグよりは、既存プログラムの動作や構造の理解と確認に使おうと思ったが、前述のごとく、kernelとstdlibとデバッガ自身は対象外(ムーッ、他の方法を考えよう)。

個人的な好みでは、GUIよりはコマンドラインのほうがいいけどな。-- Emacsから使えれば、せめてEShellと統合されてれば、もっと日常的に使うんだが、、、ウィンドウを移動する手間がカナワン!

普通の用途ではフツーに使えるだろうから、開発中はdebug_info付けておくとよいだろう。Emakefile、Makefileとかに書いて。ただし、debug_info付きのbeamからソースを再現可能。

ブレイクポイントはソース行以外に、関数ブレイクポイントというのがある。また、条件付きブレイクポイントつうのが使いでがありそうだ。ブレイクポイントにC-モジュール(C-Module)のC-関数(C-Function)を指定する。ここで「C」はConditionのこと。

C-関数引数(1個)にはBindingsが入る。これは、なんちゃってのアレみたいだよ。オフィシャルには、int:get_bindig(Name, Bindings) -> {value, Val} | unbound しかサポートされてない。

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

%% @doc 条件付きブレイクポイントのテスト
-module(c_test).

-export([print_x/1]).

%% @doc 変数Xの値を表示する
%% @spec (Bindings) -> bool()
print_x(Bindings) ->
  io:format("~p~n", [int:get_binding('X', Bindings)]),
  true.

こういう関数を条件として指定する。戻り値がtrueならブレイクして、falseならそのまま続行する。

あと、Windows上で右クリックは効かないようだ。

foldlとfoldr

| 16:45

「Before/Afterリスト方式によるストリーム風処理」に書いたように、リストの順番が逆転する現象がよく起こる。僕は順番関係が非常に苦手。当然にfoldlとfoldrの区別も分からなくなる。毎回、短いサンプルを書く。

7> lists:foldl(fun(X, A)->[X|A] end, [], [1, 2, 3]).
[3,2,1]
8> lists:foldr(fun(X, A)->[X|A] end, [], [1, 2, 3]).
[1,2,3]
9> 

ってことは(と、ここでまた混乱している)。'l'が左から見ていくのか(なにしろ'l'だからな)。だが、consで積み上げていくときは、'r'を使えばreverseしなくて済む、と。

どうせ忘れるけど。

2009-03-25 (水)

ファイルIO

| 13:56

file:consult(Filename) と file:read_file(Filename) & file:write_file(Filename, IoData::iodate()) でガサッと読み書きばっかりで、普通のファイルIOをサッパリ使わない。これはイカン。と思うので普通のファイルIOをオベンキョ。

  1. 開く file:open(Filename, Modes) -> {ok, IoDevice} | {error, Reason}
  2. 読む file:read(IoDevice, Number) -> {ok, Data} | eof | {error, Reason}
  3. 書く file:write(IoDevice, iodata()) -> ok | {error, Reason}
  4. 閉じる file:close(IoDevice) -> ok | {error, Reason}

ok/error方式じゃぁー、まっ、しょうがない。modeは、

  • read | write | append | raw | binary | {delayed_write, Size, Delay} | delayed_write | {read_ahead, Size} | read_ahead | compressed | {encoding, Encoding}

だいたい見当は付くだろうが、

  1. writeモード/appendモードでは、ないファイルは作られる。
  2. writeモードで開けると、既存ファイルはゼロに切りつめられる。
  3. パフォーマンスが深刻に問題ならraw
  4. compressedモードで gzip compressed files が扱える。
  5. {encoding, Encoding} で文字エンコーディングを指定できる(ホーッ)。

使える文字エンコーディングは、

  1. latin1
  2. unicode or utf8
  3. utf16 or {utf16,big}
  4. {utf16,little}
  5. utf32 or {utf32,big}
  6. {utf32,little}

つうことは、ビッグエンディアンデフォルトネットワークバイトオーダーだからだろう、たぶん。明示的に、big, little付けたほうがいいな。UTF-*が使えるから、まーいいとしよう。が、ユニコード対応、あまり信用はできないな。

file:readの第2引数は読む個数だが、テキストモードなら文字数、バイナリモードならバイトサイズ。戻り値も、モードにより文字リスト[int()]とbinary()の違いがある。

file:writeで書き込めるデータは:

iodata() = iolist() | binary()
iolist() = [char() | binary() | iolist()]

次はioモジュール。こちらはずっと高水準で、Erlangタームのレベルで読み書きする。

  1. io:read([IoDevice,] Prompt) -> Result
  2. io:write([IoDevice,] Term) -> ok
  3. Prompt = atom() | string()
  4. Result = {ok, Term} | eof | {error, ErrorInfo}

IoDeviceは省略可能で、省略すれば標準入出力。だが、Promptは省略できない、注意。

エラーはこんな感じ。

 {error,{1,erl_parse,["syntax error before: ",["'*'"}}

他は、行単位の入力か。

  • io:get_line([IoDevice,] Prompt) -> string() | eof | {error,Reason}

これは常識的な動きをする。io:formatとio:fwriteはお馴染み。

  • io:fwrite([IoDevice,] Format, DataList) -> ok

最後に、テキストファイルをテキスト行(文字列)のリストとして返すだけのアホ・サンプル:

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

%% @doc ファイルIOのサンプル(単なるサンプル!)
(module(read_lines).
(export([read_all_lines/1, do/1]).

%% @doc ファイルの行をリストにして返す.
%% @spec (string()) -> [string()]
%% @throws Error
read_all_lines(Filename) ->
  IoDev = case file:open(Filename, [read]) of
            {ok, IoD} ->
              IoD;
            {error, Reason} -> 
              throw(Reason)
          end,
  Lines = do_read(IoDev, []),
  file:close(IoDev),
  Lines.

%% @spec (IoDev, [string()]) -> [string()]
%% @throws Error
do_read(IoDev, Read) ->
  case io:get_line(IoDev, "") of
    eof ->
      lists:reverse(Read);
    {error,Reason} -> 
      throw(Reason);
    Line ->
      do_read(IoDev, [Line|Read])
  end.

%% @doc テスト用別名
%% @spec (string()) -> [string()]
%% @throws Error
do(Filename) ->
  read_all_lines(Filename).

group, user

| 15:21

kernelにあるgroup.erlとuser.erl読まないとダメかな?

2009-03-21 (土)

Bindingsは、なんちゃって抽象データ型

| 15:54

erl_eval.erlを見ると、Bindingsの実体はordditcだった。ただし、隠蔽された抽象データ型として扱って欲しいらしい。

new_bindings() -> orddict:new().

bindings(Bs) -> orddict:to_list(Bs).

binding(Name, Bs) ->
    case orddict:find(Name, Bs) of
	{ok,Val} -> {value,Val};
	error -> unbound
    end.

add_binding(Name, Val, Bs) -> orddict:store(Name, Val, Bs).

del_binding(Name, Bs) -> orddict:erase(Name, Bs).

add_bindings(Bs1, Bs2) ->
    foldl(fun ({Name,Val}, Bs) -> orddict:store(Name, Val, Bs) end,
	  Bs2, orddict:to_list(Bs1)).

merge_bindings(Bs1, Bs2) ->
    foldl(fun ({Name,Val}, Bs) ->
		  case orddict:find(Name, Bs) of
		      {ok,Val} -> Bs;		%Already with SAME value
		      {ok,V1} -> 
			  erlang:raise(error, {badmatch,V1}, stacktrace());
		      error -> orddict:store(Name, Val, Bs)
		  end end,
	  Bs2, orddict:to_list(Bs1)).

それぞれの関数の最後の引数には、new_bindings/0 が返したorddictが入るようだ。BindingStructとは、new_bindigsが返した、(気持ちとしては)隠蔽されたデータへのハンドルのことらしい(おそらく)。