-- / --
--
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

05 / 03
Sat

毎週土曜日更新にしようかと迷っている Ziphil です。

Ruby でメソッドの返り値を明示したかったら return foo とか書きますよね。 まあ、 Ruby は最後に評価された値をそのまま返すので、 return 文はいらないんですが。 では、 ブロックの返り値を明示したい場合はどうしましょうか。 return foo と書くと、 ブロックだけでなくメソッドまで抜けちゃいます。 調べるとどうやら next foo と書けば良いらしいです。

え、 わざわざ next 文を使う必要ないじゃないかって? ・・・ええ、 その通りですよ。


スポンサーサイト
comment ×0
04 / 26
Sat

もう 4 月も終わりか…、 という感じの Ziphil です。

いつの間にか Java8 がリリースされていました。 どうやら、 ラムダ式が追加されたみたいで、 これは Rubyist である (だった?) 自分には嬉しい仕様です。 どんなのかというと、 こんなのです。

List list = Arrays.asList("foo", "bar", "baz", "hoge", "huga");
list.forEach((String s) -> {
  String data = "[" + s + "]: " + s.length();
  System.out.println(data); 
});

map とか filter とか reduce とかもちゃんとあります。 ただし、 この場合は一度コレクションを Stream オブジェクトに変換しないといけません。 これがちょっと面倒。

List list = Arrays.asList("foo", "bar", "baz", "hoge", "huga");
String result = list.stream().filter((String s) -> {
  return s.length() > 3;
}).map((String s) -> {
  return s.toUpperCase();
}).reduce((String s, String t) -> {
  return s + ", " + t;
}).get();
System.out.println(result);

ラムダ式では型推論が働いてくれるので、 引数の型は省略できます。 また、 ラムダ式を 1 行で書くばあいは return 文も不要です。 さらに、 引数が 1 つだけの場合は、 引数の前後のカッコも省略できます。 ・・・ということで、 上のプログラムは下のようにも書けます。

List list = Arrays.asList("foo", "bar", "baz", "hoge", "huga");
String result = list.stream().filter(s -> s.length() > 3).map(s -> s.toUpperCase()).reduce((s, t) -> s + ", " + t).get();
System.out.println(result);

Ruby を長いこと使ってきた私にとっては、 こう書けると非常に親近感がわきます。 Ruby だとこんな感じで、 似てますからね。

list = ["foo", "bar", "baz", "hoge", "huga"]
result = list.select{|s| s.length > 3}.map{|s| s.upcase}.reduce{|s, t| s + ", " + t}
puts result

さて、 この Java のラムダ式ですが、 実装はどうなっているかというと、 ただの匿名クラスです。 たとえば、 Stream#filter の引数の型は Predicate<T> です。 つまり、 Java がラムダ式を Predicate<T> を継承した匿名クラスに勝手に変換してくれるイメージです。 ちなみに、 Predicate<T> は T 型の引数をとり Boolean 型を返すラムダ式に相当します。

ということで、 今までラムダ式とかクロージャとか、 そういう類のものが使えないせいで、 for 文が嫌いな私は Ruby とか Scala とか Groovy とか Kotlin とか Fantom とか Xtend とかに逃げてましたが、 その必要もほとんどなくなりましたね。

あ、 そうそう、 ラムダ式の変換先となる関数型インターフェースには、 引数の型や返り値の型などによって Supplier とか Consumer とか Prdicater とかいろいろあるんですが、 プリミティブ型専用に IntSupplier とか IntConsumer とかがあるんですよね。 それならプリミティブ型廃止すれば良いのに!


comment ×0
04 / 19
Sat

細々と生きております、 Ziphil です。

私が (最低限は) 使える言語というと、 だいたい 10 種類ほどあるわけですけど、 どういうわけか C 系列だけ全く使えないんですよ。 理由は諸説ありますが、 C をちょっと学んでみると、 Ruby の方がプログラムをきれいに書けることに気づいてしまい、 C が捨てられると、 まあそんな感じでしょう。

で、 たぶん C とか C++ とか C# には縁がないんだろうなー、 とか思っていました。 思っていたんですが、 紆余曲折あって C# を学ぶ (そして人にある程度教える) ことになってしまいました。

