やりたいことがやりたい。

情報技術で学んだことや漫画の話を中心に、好きなことを色々書いていきます。

Rubyのオブジェクトの等値性

Rubyにおいて、オブジェクトの等値性を確かめる方法がいくつもあり、それぞれの方法で性質が違うので、ややこしくならないようにまとめておきます。

主な方法は以下の4種

  • equal?
  • ==
  • eql?
  • ===

equal?メソッド

2つの値が同一のオブジェクトを参照しているかをテストします。

a = "hoge"
b = c = "hoge"
a.equal? b   # => false
b.equal? c   # => true

aとbは同じ文字列 hoge ですが、オブジェクトが異なるので違うもの扱い。

a.object_id == b.object_id  # => false

という調べ方もあります。

== 演算子

Objectクラスでは、equal?メソッドと同じ意味ですが、他のクラスでは再定義されてequal?と異なる挙動をする場合が多い。

例えば、Stringクラスでは文字列が同じかどうか、Integerクラスでは整数値が同じかどうかをテストします。インスタンスが違っていても問題ありません。

a = "hoge"
b = "hoge"
x = 100
y = 100
a == b   # => true
x == y   # => true

eql?メソッド

Objectクラスではequal?の別名として定義されています。

eql?を再定義するクラスでは、型変換をしない厳格な==としてこのメソッドを使うことが多いようです。

1 == 1.0    # => true
1.eql? 1.0  # => false

=== 演算子

いわゆるcase等値で、その名の通りRubyのcase文でのマッチングに使われます。

多くの場合、==と同じ挙動をしますが、RangeRegExpClassあたりのクラスが特別な挙動をします。

(1..10) === 5
/[0-9]+/ === "123456789"
String === "string"

Rangeクラスでは、右辺の値が範囲内にあるかどうかをテストします。

RegExpクラスでは、右辺の文字列が正規表現にマッチするかどうかをテストします。

Classクラスでは、右辺のオブジェクトがそのクラスのインスタンスになっているかをテストします。

まとめ

どの比較方法もクラスによって挙動が同じだったり異なったりしていてややこしいです。

オブジェクトの等値性をチェックするときは、いくつか性質の違う方法があることを念頭に置いておくべきだと思いました。

RubyでAOJの問題を解く その2

今日もRubyAIZU ONLINE JUDGE (AOJ)の問題を解いていきます。

今日解いた問題はこの2つです。

  • Volume0 0004 (Simultaneous Equation)
  • Volume0 0005 (GCD and LCM)

Simultaneous Equation

Simultaneous Equation | Aizu Online Judge

これは連立方程式を解く問題ですね。

僕の解答はこれです。

require "matrix"

$<.map {|a| a.split.map(&:to_i)}.each do |a|
  Matrix[[a[0], a[1]], [a[3], a[4]]]
    .lup
    .solve([a[2], a[5]])
    .map{|b| b.to_f.round(3)}
    .tap{|c| print format("%.3f %.3f\n", c[0], c[1])}
end

結構長くなってしまいました (汗)

一応ざっくり解説しておくと、3行目で標準入力を適当にさばき、4-5行目でLU分解というもので方程式を解きます。

その後、6行目で小数第4桁を四捨五入し、7行目で指定された形式で標準出力しています。

今回の問題では、LU分解なんてものを持ち込まなくても、もっと簡単に解けば良かったです。

GCD and LCM

GCD and LCM | Aizu Online Judge

最大公約数と最小公倍数を求める問題です。

僕の解答はこれです。

$<.map{|a| a.split.map(&:to_i)}
  .each{|a| puts "#{a[0].gcd(a[1])} #{a[0].lcm(a[1])}"}

多少なりとも分かりやすくするために、2行で表示しています。

これはRubyのIntegerクラスが標準で備えているgcdlcmメソッドを使えば良いだけなので、簡単ですね。

コード量の少なさランキングは執筆時点で23位でした!

まとめ

今解いている問題はいずれも簡単なものなので、解くこと自体はすぐにできますが、コード量の削減を考え出すとなかなか難しいです...

ですが、ただ解くよりも全然楽しいので、これからもなるべく短いコードを書けるように頑張りつつ、Rubyの勉強をしていきます^^

RubyでAOJの問題を解く その1

最近、Rubyを勉強したい欲が高まっていて、アプリケーションを作るまではいかなくても、何か軽い題材が欲しいなーと思っていました。

そこで、目についたのが、AIZU ONLINE JUDGE (AOJ) というプログラミング問題のオンライン採点サービスです。

いわゆる競技プログラミングの問題を解いて、それが正解かどうかを判定してくれるシステムになっています。

他の人とプログラムの実行速度やコードの量を競うことができ、他のユーザのソースコードを見て勉強することもできます。

僕は、Rubyで問題を解いていくことにしたのですが、 (特に序盤の) 簡単な問題はなるべくコード量を減らして解くことを目標に設定しています。

これから、たまにAOJ関係の記事が出てくると思いますが、変なコードだと思ったら指摘頂けると嬉しいです:)

今日解いた問題。

  • Volume0 0003 (Is it a Right Triangle?)

Is it a Right Triangle?

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0003&lang=jp

僕の回答

$<.map(&:split)[1..-1]
  .map{|e| e.map(&:to_i).sort}
  .each{|e| Math.hypot(e[0],e[1]) == e[2] ? puts("YES") : puts("NO")}

1行目で標準入力を配列に分割。2行目は各辺の長さが文字列扱いなので整数型にした後に昇順ソート。3行目で直角三角形かどうかの判断をしてYES/NO出力をしています。

もっと短くできそうだなーと思いながらも思いつきませんでした... (ちなみにコード量ランキングではこの記事を執筆時点で80位でした。僕のコードの半分くらいのものもあって本当にすごいです...)

まとめ

自分が思っているよりも、Ruby力が低いことを実感しました。 (コード量の少なさで1番だった人のコードを見ましたが、よくわかりませんでした...)

今のところ大正義mapに頼りまくっている感じがありますね笑

でも、書いていて楽しかったので、これからもちょこちょこ時間を見つけて頑張って問題を解いていこうと思います!