Hatena::Grouperlang

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

2010-01-21 (木)

プロセス辞書

| 12:13

プロセス辞書は使うな」はまったく正しいと思う。

破壊的代入による副作用が欲しくなると安着に使うヤツがいる。そういうヤツは、、、、それは僕です。

2010-01-20 (水)

funを使うと

| 18:26

Voluntasさん 2010/01/13 11:58 :

fun() でのパターンマッチはエラーが起こったとき優しくないので使うのは最低限にしておいたほうがよさげです。

おっしゃるとおりですなー。foldr内のfunの例ですが:

{'EXIT',{function_clause,[{lists,foldr,
                                 [#Fun<unify2.1.52405648>,{ok,},{ok,}]},
                          {erl_eval,do_apply,5},
                          {erl_eval,expr,5},
                          {shell,exprs,6},
                          {shell,eval_exprs,6},
                          {shell,eval_loop,3}]}}

1.52405648 とか言われてもどれだかワカラン。

AnkitAnkit2013/08/10 19:32Thknas for taking the time to post. It's lifted the level of debate

YunusYunus2013/08/12 09:26That's an astute answer to a tricky qutesion http://axjgdrgxxw.com [url=http://ixztdk.com]ixztdk[/url] [link=http://wsajds.com]wsajds[/link]

DidikDidik2013/08/14 20:34This "free sharing" of <a href="http://sasafumqch.com">inrtomaoifn</a> seems too good to be true. Like communism.

2010-01-13 (水)

続・久しぶりに使うと -- 後で読めるようにする

| 10:05

リハビリ・サンプルとして、自分で書いたErlangコードを眺めてみたが、意味不明なところがある。それで思ったことを書きます。

僕の習慣:変数名がだいたい大文字1文字。表向き/建前の理由としては; Erlang変数局所変数しかないし単一代入だから、スコープは狭く、値のバインディングを追跡しやすい。だから、短い名前でも大丈夫。

でも、やっぱり1文字はマズイな。純粋な数値計算やリスト処理だと、N(数値のつもり)とかL(リストのつもり)とかで十分なんだけど、背後になんらかのセマンティクスがあるときは、それを示唆する名前を付けないと意味不明。例えば、LじゃなくてPersonListとかね。変数名が長くなる時はアンダスコア区切りよりキャメルケース(ただし、もちろん先頭大文字)のほうがいいような気がする。(先頭大文字はキャメルに見えないから、キャメルケースって言わないのか?)

それと、アルゴリズム書くより(それが可能なら)列挙した方がいい、と思った。例えば、mod 3 の足し算は普通次のように書くわな。

sum_mod3(N, M) ->
    (N + M) rem 3.

関数の定義域を {0, 1, 2} に確実に制限したいなら:

sum_mod3(N, M) when 0=<N, N=<2, 0=<M, M=<2 ->
    (N + M) rem 3.

だけど、次のように書いた方が分かりやすいんじゃねっ。

sum_mod3(0, 0) -> 0;
sum_mod3(0, 1) -> 1;
sum_mod3(0, 2) -> 2;
sum_mod3(1, 0) -> 1;
sum_mod3(1, 1) -> 2;
sum_mod3(1, 2) -> 0;
sum_mod3(2, 0) -> 2;
sum_mod3(2, 1) -> 0;
sum_mod3(2, 2) -> 1.

別な例を挙げると:

max(N, M) when is_integer(N), is_integer(M) ->
    if
	N >= M -> N;
	true -> M
    end.

上のより、次のほうが分かりやすいと感じる。

max(N, M) when is_integer(N), is_integer(M), N >= M -> N;
max(N, M) when is_integer(N), is_integer(M), N <  M -> M.

冗長ではあるけれど、それぞれのケースをズバリそのまま記述しているから具体性が高い。

短い名前も複雑なアルゴリズムも、読む側に推測だの解釈だのという知的努力を要求する。知的努力を要求しない書き方は、冗長、ダサイ、カッコ悪いになるんだけど、トータルなコストを勘案すると、なるべくアホっぽく書いた方がかえって良いみたい、と思った。

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ガードと同じ感じの条件で多方向分岐ってことでしょうが、そんなん、あんまり出てこないし。

2010-01-12 (火)

久しぶりに使うと

| 15:27

すっかりErlangはご無沙汰。ものすごく久しぶりに使った。12月に使っていた機械が壊れたのでインストールからやり直し。おっ、GUIはwxになったんですか、GSより綺麗だ。unicodeってモジュールも入ったが、それで悲惨な日本語処理がどうにかなるかどうかは分からない。

さて、体験したこと感じたこと; 以前は、OSシェルや他のインタプリタ使っていても行末でピリオドを打ってしまって困ったが、今はピリオドを忘れて困る。データが全部イミュータブルだってことを忘れてイライラする。まー、これは以前からイライラしていたか。

一方、ガード付き関数節がたくさん書けること、引数パターンやcase式で場合分けが書けること、リスト処理が比較的充実していること、などはシミジミとありがたい。今はプロセス使う用途がないので、プロセスがありがく感じる状況じゃない。

2009-06-11 (木)

関数節と関数名

| 08:41

fun式の場合

8> (fun (0) -> 1; (1) -> 2; (2) -> 3 end)(1) .
2
9>

と書ける。けっこうスッキリしている。

(0) -> 1; 
(1) -> 2; 
(2) -> 3.

という書き方が、普通の関数定義でも使えるといいな。

succ(0) -> 1; 
    (1) -> 2; 
    (2) -> 3.

とか、

succ
 (0) -> 1; 
 (1) -> 2; 
 (2) -> 3
.

いいね、いいね。でもモチロン使えない(泣)。

[追記]余計なことが書いてないのがいいし、表みたいな感じの関数節をイッパイ書いた後で関数名をリネームするとき便利だと思う。ムキになって無理クリ:

succ(N) -> (fun 
 (0) -> 1; 
 (1) -> 2; 
 (2) -> 3
end)(N).

エラーしたときの関数名が '-succ/1-fun-0-' みたいになってしまうのが難点。[/追記]

[追記の追記] funで囲うより、内側にcaseを入れたほうがスッキリでしょ、って?

succ(N) -> 
case N of
 0 -> 1; 
 1 -> 2; 
 2 -> 3
end.

確かにそうだ。

でもね、関数節で関数名を省略できるのと、caseを入れるのとは違うのだ、微妙に、気分的に。[/追記の追記]

VoluntasVoluntas2010/01/13 11:58fun() でのパターンマッチはエラーが起こったとき優しくないので使うのは最低限にしておいたほうがよさげです。

m-hiyamam-hiyama2010/01/13 12:17Voluntasさん、
funは好きで、すぐ使っちゃうんだけど、http://erlang.g.hatena.ne.jp/m-hiyama/20100113/1263344750 で書いた観点からも、名前付き関数を定義したほうがいいようです。短いならfunのほうがコンパクトで読みやすいですけど。

FreidaFreida2013/08/10 18:12I can't believe you're not playing with meht--at was so helpful.

PamPam2013/08/12 08:02That's an expert answer to an inireesttng question http://uupzlrq.com [url=http://ykmkxb.com]ykmkxb[/url] [link=http://ktbaqg.com]ktbaqg[/link]

HyunwooHyunwoo2013/08/14 19:11I could watch <a href="http://suamipfmjx.com">Scrihdlen's</a> List and still be happy after reading this.

2009-06-10 (水)

便利関数を使ったら不便だった例

| 08:41

「コマンドラインなら1行なのに、、、」に書いたコレ:

% @doc ファイルの種類を返す.
% @spec (string()) -> (not_exist | dir | regular | special)
file_type(Filename) ->
  case filelib:is_file(Filename) of
    false ->
      not_exist;
    true ->
      case filelib:is_dir(Filename) of
        true ->
          dir;
        false ->
          % regularとspecialの区別はあんまり当てにならないみたい
          case filelib:is_regular(Filename) of
            true ->
              regular; 
            false ->
              special
          end
      end
  end.

filelibを使ったためにかえって鬱陶しくなった。read_file_info/1 を直接使ったほうが単純。

-include_lib("kernel/include/file.hrl").

%% 戻り値は file.hrl を参照
file_type(Filename) ->
  case file:read_file_info(Filename) of
    {ok, Info} ->
      Info#file_info.type;
    {error, enoent} ->
      not_exist;
    {error, Reason} ->
      throw(Reason)
  end.

↑これは、裸の値(アトムだけ)かthrow例外で応答する。ok/error方式なら、

file_type_2(Filename) ->
  case file:read_file_info(Filename) of
    {ok, Info} ->
      {ok, Info#file_info.type};
    {error, Reason} ->
      {error, Reason}
  end.

common_errorを使うなら、

 throw({{?MODULE, io_error}, Reason})

とか。

しばらく書いてないと忘れてしまうこと

| 16:58

caseやifの入れ子を使うより、

  1. 引数パターンを使って関数節に分ける
  2. ガードを使って条件を記述する(これも関数節)
  3. あまり複雑にならないなら論理演算子 andalso, orelse を使う

としたほうがたいてい見やすい。

lpgtksgbfplpgtksgbfp2014/01/14 17:18hrffsfsmboh, <a href="http://www.twrxyjffyc.com/">vocqtmovko</a> , [url=http://www.adoypvwbqp.com/]rabrbhjvxx[/url], http://www.mtapblapqb.com/ vocqtmovko

2009-06-09 (火)

引数順序の誤解

| 11:30

lists:nth(List, N) だと思っていたら、lists:nth(N, List) だった。これが原因のバグデバッガを起動したら、デバッガが死んだ。lists:nth(0, List) とかもやりそう。

jj1bdxjj1bdx2009/06/09 18:04Armstrong先生も,関数名に属性を埋め込みたいねえ,という話をしてましたね.ただ現実には難しいだろうなあ.
http://www.erlang.org/cgi-bin/ezmlm-cgi?4:mss:44296:iabfbomcgoelhocacnai

m-hiyamam-hiyama2009/06/10 08:15jj1bdxさん、
名前付き引数のサポートですね。デフォルト値と組み合わせると確かに便利です。が、順序を憶えなくていい代わりに名前を憶える負担が。バランスが難しいですね。
> ただ現実には難しいだろうなあ.
これは無理みたいな、、、

2009-06-02 (火)

コマンドラインなら1行なのに、、、

| 10:58

とあるディレクトリから下に、サイズ1メガ(1024*1024 = 1048576 ≒ 1000000)以上のファイルがあるかどうかを調べる必要があった。

$ find the/dir -type f -size +1000000c -print

で出来た。

が、なぜか(なぜだ?)Erlangでも書いてみようと思って、無駄に丁寧に書いた。あー無駄だ。

100> check_size:traverse("the/dir", fun check_size:check_size/4, [1000000]).

↑で、先のfindコマンドとほぼ同じ。

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

-module(check_size).
-compile(export_all).

% @doc ファイルの種類を返す.
% @spec (string()) -> (not_exit | dir | regular | special)
file_type(Filename) ->
  case filelib:is_file(Filename) of
    false ->
      not_exist;
    true ->
      case filelib:is_dir(Filename) of
        true ->
          dir;
        false ->
          % regularとspecialの区別はあんまり当てにならないみたい
          case filelib:is_regular(Filename) of
            true ->
              regular; 
            false ->
              special
          end
      end
  end.

% @equiv traverse(TopDir, Action, [])
traverse(TopDir, Action) ->
  traverse(TopDir, Action, []).

% @doc 起点となるディレクトリから再帰的にディレクトリツリーをたどり、
% 各ファイルに対して指定されたアクションを実行する.
%
% == アクション関数の仕様 ==
% <dl>
% <dt>第1引数</dt><dd>ディレクトリ名</dd>
% <dt>第2引数</dt><dd>ファイルベース名</dd>
% <dt>第3引数</dt><dd>ファイルのタイプ (not_exit | dir | regular | special)</dd>
% </dl>
% 
% 他に追加の引数をいくつでも付けてよい. 
% 追加引数は、リストにまとめて traverse の第3引数に渡す.


% @spec (string(), function(), [any()]) -> ok
traverse(TopDir, Action, MoreArgs) ->
  {ok, Names} = file:list_dir(TopDir),
  lists:foreach(fun (Name) -> 
                    do_action(TopDir, Name, Action, MoreArgs)
                end,
                Names).

% @doc 関数として渡されたアクションを、
% 指定のディレクトリとファイル名(basename)に対して実行する.
% @spec (string(), string(), function(), [any()]) -> ok
do_action(Dir, Name, Action, MoreArgs) ->
  Filename = filename:join(Dir, Name),
  Type = file_type(Filename),
  Fullargs = [Dir, Name, Type | MoreArgs],
  case Type of
    dir ->
      apply(Action, Fullargs),
      traverse(Filename, Action, MoreArgs);
    _Other ->
      apply(Action, Fullargs),
      ok
  end.

% @doc traverseのアクション関数、ファイルサイズのチェック.
% @spec (string(), string(), atom(), number()) -> ok
check_size(Dir, Name, Type, Limit) ->
  File = filename:join(Dir, Name),
  case Type of
    regular ->
      Size = filelib:file_size(File),
      if
        Size >= Limit ->
          io:format("!! ~s ~p~n", [File, Size]);
        true ->
          ok
      end;
    dir ->
      io:format("[~s]~n", [File]);
    special ->
      ok
  end.

2009-04-22 (水)

やっとわかった桁指定パディング付きフォーマット

| 09:07

printfの"%2d"みたいのが今までできなかった(苦笑)。

100> io:fwrite("~5.8.0B~n", [31]). 
00037
ok
101> io:fwrite("~6.16.0B~n", [31]). 
00001F
ok
102> io:fwrite("~6.10._B~n", [31]). 
____31
ok
103> 

ま、調べりゃすぐ分かることだけど。

  • 「~」 桁数 「.」 基数 「.」 パディング文字 「B」
  • 「~」 桁数 「.」 基数 「B」 (デフォルトは空白)
  • 「~」 桁数 「B」 (デフォルトは基数10;十進数
  • 「~」 「B」 (デフォルトはキッチリ詰める)

なんでBなのか? わからん、謎じゃ。

[追記]パディング文字は~も含めて何でもいいようだが、*はダメ。*は、なんか特殊みたい。[/追記]

2009-04-21 (火)

YAWS for Windows:余計なことを

| 10:20

YAWS for Windowsは、インストーラが勝手にパスを追加する。まー、当たり前の動作だけど、僕はハマってしまった。

Argから取れるURLパスの情報

| 13:59

Argに何でも入っているが、まず、request内にpathがある。

  • request.path {abs_path,"/app/dump/foo?bar"}

abs_pathじゃないときがあるかどうかは不明。abs_pathじゃないリクエスト? イメージできない。

argの直下にあるpathinfoがけっこう使いやすい。

  • server_path "/app/dump/foo"
  • querydata "bar"
  • prepath "/app/"
  • pathinfo "/foo"

ローカルファイルシステムとの関係は次でわかる。

  • docroot "c:/cygwin/var/yaws_site"
  • docroot_mount "/"
  • fullpath "c:/cygwin/var/yaws_site/app/dump/foo"