ということで、 Visual Studio Express を急遽ダウンロードしたのが一昨日、 C# を勉強し始めたのが昨日。 で、 ついさっき、 ゲーム作成の基礎部分 (ウィンドウを表示したり FPS を安定させたり) が完成したわけです。 まあ、 C# はだいたい分かりました。 さすがに Ruby より詳しくはなってませんが、 Java と同程度の能力にはなったと思います。 同じオブジェクト指向言語なので、 難しくはなかったですね。

で、 長年敬遠していた C 系列の言語ですが、 少なくとも C# は悪くないかな、 って感じです。

でも、 ForEach がしっくりきませんね。 Ruby の影響から、 どうも foreach 構文ではなく、 ForEach メソッドを使いたくなるんですが・・・。

Array.ForEach(array, (Foo foo) => {
    DoSomething();
});

・・・とまあ、 そうするとこんな風になるわけですよ。 Array#ForEach じゃなくて Array.ForEach なんですよね。

array.ForEach((Foo foo) => {
    DoSomething();
});

こう書きたいなぁ・・・。 いや、 まあ、 書けるんですけど、 あんまり推奨されませんよねぇ。

そんなわけで、 C# でした。 今後のアプリケーション開発は JRuby か C# かな。


comment ×0
03 / 30
Sun

引っ越しが終わった Ziphil です。

私のプログラムでは、 アイテムなどのデータベースは、 アイテム ID をキーとしたハッシュで管理しています。 そこで、 すべてのデータベースを合わせた 1 つのハッシュを作ろうとして、 hash1 + has2 のように書いたら、 Hash#+ がないと怒られました。 ・・・ないの?

どうやら、 こういう場合は Hash#merge を使うみたいです。 調べてみると、 ただ 2 つのハッシュを混合したハッシュを作るだけでなく、 キーが重複した場合の処理を指定できるみたいです。

hash1 = {:a => 2, :b => 4, :c => 9}
hash2 = {:b => 5, :c => 6, :d => 7}
p hash1.merge(hash2){|key, value1, value2| value1 + value2}    #=> {:a => 2, :b => 9, :c => 15, :d => 7}

次です。 Object#tap というメソッドを知りました。 これは、 self をブロックの引数として評価した後、 self そのものを返すメソッドです。 で、 こんなメソッドが何の役に立つかというと、 メソッドチェーンの途中経過を覗けるんです。

x = [1, 3, 5, 6, 8, 10, 13, 16, 18, 19, 21, 22, 24]
y = x.select(&:even?).map(&:succ)

こんな風に、 配列から偶数だけを取り出して、 それぞれに 1 をたした配列を作りたいとします。

x = [1, 3, 5, 6, 8, 10, 13, 16, 18, 19, 21, 22, 24]
y = x.select(&:even?).tap{|t| p t}.map(&:succ)    # => [6, 8, 10, 16, 18, 22, 24]

こんな感じに途中に tap をはさめば、 select メソッド実行後の途中経過を見ることができるわけです。

デバッグっぽい使い方以外にも、 使い道がありますよ。

x = [2, 5, 8]
p x.inject({}){|h, t| h[t] = t ** 2; h}        # => {2 => 4, 5 => 10, 8 => 16}
p x.inject({}){|h, t| h.tap{h[t] = t ** 2}}    # => {2 => 4, 5 => 10, 8 => 16}

こんな感じに、 配列の各値をキーとして、 その 2 乗を値とするハッシュを作ろうとするわけです。 普通なら 2 行目みたいに inject 使えばいいんですが、 どうも ; h ってのが美しくない。 そこで、 tap の出番です。 多少冗長にはなりますが、 見た目が美しくなります。 効果には個人差があります。

まあ、 何をしているかというと、 破壊的メソッドを実行したときに自分自身が返るようにしているわけです。 要するにかっこつけです。 はい。

とまあ、 Ruby の豆知識でした。


comment ×0
03 / 13
Thu

PHP もやってみたいと思い始めた Ziphil です。

Ruby で開発をするとき、 私は基本的に 1 つのファイルに 1 つのクラスを書くようにしています。 あるクラスだけで使用される補助的なクラスだけは、 例外として、 用いられるクラスが書かれているファイルと同じファイルに書きます。 これは Java からの遺産です。

このとき、 MainCanvas.rb のようにファイル名をクラス名そのままにしてたんですが、 全部小文字が主流のようですね。 そういえば、 ライブラリ名は全部小文字だった。 別に何かこだわりがあってキャメルケースのファイル名にしてるわけではないので、 ここは慣習に乗ろうと思っています。 でも修正面倒だなぁ・・・。

