Erlangと配列
(「ErlangとCRC32」から改題)
CRC32が必要になりそうだったので、Erlangでどうやって実現させるかを考えてみました。zlib:crc32があることが分かりましたが、CRC32と一口に言っても利用する多項式によって様々なバリエーションがありますので、多項式を外から与えることができないとまずいということがあるかもしれません。
CRCは前後のブロックに依存しないので、0〜255の計算結果をあらかじめテーブルに保存しておいて、バイト単位でそのテーブルを索くような処理をすることができます。もし自力で実装するとなるとそのようなテーブルを持つことになるわけですが、Erlangで書く場合、任意のインデックスにアクセスするためのデータ構造(要するに配列ですが)として何を使えばいいのでしょうか?
任意のインデックス(のようなもの)にアクセスができるものとして下記の5つを思いつきました。
- tuple: element/2でアクセスできます。追加は erlang:append_element/2が使えます。
- binary: binary:part/3 を使うとバイナリの一部分を持ってくることができます。パターンマッチングでも持ってくることができます。
- dict: インデックスをキーにしてしまえば使えそうです。
- ets: dictと同様に使えそうです。
- プロセス辞書: これもハッシュがわりに使えるのでdict/etsと同様に使えそうです。
今回のように、あらかじめ作成しておいたN個(今回は256個)のテーブルの任意の値を取得するという場合にどれが性能的に有利なのかを調べてみたいと思いました。
(比較用のCソースはTNKソフトウェア - 私的ZIPファイル研究所を参考にさせていただきました)
実行した結果は下記のようになりました。(コードは後ろに載せています)
- Erlang R14B03 (erts-5.8.4) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]
- Mac OS X Server 10.6.8
- Processor 2.53GHz Intel Core 2 Duo
- Memory 4GB 1067MHz DDR3
trial | tuple | binary | dict | process_dictionary | ets | zlib | C(参考) | |
1 | 5760 | 8852 | 14497 | 5638 | 12351 | 427 | 184 | |
2 | 5753 | 8820 | 14530 | 5679 | 12451 | 430 | 184 | |
3 | 5844 | 8811 | 14083 | 5679 | 12179 | 430 | 185 | |
4 | 5784 | 8814 | 14183 | 5637 | 12231 | 431 | 184 | |
5 | 5813 | 8791 | 14517 | 5708 | 12243 | 433 | 186 | |
6 | 5792 | 8837 | 14491 | 5655 | 12477 | 429 | 187 | |
7 | 5785 | 8795 | 14075 | 5670 | 12207 | 430 | 184 | |
8 | 5733 | 8847 | 14473 | 5632 | 12167 | 439 | 184 | |
9 | 5819 | 8813 | 14565 | 5668 | 12474 | 433 | 184 | |
10 | 5752 | 8827 | 14188 | 5692 | 12341 | 430 | 183 |
計測値は、各列の先頭に書かれているアルゴリズムのCRC32を10万回計算したときにかかった時間、単位はmsecです。対象データはすべて同じで、0〜FFが1回ずつ順番にあらわれる256バイトのデータとなっています。
試す前の予想としては、zlib→binary→dict→ets→tuple→process dictionary といった感じでした。結果はzlib→tuple≒process dictionary→binary→ets→dict でした。ぜんぜん違いすぎて笑えてきますね。tupleが速いというのもそうですが、プロセス辞書が予想外に速いことに驚きました。そしてetsとdictが予想と逆だったのも意外です。
zlibは当然ですが速いですね。これが使えれば言うことがないんですが....C版は自力で書いたtuple/プロセス辞書版の30倍ほど速いです。
別の環境でも試してみました。こちらもR14B03です。
- Windows 7
- Processor 2.13GHz Intel Core 2 Duo
- Memory 2GB
loop | tuple | binary | dict | process_dictionary | ets | zlib |
1 | 7799 | 12619 | 22666 | 7862 | 17627 | 482 |
2 | 7721 | 12495 | 22806 | 7846 | 17471 | 467 |
3 | 7534 | 12527 | 22807 | 7923 | 17549 | 530 |
4 | 7598 | 12557 | 22775 | 7986 | 18205 | 467 |
5 | 7736 | 12666 | 22978 | 7940 | 17596 | 482 |
6 | 7597 | 12823 | 23368 | 8033 | 17533 | 483 |
7 | 7690 | 12916 | 22948 | 7845 | 17597 | 484 |
8 | 7643 | 12433 | 23291 | 7972 | 17769 | 498 |
9 | 7784 | 12621 | 23088 | 7971 | 17659 | 483 |
10 | 7628 | 12776 | 23228 | 7941 | 17628 | 514 |
順番はMacのものと同じようです。それにしてもこの手の試行は苦手ですね... 本来計測したいものではないものを一生懸命計測しているのではないだろうか、特定の条件に限って著しく不利/有利なことを気付かずにしてたりしないだろうか、といった心配事がたくさんあります。
計測に使ったコードは下記の通りです( crc:bench_all(10,100000) を実行すると上記のような試行を実施します)。各構造ごとにテーブル作ったりCRCを実行したりしているのは、それぞれ make_table, do_crc という関数になります。
続きを読むErlangとQuine
Quineとは自分自身を出力するようなプログラムです。出力結果と、元のソースを比較するとぴったり一致します。賢い人にしか書けないと思っていたので、私には縁がないと思っていたのですが、Google Code Jam Japan 2011 T-shirtによると、「Quine を書いたことが無いのなら、自分で書いてみることを強くお勧めする」とKen Thompsonが言っていたらしいです。artonさんの記事に触発されたこともあり、私もErlangのQuineにチャレンジしてみました。
Wikipedia(ja)と、Wikipedia(en)をざっと眺めて、おぼろげながら様子が分かってきたので、実際に書いてみました。printfの書式指定を使って簡潔に書く例があったな、という記憶を頼りに書いたのが以下のコードです。
main(_)-> S="~nmain(_)->~nS=~c~s~c,~nio:format(S,[34,S,34]).~n", io:format(S,[34,S,34]).
実行するとなるとescriptの方がいいだろうと考えました。1行目はあけています。escriptはほとんど使ったことがないので、なぜ1行目をあけなければいけないのかは理解できていません。書き終わってから再度Wikipediaを見てみたらほとんどまったくそのまんまだったので、驚くとともに、試行錯誤にかかった時間を考えてちょっと泣きました。
簡潔に書く方法は分かったので、Wikipedia(en)にあるJavaのコードのようなアプローチを試してみます。Quineを作成する手順は、自分自身のコードを吐き出すための文字列(S)を作成する→それを使って出力する、となります。待ち受けている困難は以下のような感じでした。
- 自分自身のコードを吐き出すための文字列(S)、に含まれているコードには、自分自身(S)も含まれていることになりますので、ひと工夫しないといけません。
- ダブルクォート自体を文字列に含めることが困難です。エスケープして使おうすると、出力結果の方にはエスケープがされてないものが出てしまうので一致させることができません。
前者は、Sの中身はコードには書かず(書きたくても書けませんが)、外側のプログラムで合成するようなロジックを書くことで回避できます。後者は、ダブルクォート以外の方法でダブルクォートと同等の働きをするものがあればそれを使えばいいのですが、Erlangでは適切なものが思いつかなかったので、シングルクォートで書いておいて、出力時にダブルクォートで置き換えるようにしてみました。
できあがったコードは下記の通りです:
main(_)-> S=[ "", "main(_)->", "S=[", "@", "],", "print(S,S).", "", "replace(L,F,T)->", " replace(L,F,T,[]).", "replace([],_F,_T,R)->lists:reverse(R);", "replace([F|TL],F,T,R)->", " replace(TL,F,T,[T|R]);", "replace([H|TL],F,T,R)->", " replace(TL,F,T,[H|R]).", " ", "print(_S,[])->ok;", "print(S,['@'|TL])->", " io:format('~s~n', [list_quoted(S,[])]),", " print(S,TL);", "print(S,[H|TL])->", " io:format('~s~n',[replace(H,39,34)]),", " print(S,TL).", "list_quoted([],R)->string:join(lists:reverse(R),[44,10]);", "list_quoted([H|TL],R)->", " list_quoted(TL, [io_lib:format('~c~s~c',[34,H,34])|R])." ], print(S,S). replace(L,F,T)-> replace(L,F,T,[]). replace([],_F,_T,R)->lists:reverse(R); replace([F|TL],F,T,R)-> replace(TL,F,T,[T|R]); replace([H|TL],F,T,R)-> replace(TL,F,T,[H|R]). print(_S,[])->ok; print(S,["@"|TL])-> io:format("~s~n", [list_quoted(S,[])]), print(S,TL); print(S,[H|TL])-> io:format("~s~n",[replace(H,39,34)]), print(S,TL). list_quoted([],R)->string:join(lists:reverse(R),[44,10]); list_quoted([H|TL],R)-> list_quoted(TL, [io_lib:format("~c~s~c",[34,H,34])|R]).
手順を再度確認すると、以下のようになります。
- プログラム全体をリスト化した文字列をSに入れます。
- ただし、S自体を定義する場所には"@"だけを入れておきます。
- また、文字列中の、本来ダブルクォートであってほしい部分はシングルクォートで代用しておきます。
- mainの最後で、作成したSを出力するようにします。このとき、"@"をてがかりにしてS自体の定義を置き換えてゆきます。具体的には、以下のような流れになります。
- Sの各要素をシングルクォート→ダブルクォートに置換しつつ出力
- "@"の行が見つかったら、Sを最初から最後まで出力しなおします。このときは、シングルクォート→ダブルクォートの置換を行わず、各行をダブルクォートで挟んだ上、カンマ→改行で区切って出力します。
- Sの残りの行について、シングルクォート→ダブルクォートに置換しつつ出力
- Sの各要素をシングルクォート→ダブルクォートに置換しつつ出力
実際にコードを書くときは、Sの定義以下の部分を、ダブルクォート→シングルクォートで置換した上で文字列として並べてゆけばOKです。
置換の処理は適切な関数が見つからなかったので replaceというものを作っています。なんだか随分長くなってしまいましたが、枠組さえ分かれば上記のように機械的に作ってゆくことができるので、あとは必要なものをどんどん実装(そして文字列を作成)してゆく手間だけの問題です。
問題といえばもう1つ、改行コードやダブルクォート、シングルクォート、カンマなどの文字コードを即値で指定しているため、これがどんな環境でも動くとは言えそうにないというのがあります。他の3つはともかく、改行コードくらいはどうにかしたいものですが...
さて、他の人が書いたErlangのQuineはないかな?と探してみました。
bkil's blog on science, programming, ecology, ...のQuineは鮮やかですね。~pと~sの違いを巧妙に利用しています。
思ったほど大変ではなかったというのが実際に作ってみた感想でした。今回書いたようなアプローチでは、言語ごとの有利/不利の差があるとしたら、文字列操作に関わる機能についてぴったり合うものがあるのかないのかという程度なのかな。そして、言語やライブラリの細かい仕様を把握している人ほど有利という意味ではcode golfに近いものがあるのかなと思いました(golfやったことないのに言うのも変ですが)。
(追記) shinhさんにanarchy golfにもErlangのQuineが投稿されてるよと教えてもらいました。140バイトの方はだいたい冒頭のやつと同じですが、104バイトのやつはすさまじいですね...
11> S="S=~p,io:format(S,[S])",io:format(S,[S]). S="S=~p,io:format(S,[S])",io:format(S,[S])ok 12>
うーん、まったく思いつきませんでした。すごい。
アラビア数字・ローマ数字変換
お題:アラビア数字・ローマ数字変換 - No Programming, No LifeをErlangでやってみました。やってみたのはけっこう前ですが放置していました。
まずarabic_to_roman。ローマ数字には位取りの0がないのでちょっと表現が難しいですが、1、10、100の位について、それぞれ1、5、10の重みをもつ文字が対応していると考えてみました。1の位であれば、I、V、Xだし、10の位であれば、X、L、Cですね。
-module(roman). -export([arabic_to_roman/1, roman_to_arabic/1]). -export([test/0]). roman_digit(N, {D1, D5, D10}) when N >= 1 andalso N =< 9 -> case N of 1 -> [D1]; 2 -> [D1, D1]; 3 -> [D1, D1, D1]; 4 -> [D1, D5]; 5 -> [D5]; 6 -> [D5, D1]; 7 -> [D5, D1, D1]; 8 -> [D5, D1, D1, D1]; 9 -> [D1, D10] end; roman_digit(0, _D) -> [].
これを使って変換してみましょう...
arabic_to_roman(N) when N >= 1 andalso N =< 3999 -> D1000 = N div 1000, D100 = (N rem 1000) div 100, D10 = (N rem 100) div 10, D1 = (N rem 10), DL = [{"M", void, void}, {"C", "D", "M"}, {"X", "L", "C"}, {"I", "V", "X"}], lists:flatten(lists:map(fun(({X, D})) -> roman_digit(X, D) end, lists:zip([D1000, D100, D10, D1], DL))).
1000の位の扱いと、あと0の扱いがやはり若干苦しいですね。
10> roman:arabic_to_roman(1234). "MCCXXXIV" 11>
ふむふむ。では次はroman_to_arabicです。こちらは、各桁の1の重みを持つ文字が、場所によって+1なのか-1なのかが変わるというのが厄介です。状態を持たせて、とか、先読をして...といったやりかたも考えられますが、文字列のパターンマッチングを使った方が話が早そうです。こんな感じになるでしょうか...
roman_to_arabic(S) -> roman_to_arabic(string:to_upper(S), 0). roman_to_arabic("", Acc) -> Acc; roman_to_arabic("IV" ++ Rem, Acc) -> roman_to_arabic(Rem, Acc + 4); roman_to_arabic("IX" ++ Rem, Acc) -> roman_to_arabic(Rem, Acc + 9); roman_to_arabic("XL" ++ Rem, Acc) -> roman_to_arabic(Rem, Acc + 40); roman_to_arabic("XC" ++ Rem, Acc) -> roman_to_arabic(Rem, Acc + 90); roman_to_arabic("CD" ++ Rem, Acc) -> roman_to_arabic(Rem, Acc + 400); roman_to_arabic("CM" ++ Rem, Acc) -> roman_to_arabic(Rem, Acc + 900); roman_to_arabic("I" ++ Rem, Acc) -> roman_to_arabic(Rem, Acc + 1); roman_to_arabic("V" ++ Rem, Acc) -> roman_to_arabic(Rem, Acc + 5); roman_to_arabic("X" ++ Rem, Acc) -> roman_to_arabic(Rem, Acc + 10); roman_to_arabic("L" ++ Rem, Acc) -> roman_to_arabic(Rem, Acc + 50); roman_to_arabic("C" ++ Rem, Acc) -> roman_to_arabic(Rem, Acc + 100); roman_to_arabic("D" ++ Rem, Acc) -> roman_to_arabic(Rem, Acc + 500); roman_to_arabic("M" ++ Rem, Acc) -> roman_to_arabic(Rem, Acc + 1000).
ローマ数字は3999までしかないらしいのでこれでもいいような気がしないでもないですが、もうちょっと考えてみたいので没にしました。作りなおしたのがこちらです:
roman_to_arabic(S) -> roman_to_arabic(string:to_upper(S), 0). roman_to_arabic("", Res) -> Res; roman_to_arabic(S, Res) -> D = [{"M", 1000}, {"CM", 900}, {"D", 500}, {"CD", 400}, {"C", 100}, {"XC", 90}, {"L", 50}, {"XL", 40}, {"X", 10}, {"IX", 9}, {"V", 5}, {"IV", 4}, {"I", 1}], case lists:foldl(fun({DD, DN} ,Acc)-> case Acc of [] -> case string:str(S, DD) of 1 -> {DN, string:len(DD)}; _Else -> Acc end; Acc-> Acc end end, [], D) of {X, Len} -> roman_to_arabic(string:substr(S, Len + 1), Res + X); [] -> error end.
やってることは一緒のつもりですがややこしいですね。最初に見つかったところで止まるループを作りたかったのですが、やりかたが思いつかず上記のような苦しい表現になってしまいまいた。いずれにしても没バージョンに比べて著しく優れているとも思えませんね...
19> roman:roman_to_arabic("xvi"). 16 20>
XVIは16MHzなので正しいようですね(ひどい覚えかただ)。最後にテストを:
test() -> assert(arabic_to_roman(11), "XI"), assert(roman_to_arabic("XI"), 11), assert(arabic_to_roman(12), "XII"), assert(roman_to_arabic("XII"), 12), assert(arabic_to_roman(14), "XIV"), assert(roman_to_arabic("XIV"), 14), assert(arabic_to_roman(18), "XVIII"), assert(roman_to_arabic("XVIII"), 18), assert(arabic_to_roman(24), "XXIV"), assert(roman_to_arabic("XXIV"), 24), assert(arabic_to_roman(43), "XLIII"), assert(roman_to_arabic("XLIII"), 43), assert(arabic_to_roman(99), "XCIX"), assert(roman_to_arabic("XCIX"), 99), assert(arabic_to_roman(495), "CDXCV"), assert(roman_to_arabic("CDXCV"), 495), assert(arabic_to_roman(1888), "MDCCCLXXXVIII"), assert(roman_to_arabic("MDCCCLXXXVIII"), 1888), assert(arabic_to_roman(1945), "MCMXLV"), assert(roman_to_arabic("MCMXLV"), 1945), assert(arabic_to_roman(3999), "MMMCMXCIX"), assert(roman_to_arabic("MMMCMXCIX"), 3999). assert(X,X) -> ok.
実行してみると...
20> roman:test(). ok 21>
OKのようです。
ErlangとJInterface
JIntefaceはErlangとJavaの相互運用を可能にするモジュールです。OTPに最初から含まれているので手軽に利用することができます。
先日、ErlangにRPCで指示を出して、応答をJTreeで表示するというテストアプリを書いてみたところ思いのほか便利で、これに加えて、普段Erlangを使っているときに表示されている式を適当にコピペするとビジュアライズできるようになったらもっと便利だなあと思いました。
そのためには文字列で与えたErlangのコードをparseしてevalできるような仕組が必要です。Erlang側にモジュールを作ってその力を借りればそんなに難しくなさそうですが、今回はJava側で組み立てて実行するようにしてみました。
(Erlangのevalってこうやるのか - kgbu? を参考にさせていただきました)
public static OtpErlangObject rpcExec(OtpConnection connection, String M, String F, OtpErlangList A) throws IOException, OtpErlangExit, OtpAuthException { connection.sendRPC(M, F, A); return connection.receiveRPC(); } public static final OtpErlangList emptyList = new OtpErlangList(new OtpErlangObject[]{} ); public static OtpErlangObject evalString(OtpConnection c, String expr) throws IOException, OtpException { OtpErlangObject received ; // erl_scan:string(expr) received = rpcExec(c, "erl_scan", "string", new OtpErlangList( new OtpErlangObject[] { new OtpErlangString(expr) })); // should be {ok, Tokens, EndLocation} if(!(received instanceof OtpErlangTuple)) return received; OtpErlangTuple tuple = (OtpErlangTuple)received; if(tuple.arity() != 3) return received; OtpErlangAtom t1 = (OtpErlangAtom)tuple.elementAt(0); if(!t1.atomValue().equals("ok")) return received; OtpErlangObject tokens = tuple.elementAt(1); // erl_parse:parse_exprs(Tokens) received = rpcExec(c, "erl_parse", "parse_exprs", new OtpErlangList( new OtpErlangObject[] { tokens })); // should be {ok, [Expression]} (Expression is tuple) if(!(received instanceof OtpErlangTuple)) return received; tuple = (OtpErlangTuple)received; if(tuple.arity() != 2) return received; t1 = (OtpErlangAtom)tuple.elementAt(0); if(!t1.atomValue().equals("ok")) return received; OtpErlangTuple expression = (OtpErlangTuple)((OtpErlangList)tuple.elementAt(1)).elementAt(0); // erl_eval:expr(Expression, erl_eval:bindings(erl_eval:new_bindings())) OtpErlangObject newBindings = rpcExec(c, "erl_eval", "new_bindings", emptyList); OtpErlangObject bindings = rpcExec(c, "erl_eval", "bindings", new OtpErlangList(new OtpErlangObject[] { newBindings} )); received = rpcExec(c, "erl_eval", "expr", new OtpErlangList(new OtpErlangObject[] { expression, bindings} )); return ((OtpErlangTuple)received).elementAt(1); }
簡単なUIをつけてみました。
下のTextAreaに入力したテキストを↑のevalに通して、結果を上のJTreeに表示します。
コピペしたErlangのtermを表示した例です。バイナリはHEXで表示するようにしてみました。もうちょっとUIを考えないと常用には耐えられなさそうですけど、なかなか便利かもしれないと思いました。
Erlangとビット演算
FeliCaのポーリングで取得できるPMmからコマンドごとのタイムアウト時間を計算する必要が生じたため、Erlangで書いてみました。
PMmの各バイトを、2ビット、3ビット、3ビットに分けて計算するので、まずは取り出してみましょう。
4> <<E:2, B:3, A:3>> = <<2#10011101>>. <<157>> 5> E. 2 6> B. 3 7> A. 5
この手の処理はErlangで書くととても楽ちんですね。後は定義に従ってコードを書きます。
-module(pmm). -compile(export_all). decode(P, N) -> <<E:2, B:3, A:3>> = <<P>>, 0.3020 * ((B + 1) * N + (A + 1)) * math:pow(4, E). decode_pmm(PMm, N) -> <<_D8, _D9, D10, D11, D12, D13, D14, D15>> = PMm, [ {'PMm', PMm}, {request_service, decode(D10, N)}, {request_response_and_request_system_code, decode(D11, 0)}, {authentication, decode(D12, N)}, {read, decode(D13, N)}, {write, decode(D14, N)}, {issue, decode(D15, 0)}].
Nは対象のブロック数です(場合によってはサービス数も含みます)。どれどれ...
8> pmm:decode_pmm(<<16#01, 16#10, 16#8B, 16#42, 16#8F, 16#BE, 16#CB, 16#FF>>, 5). [{'PMm',<<1,16,139,66,143,190,203,255>>}, {request_service,67.648}, {request_response_and_request_system_code, 3.6239999999999997}, {authentication,86.976}, {read,227.10399999999998}, {write,270.592}, {issue,154.624}] 9>
よい感じのようです。
文字列を先頭から見て同じところまで除去 (2)
前回の記事で書いたソースをいじってみましょう。
下記2点が気になりました。
- リストに1つしか文字列がないときに期待通りの動きをしない。
これは、lists:all(Fun, []) が true を返すためですね。
- 1文字辿るのに2回もループを回している。
lists:all/2 と、lists:map です。これを同時にできる方法があればいいのですが、標準で使えそうなものを見つけることができなかったので自作しました(ahya:split)。
-module(ahya). -compile(export_all). split(L) -> split(L, [], []). split([], _, Tails) -> lists:reverse(Tails); split([H|TL], Head, Tails) -> [H1|T1] = H, case Head of [] -> split(TL, H1, [T1|Tails]); H1 -> split(TL, Head, [T1|Tails]); _ -> failed end. ahya2([[]|L]) -> [[]|L]; ahya2([H|[]]) -> [H]; ahya2(L) -> case split(L) of failed -> L; Tails -> ahya2(Tails) end.
前回試した範囲では同じ結果を返しているようです。lists:reverse/1呼んでるじゃないか、という厳しい声が上がりそうですし、コードを読んだときのわかりやすさがだいぶ損われてしまったように思いますね。
文字列を先頭から見て同じところまで除去
お久しぶりです。
No Programming, No Lifeで見掛けた表題のお題をErlangでやってみます。
まず、文字列が2つのときを考えます。これは文字列のパターンマッチングを使えば問題なさそうですね。
-module(ahya). -compile(export_all). ahya([H1|T1], [H1|T2]) -> ahya(T1, T2); ahya(L1, L2) -> [L1, L2].
どれどれ...
50> ahya:ahya("abcdef", "abc123"). ["def","123"] 51> ahya:ahya("12345", "67890"). ["12345","67890"] 52>
大丈夫そうですね。では3つ以上の場合を考えてみましょう。上記の文字列2つバージョンを繰り返し呼ぶようにしてあげようかと思ったのですが、無修正では無理なような気がしましたので、あらためて考えてみます。
1分ほど考えた結果、文字列のリストを貰って、各要素の先頭1文字を取り出して、一致したら除去して...というのが単純でいいかなと思いました。
ahya2(L) -> ahya2(L, []). ahya2([[]|L], _) -> [[]|L]; ahya2([H|TL], Acc) -> [Head|_] = H, case lists:all(fun([X|_]) -> X =:= Head end, TL) of false -> [H|TL]; true -> ahya2(lists:map(fun([_|X]) -> X end, [H|TL]), [H|Acc]) end.
こんな感じでしょうか?では早速動かしてみましょう...
52> ahya:ahya2(["abcdef", "abc123"]). ["def","123"] 53> ahya:ahya2(["あいうえお", "あいさんさん", "あいどる"]). [[164,130,166,130,168], [179,130,241,130,179,130,241], [199,130,233]] 54> ahya:ahya2(["12345", "67890", "12abc"]). ["12345","67890","12abc"] 55>
あれ、なんか2つ目が...???
69> erlang:display("あいさんさん"). [130,160,130,162,130,164,130,166,130,168] true 70> erlang:display("あいどる"). [130,160,130,162,130,179,130,241,130,179,130,241] true 71> erlang:display("あいどる"). [130,160,130,162,130,199,130,233] true 72>
うーん、動作としては合っていますが、期待する答えではありませんね。
そもそもUTF-8じゃないし...