原文:10 Things Every Java Programmer Should Know About Ruby
Jim Weirich
コンサルタント / Compuware
jim@weirichhouse.org
Copyright 2005 by Jim Weirich (All Rights Reserved)
私は以前、大規模製造会社の従業員に就業後のC言語コースを教えていました。提出されたCのコードのスタイルをちょっと見ただけで、生徒が以前にどんな言語を利用していたかを簡単に当てられました。全くもって本当のことです。「どの言語でも、君はFORTRANを書けるね」
Rubyを調査中のJavaプログラマなら、Rubyが色々な点でJavaに似ていることに気づくはずです。クラスがあり、モジュールがあり、ネームスペース、スコープ、インスタンス変数、あとメソッドもあります。このためJavaプログラマは、このオブジェクト指向言語はお手のものだと感じるかもしれません。
そして、そのままJavaのスタイルでプログラムを作成し続けたくなることでしょう。
もちろん異なる点もいくつかは存在します。(たぶん最初の衝撃は、型宣言がないことです)
でも少し努力すればなんとかなります。さもなければ絶好の機会を見失ってしまうことになるでしょう。
サピア・ウォーフの仮説は、「思考と行為は言語により決定される(少なくとも部分的には)」という理論です。 […] 今日まで、議論は決着していませんし、完全に証明されてもいません。しかし、世界中の研究者の興味を集め続けています。
引用
プログラムについての考察を与えない言語は知る価値が無い
―Alan Perlis
この記事は「RubyはJavaより素晴らしい」という類のものではありません。
Javaには「これもない」「あれもない」という論法は、JavaやJavaプログラマを強く非難することになりがちです。
それよりも「RubyでJavaを書く」という状態を乗り越えるのを助け、Rubyを調査中のJavaプログラマがRuby Wayを発見することの一助になればと思っています。
では早速、10の事柄を...
いくつかの慣習は言語自体で規定されています。その他はコミュニティで標準に使われているだけです。
Rubyでは、変数名へ割り当てられるもの全てが完全なオブジェクトです。
これは面白い効果があります。
a = Array.new
クラスは自らインスタンスを作成するため、究極のファクトリオブジェクトと言えます。
def create_from_factory(factory)
factory.new
end
obj = create_from_factory(Array)
0.zero? # => true
1.zero? # => false
1.abs # => 1
-1.abs # => 1
1.methods # => 1オブジェクトのメソッドのリスト
2.+(3) # => 5 (2+3と同じ)
10.class # => Fixnum
(10**100).class
# => Bignum
a = nil a.nil? # => true a.methods # => メソッドのリスト a.abs # => NoMethodError(メソッドが存在しないエラー)
def with_block
yield
end
with_block {
# 決して変換されない
}
def with_proc(&block)
block.call
end
with_proc {
# 内部的に変換される
}
Rubyの全ての処理は以下を通じて実行されます:
Javaプログラマはobj.method()を、「メンバ関数の一覧から関数を探してきて、それを実行する」と考えがちです。
Rubyプログラマはobj.methodを、「メッセージをオブジェクトへ送る」というように考えます。
違いは僅かですが重要です!
以下のようなクラスを考えてください。ここでは今まで送られたすべてのメッセージを記録できるオブジェクトを定義しています。そして、それらのメッセージを別のオブジェクトへ再度送信することもできます。
class VCR
def initialize
@messages = []
end
def method_missing(method, *args, &block)
@messages << [method, args, block]
end
def play_back_to(obj)
@messages.each do |method, args, block|
obj.send(method, *args, &block)
end
end
end
サンプルコード
require 'src/vcr'
vcr = VCR.new
vcr.sub!(/Java/) { "Ruby" }
vcr.upcase!
vcr[11,5] = "Universe"
vcr << "!"
string = "Hello Java World"
puts string
vcr.play_back_to(string)
puts string
出力
Hello Java World HELLO RUBY Universe!
C++に比べた時のJavaの大きな魅力の1つは、言語の動的な側面です。実行時にクラスをロードし、オブジェクトの持つクラスやメソッドを調べて、その時に発見されたメソッドを呼ぶこともできます。
RubyはJavaよりも幾つかの点でダイナミックに動作します。
Javaバージョン
public static Object create(Class c, String value)
throws Exception
{
Constructor ctor = c.getConstructor(
new Class[] { String.class } );
return ctor.newInstance(
new Object[] { "Hello" } );
}
public static void main (String args[])
throws Exception
{
Greeting g =
(Greeting) create(Greeting.class, "Hello");
g.show();
}
Rubyバージョン
def create(klass, value)
klass.new(value)
end
create(Greeting, "Hello")
クラスの好きな場所にメソッドを追加できます。これはビルトインクラスに対しても同様です。
class Integer
def even?
(self % 2) == 0
end
end
p (1..10).select { |n| n.even? }
# => [2, 4, 6, 8, 10]
注意が通知されますが、この機能は非常に便利です。
特異メソッドはクラスではなく、個々のオブジェクトに定義されます。
class Dog
end
rover = Dog.new
fido = Dog.new
def rover.speak
puts "Red Rover"
end
rover.speak # => "Red Rover"
fido.speak # => NoMethodError
フックにより、プログラム実行時の気になる瞬間をプログラマがコントロールすることができます。
class MyClass
def MyClass.method_added(name)
puts "Adding Method #{name}"
end
def new_method
# 省略
end
end
出力
Adding Method new_method
class Module
def trace_attr(sym)
self.module_eval %{
def #{sym}
printf "Accessing %s with value %s\n",
"#{sym}", @#{sym}.inspect
@#{sym}
end
}
end
end
class Dog
trace_attr :name
def initialize(string)
@name = string
end
end
Dog.new("Fido").name # => Accessing name with value "Fido"
型とは
「値の集合」
と
「操作の集合」
Cのコード (弱い型付け)
#include <stdio.h>
extern float two();
int main() {
float x = 1.5 + two();
printf("%f\n", x);
printf("%d\n", x);
return 0;
}
int two() { return 2; }
出力
nan 0
Javaのコード (強い型付け)
public class Main {
public static
void main (String args[]) {
double x = 1.5 + Two.two();
System.out.println(x);
}
}
public class Two {
public static int two() {
return 2;
}
}
出力
3.5
Rubyのコード (?)
require 'two' x = 1.5 + two puts x printf "%d", x
def two
2
end
出力
3.5 3
または...
Rubyのコード
def factorial(n)
result = 1
(2..n).each do |i|
result *= i
end
result
end
puts factorial(20)
puts factorial(21)
出力
2432902008176640000 51090942171709440000
Javaのコード
public class Fact {
static long factorial(long n) {
long result = 1;
for (long i=2; i<=n; i++)
result *= i;
return result;
}
public static
void main (String args[]) {
System.out.println(factorial(20));
System.out.println(factorial(21));
}
}
出力
2432902008176640000 -4249290049419214848
| Java | Ruby |
|---|---|
| 強い型付け | 強い型付け |
| 静的 | 動的 |
| 明示的 | 暗黙的 |
Bob Martin (http://www.artima.com/weblogs/viewpost.jsp?thread=4639)
長い間私は、頑固な静的な型付け支持者でした。Cを使っていた頃に、身をもって教訓を得ていきました。たくさんのシステムが、ばかげた型エラーのせいで本稼動中にクラッシュしました。 […]
4年前に、XP(Extreme Programming)に関わりました。[…]今では、開発をバックアップする包括的な単体テスト一式がないプロジェクトは想像できません。
[…]
2年程前に、私はあることに気が付きました。それは、私がどんどん安全のために存在する型付けの機構を当てにしなくなっていたことです。単体テストが型エラーの発生を防いでいてくれていたためです。[…]
そしてPythonでアプリケーションをいくつか書き、続いてRubyでも書いてみました。型に関する問題が全く発生しないことがわかったときも、全然驚きませんでしたよ。
RubyはDuck Typingを使います。
class Duck
def talk() puts "Quack" end
end
class DuckLikeObject
def talk() puts "Kwak" end
end
flock = [
Duck.new,
DuckLikeObject.new ]
flock.each do |d| d.talk end
Rubyにはインタフェースがありませんが、モジュールを使うことでMix-inを実現できます。
モジュールとは...
全てのロジックは未満(<)メソッドにあるにも関わらず、その他全ての比較演算子も定義しなければいけません。
class Pair
attr_accessor :first, :second
# ...
def <(other)
(first < other.first) ||
(first == other.first && second < other.second)
end
def >(other)
other < self
end
# 未満(<)に関連する他の比較演算子の定義:
# <=, >=, ==
end
Mix-inにより、共通点を抜き出すことができます。
module LessComparable
def >(other)
other < self
end
# 未満(<)に関連する他の比較演算子の定義:
# <=, >=, ==
end
class Pair
include LessComparable
attr_accessor :first, :second
# ...
def <(other)
(first < other.first) ||
(first == other.first && second < other.second)
end
end
繰り返し
[1,2,3].each do |item| puts item end
リソース管理
file_contents = open(file_name) { |f| f.read }
コールバック
widget.on_button_press { puts "Got Button Press" }
$ ri Array
---------------------------------------------------------- Module: Array
Arrays are ordered, integer-indexed collections of any object.
Array indexing starts at 0, as in C or Java. A negative index is
assumed to be relative to the end of the array---that is, an index
of -1 indicates the last element of the array, -2 is the next to
last element in the array, and so on.
------------------------------------------------------------------------
Includes:
---------
Enumerable(all?, any?, collect, detect, each_with_index, entries,
find, find_all, grep, include?, inject, map, max, member?, min,
partition, reject, select, sort, sort_by, to_a, zip)
Class methods:
--------------
[], new
Instance methods:
-----------------
&, *, +, -, <<, <=>, ==, [], []=, assoc, at, clear, collect,
collect!, compact, compact!, concat, delete, delete_at, delete_if,
each, each_index, empty?, eql?, fetch, fill, first, flatten,
flatten!, frozen?, hash, include?, index, indexes, indices, insert,
inspect, join, last, length, map, map!, nitems, pack, pop, push,
rassoc, reject, reject!, replace, reverse, reverse!, reverse_each,
rindex, select, shift, slice, slice!, sort, sort!, to_a, to_ary,
to_s, transpose, uniq, uniq!, unshift, values_at, zip, |
インスタンスメソッドlastに関する問い合わせ結果...
$ ri Array#last
------------------------------------------------------------- Array#last
array.last => obj or nil
array.last(n) => an_array
------------------------------------------------------------------------
Returns the last element(s) of _self_. If the array is empty, the
first form returns +nil+.
[ "w", "x", "y", "z" ].last #=> "z"
1+2を行い、Procで定義されているメソッドを取得しています...
$ irb --simple-prompt
>> 1 + 2
=> 3
>> Proc.instance_methods(false)
=> ["[]", "==", "dup", "call", "binding", "to_s",
"clone", "to_proc", "arity"]
同僚曰く (意訳)
問題を解決するために、Rubyを試しに使ってみました。「コードをちょっと書いてみて...、気が付いたら、それで仕事が終わっていたんです。」
例
(10) Rubyの慣習を学ぶ
(9) 全てがオブジェクト
(8) (ほとんど) 全てがメッセージ
(7) Ruby は想像以上にダイナミック
(6) オブジェクトは静的ではない強い型付けを持つ
(5) インターフェイスに関する心配は無用
(4) Mix-inでミックスする
(3) クロージャーの受け入れ
(2) ri は相方、irb は別の相方
(1) 大量のコードを書かない!
この作品は、クリエイティブ・コモンズ・ライセンス(帰属 - 非営利 2.0)の下でライセンスされています。
あなたは以下の条件に従う場合に限り、自由に本作品を複製、頒布、展示、上演、演奏、口述、上映、公衆送信することができます。また二次的著作物を作成することができます。
(http://creativecommons.org/licenses/by-nc/2.0/ 詳細はこちらを参照)