ここからはまた違う話ですが、 Ruby の for 文って each メソッドのシンタックスシュガーじゃないですか。 私はこの for 文を結構使うんですが、 なんだか each だけ特別扱いってのもどうかと思うんですよね。 ということで、 for 文使うのをやめようかと。

# for 文
for x in 1..100
  for y in 1..(x * 2)
    x.do_something
    y.do_something
  end
end
# each
(1..100).each do |x|
  (1..(x * 2)).each do |y|
    x.do_something
    y.do_something
  end
end

これも、 正直どっちでも良いんですよね。 私が for 文をよく使うのは、 HSP とか Java とかの遺産だと思います。 これらの言語には each なんてないので。

それと、 1..100 っていうのは良いんですが、 1..(x * 2) って少し気持ち悪いんですよね。 あ、 別にカッコは不要で 1..x * 2 でも問題ないんですけど、 これはこれで * 2 が離れちゃって微妙。 じゃ、 Range.new(1, x * 2) で! いや、 いっそのこと、 each なんてやめて Numeric#step を使うとか。 この辺は、 個人の印象によりますね。

次行きましょう。 私のコードにこんな部分があります。

def load_short_weapon_file
  regexp = /(\d+);\s*(.+,\s*.+,\s*.+,\s*.+,\s*.+,\s*.+,\s*.+,\s*.+,\s*.+,\s*.+)/
  open("../data/data/database/item-1.txt") do |file|
    while line = file.gets
      if line =~ regexp
        # 省略
        data_string.split(/\s*,\s*/).each_with_index do |data, i|
          case keys[i][1]
            # 省略
          end
        end
      end
    end
  end
end   

最後に end が連なってますね。 Ruby の構文からして連なるのは仕方ないんですが、 一部の Rubyist には 「END HELL」 とまで言われて嫌われているそうです。 私は別に気にしませんけどね。

で、 ある人は言いました。 こうすれば良いではないか、 と。

def load_short_weapon_file
  regexp = /(\d+);\s*(.+,\s*.+,\s*.+,\s*.+,\s*.+,\s*.+,\s*.+,\s*.+,\s*.+,\s*.+)/
  open("../data/data/database/item-1.txt") do |file|
    while line = file.gets
      if line =~ regexp
        # 省略
        data_string.split(/\s*,\s*/).each_with_index do |data, i|
          case keys[i][1]
            # 省略
          ennnnnnd    # ←これ

変態的です。 嫌いじゃないですけど。 しかも、 これ parse.y とか書き換えれば実装できるんですよ。 いや、 でもこれインデントどうなるんだよ。

そうそう、 Python はインデントでブロックを明示するので、 そもそも end を書かないんですよね。 そのアイデアは良いと思うんですが、 閉じがないのでアンバランスな気がしないでもないです。

また話が変わります。 私、 RPG 開発などは JRuby でやってますが、 ちょっとした小物を作るときは生の Ruby を使ってます。 バージョンは 1.9.1 とすでに一昔前のものを使ってますが、 これはもうすぐパソコンを買い換えるので、 最新版をインストールするのが面倒なだけです。 CGI なんかは 1.8.7 で作ってます。 サポートはとっくに終わってることは知ってるんですが、 借りているサーバーが 1.8.7 なので仕方ない。

で、 最近というわけではもうなくなってますが、 Ruby 2.0 って何ができるの? いや、 最近は Ruby 2.1 が出てるみたいで、 それでは何ができるの? ・・・ということです。

一番の変化は、 やっぱりキーワード引数でしょうね。 1.9 でも最後の引数がハッシュの場合は、 ハッシュの波カッコを省略できるようになっていて、 これによってキーワード引数っぽいことができました。 2.0 は、 キーワード引数が正式な構文になったようです。 私はあまり使わないと思います。

2.1 からの新機能ですが、 どれを取り上げれば良いのか分かりませんが、 数値リテラルが増えました。 r をつけると Rational 型に、 i をつけると Complex 型に。 まあ、 これは良いんですが、 私は f がほしいです。 2.0 なんて書くより 2f の方がきれいだと思います。

後は、 def の返り値ですかね。 メソッド名の Symbol インスタンスを返します。 これによって、 private def ~ という書き方が可能になりました。 便利だと思います。 でも、 Ruby を使うときは、 私はあまり private とか public とか気にしたことがありません。

とまあ、 Ruby の雑多な話題でした。


