トップ «前の日記(2007-05-30) 最新 次の日記(2007-06-05)» 編集

Route 477



2007-06-04

[erlang] もしも「ふつうのErlang」が出版されたら

第1章は「エラーメッセージ」で。

[event][erlang] Erlang勉強会#1

というわけで(どういうわけだ) Erlang勉強会#1 に行ってきた。「参加者はfizzbuzzを書いてくること」という 告知にもかかわらず12人も集まってすげー!と思ったんだけど、 やっぱり半分くらいの人がまだ書けてなかった(笑)。僕ですか?電車の中で頑張って書きましたよ。ええ。

-module(fizzbuzz).
-compile(export_all).  %これは便利

fizzbuzz(N) when N rem 3  == 0 -> "fizz";
fizzbuzz(N) when N rem 5  == 0 -> "buzz";
fizzbuzz(N) when N rem 15 == 0 -> "fizzbuzz";
fizzbuzz(N) -> integer_to_list(N).

run() -> lists:map(fun fizzbuzz/1 , lists:seq(1, 100)).
%run2() -> [fizzbuzz(X) || X <- lists:seq(1,100)].

数値を"fizz"とか"buzz"とかに変換する関数を書いて、それを1から100までのリストにmapする方向で実装してみた。

  • 定義済みの関数を関数に渡すには fun fizzbuzz/1 のように書く(1は引数の個数)。
    • 関数は関数名とarityで区別されるらしい。

最初はlists:seqを知らなくて、iotaという関数を実装してた。

iota(Start, Start) -> [Start];
iota(N,     Start) -> iota(N-1, Start) ++ [N].   

逆順に作ってreverseするほうが速そうだな。

本編では、練習としてold roman (ローマ数字の簡略版、4をIVじゃなくてIIIIのように書く) を整数に変換する関数を書いた。

-module(roman).
-compile(export_all).

chr2int($M) -> 1000;  % 1文字を整数に変換する。「$M」はC言語の「'M'」、Rubyの「?M」。
chr2int($D) -> 500;
chr2int($C) -> 100;
chr2int($L) -> 50;
chr2int($X) -> 10;
chr2int($V) -> 5;
chr2int($I) -> 1;
chr2int(X)  -> erlang:error("invalid char in old roman: " ++ [X]). % ++はリストの連結(format使う方がいいかも)。

old2int(Str) -> 
  Mapped = lists:map(fun chr2int/1, Str),        % まず整数のリストに変換して、
  Soted  = lists:reverse(lists:sort(Mapped)),    % ソートしたものを作る
  if 
    Soted == Mapped -> lists:sum(Mapped);        
    true            -> erlang:error("invalid order in old roman: " ++ Str)
  end.

old romanの仕様から考えると、"VII"はいいけど"IVI"とかはエラーにしたい。ここではまず整数のリストを作って、 それが降順かどうかでエラーチェックするようにしてみた。ソートして同じかを比較するというのはなんきさんの案。 本当は「(apply < ls)」みたいなことがしたかったんだけど(scheme脳)、多引数の「<」みたいな便利なもんはあるのかな。

  • erlang:errorでエラーを通知できる。(ここはthrowで例外を投げるほうがいいかも?)
    • エラーメッセージは親切にしておかないと、処理系のエラーなのか自前のエラーなのか見分けがつきません(笑) 気をつけよう。
  • ifの見た目が変態すぎるw
    • ifっていうかcondだよね
    • 条件に副作用を持つものは書けない(?)らしい。

その他:

  • =:= っていう演算子がある
    • ==は、整数と少数を区別しないが、=:= は区別する(1.0 =:= 1は偽)。
  • 文字列がなく、数値のリストを作ると文字列と見なされる
1> [104, 111, 103, 101].
"hoge"
  • 副作用についてはわりと緩めらしい。HaskellじゃなくてSchemeやOCamlのような。
test() ->
  io:put_chars("hoge"),
  io:put_chars("moge"). % 普通にOK
  • Emacsのerlang-modeが凄い
    • 「;」を打つと、自動的に次の行に関数名を補完してくれたりとか
    • 「;」と「.」と「,」を使い分ける→実はエディタ指向言語なのかも
    • 例えばC#の仕様はVisual Studioでの補完がやりやすいように考えられてる(という噂)
  • HIGHER ORDER PERLという本が熱いらしい
    • 目次を見てるだけですでにヤバい(笑)

1558607013

[erlang] ESDLが以下略(2)

test\ 以下で

erlc -W -bbeam -I..\.. -I..\include -pa ..\ebin testsprite.erl

とやると、testsprite.beamにコンパイルできる。ってとこまでは分かった。

だがしかし、これを実行しようとすると…

C:\research\Erlang\esdl-0.96.0626\test>erl -pa ..\ebin testsprite.beam
Eshell V5.5.4  (abort with ^G)
1> testsprite:go().
Driver Failed {error,driver_incorrect_version}

=ERROR REPORT==== 2-Jun-2007::20:23:09 ===
Error in process <0.29.0> with exit value: {einval,[{erlang,open_port,[{spawn,"s
dl_driver"},[binary]]},{sdl,init,1},{testsprite,go,1},{erl_eval,do_apply,5},{she
ll,exprs,6},{shell,eval_loop,3}]}

** exited: {einval,[{erlang,open_port,[{spawn,"sdl_driver"},[binary]]},
                    {sdl,init,1},
                    {testsprite,go,1},
                    {erl_eval,do_apply,5},
                    {shell,exprs,6},
                    {shell,eval_loop,3}]} **

driver_incorrect_versionって何だよー。

って、driver_incorrect_versionで検索したら自分の日記がでてきた。進歩してねえじゃん。

もうちょっと調べてみる。

[ruby][prog] 英語なまりのRuby

Enumerableを操作するのに

@stooges.select {|s| s.name == 'Mo'}

と書く代わりに

@stooges.that.have.name == 'Mo'

と書くためのライブラリ、ho_enumerable.rbについて。

[http://www.rubyist.net/~matz/20070530.html#p04より引用]

一瞬「かっこえー!」と言いそうになったんだけど、プログラミング言語として考えるとこれはセンスないよな。 なんつーか、thatとかhaveが何を返すのかが全然わからんところが嫌だ。せめて

@stooges.that_have.name == 'Mo'

と分けてほしい。

どちらにせよ、自然言語に近くてもプログラミング言語的に不自然だったら駄目だと思う。*1

余談

Lispだと「.」が要らないので、より英語に近くできるらしい。

(例) Loopマクロより:

(loop for x being the hash-keys in ht collect x)

*1 チャレンジとしては面白いけど

[ruby] FizzBuzz一般化

14分。

仕様を読み間違えた(optparse使おうとした)のとto_i忘れでちょっとはまった("1".."100" が普通に通るんだよな…)。

require 'enumerator'

class FizzBuzz

  def initialize(argv)
    raise ArgumentError if argv.size < 2

    @start = argv[0].to_i
    @end   = argv[1].to_i

    @str_mod = []
    if argv.size > 2
      argv[2..-1].each_slice(2) do |n, str|
        @str_mod << [n.to_i, str]
      end
    end
  end

  def run
    puts (@start..@end).map{|i|
      s = ""
      @str_mod.each do |n, str|
        if i % n == 0
          s << str
        end
      end
      s += i.to_s if s.empty?
      s
    }
  end

end

FizzBuzz.new(ARGV).run