comment ×0
03 / 09
Sun

IDE を NetBeans に変えてみた Ziphil です。

こんな風に定数を定義して。

BORDER = 1
ROOM = 2
PATH = 3
VERTICAL = 10
HORIZONTAL = 11

で、 こんな風に書けば、 定数を定義せずに数値を書くより、 何をしてるか分かりやすいじゃないですか。

if area.width >= minimum * 2 + 1
  bx = rand(area.width - minimum * 2) + minimum
  area.height.times do |j|
    @tiles[area.y + j][area.x + bx] = BORDER
  end 
  @areas[i..i] = area.devide(bx, VERTICAL)
end

正直、 定数の実際の値なんてどうでも良いじゃないですか。 ここでは、 VERTICAL の値を 10 にしてますが、 別に 100 でも 1000 でも良いんです。

数値である必要もなく、 VERTICAL = :vertical としても良いわけです。 しかし、 :vertical で具体的な意味が分かるので、 もはや定数を定義する意味がなくなります。 では、 定数の定義をやめてシンボルを使ってみると、 こうなります。

if area.width >= minimum * 2 + 1
  bx = rand(area.width - minimum * 2) + minimum
  area.height.times do |j|
    @tiles[area.y + j][area.x + bx] = :border
  end 
  @areas[i..i] = area.devide(bx, :vertical)
end

こうすれば、 定数の定義も必要なく、 かつ何をしているのか分かりやすい。 これでも・・・、良いんですよね?


03/09 リファレンスマニュアルを見てみると、 シンボルの用途に 「C の enum 的な使用」 というのがありました。 そもそも、 列挙型と定数って何が違うんでしょう。

例えば、 静的型付けの Java を例にとってみましょう。 定数を定義してみます。

public static final int VERTICAL = 10;
public static final int HORIZONTAL = 11;

このとき、 VERTICAL, HORIZONTAL はともにただの int 型の数値ですから、 VERTICAL もしくは HORIZONTAL を要求するメソッド引数に、 5 や 100 などの他の int 型数値を指定することができてしまいます。 型の保証が崩れます。 これを解消するものが列挙型だと思います。

public enum Direction {
  VERTICAL, HORIZONTAL
}

こうしておけば、 VERTICAL, HORIZONTAL は Direction 型ですから、 これらを要求するメソッドも Direction 型を要求するようにしておけば、 5 や 100 などの関係ない数値を指定するとエラーになってくれます。

と、 Java ではこのような恩恵が得られますが、 Ruby は動的型付けですから、 列挙型も定数も同じようなものになってしまう気がします。 ということで、 シンボルを列挙型の代用としても、 定数を使っても、 特に違いはないわけですから、 ここは好みの問題になるんでしょうか。


comment ×0
03 / 07
Fri

太ってきてやばいと感じる Ziphil です。

これまで 4 つ (Temple 型, Maze 型, Cavern 型, Wilderness 型) のダンジョン生成アルゴリズムを作ったので、 ちょっと速度比較をしてみましょうか。 マップのサイズは 50×50 としましょう。 こんな感じになりました。

                  user     system      total        real
temple:       0.007000   0.000000   0.007000 (  0.007000)
maze:         0.117000   0.000000   0.117000 (  0.117000)
cavern:       0.310000   0.000000   0.310000 (  0.310000)
wilderness:   0.375000   0.000000   0.375000 (  0.375000)

Temple 型だけ異様に速いですね。 まあ、 重い処理がありませんし、 当たり前と言えば当たり前ですが。

さて、 Cavern 型が重いのは、 セルオートマトンを実行する際に、 全マスの周囲の状態を調べる処理を 3 回も繰り返しているからでしょう。 Wilderness 型は、 2 点の通路作成が重いんだと思います。 ちなみに、 この通路作成はランダム要素が非常に強いので、 場合によっては Maze 型と同じくらいの速度でマップが生成されることもあります。

で、 処理が重いのか軽いのかは分かりましたが、 正直あまり気にしてません。 もしこの処理が毎フレーム行われるのなら、 0.3 秒もかかる処理は致命的ですが、 これはマップ生成時に 1 度だけ実行されるものなので、 正直 0.5 秒以内なら許容範囲だと思ってます。 軽いに超したことはないんですが。 ・・・でも、 やっぱりマップ遷移のときにフリーズするのは良くないかなぁ・・・。


comment ×0
back-to-top
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。