PHPとRubyの文法面および主要な機能にのみ注目して(ラフな)比較を行った。そのため、各種関数などにはほとんど触れていない。
やや便宜的な説明になるがPHPの式および文の特徴を以下に示す。
echoなど一見関数に見えるものの中には値を返さないものもある;で終えたものRubyの式および文の特徴を以下に示す。
ifやfor、メソッド定義も値を返す;を付けてもよい
「ブロック」といったときPHPとRubyで意味するところはかなり違う。
まずPHPでいうブロックは以下のように記述されるものである。
{ <文>... }
これは複数の文を一つにまとめるための記法であり、それ以上の特別な意味はなく、スコープを作ることもない。通常はifやforなどで複数の文を記述するために使われる。
これに対しRubyでいうブロックは以下のように記述されるものである。
proc { ... }
proc do ... end
method(...) { ... }
method(...) do ... end
{〜}の部分がブロックの主体であり、どちらのケースでもその内容をオブジェクト化する。オブジェクトはcallメソッド(やyieldメソッド)によって実行できる。
PHPのブロックとは違ってブロックが独自のスコープを作る。また、{〜}の中に書かれた文だけでなく、そのコンテキストの一部を含めてオブジェクト化するため、いわゆるクロージャとして機能する。
def create_block(s, n)
local_var = s
proc {local_var += n}
end
b1 = create_block(0, 3)
b2 = create_block(0, -5)
# b1 b2
b1.call #=> 3
b1.call #=> 6
b2.call #=> -5
b1.call #=> 9
b2.call #=> -10
PHPの定数は以下のような特徴を持つ。
_または英文字で始まり、_または英数字で構成されるtrue、false、nulldefine()関数で宣言する
trueを返すfalseを返し、E_NOTICEエラーを発生させるRubyの定数は以下のような特徴を持つ。
_または英数字で構成されるPHPの変数名は以下のルールに基く。
$で始まる_または英文が続き、_または英数字で構成されるPHPにはローカル変数、グローバル変数、オートグローバル変数の三類種の変数がある。
globalを使った宣言が必要となる。
global宣言が不要。
Rubyの変数はローカル変数、インスタンス変数、クラス変数、グローバル変数といった種類があり、それぞれ変数名の最初の数文字で種類を表す。
_か英小文字で始まり、_または英数字で構成される。ローカルスコープからアクセスできる。ローカルスコープには以下の種類があり、最も内側のものが優先される。
def〜end)class〜end)module〜end)@で始まり、_または英数字で構成される。そのオブジェクトのクラスのメソッド(インスタンスメソッド)からアクセスできる。
@@で始まり_または英数字で構成される。そのクラスとサブクラスと各クラスのインスタンスのメソッド(クラスメソッドとインスタンスメソッド)からアクセスでき、外部からはアクセスできない。クラスのインスタンス変数との違いに注意。
$で始まり、_または英数字で構成される。スクリプト中のどこからでもアクセスできる。ただし、$で始まる変数の一部のはグローバル変数ではないことに注意が必要(正規表現使用時の$1〜$9など)。
各変数ともそれぞれ、最初に代入が行われた場所での、それぞれが対応するスコープに所属することになる。
なおこれら以外に疑似変数と呼ばれるものがる。
self - 現在の実行の真体となるオブジェクトnil - 未定義値true - 真false - 偽PHPのデータには以下の分類と種類がある。
var_dump()関数を使うとデータ型やその内容を分かりやすく表示することができる。
各データ型は演算や比較に際して相互に自動変換される(type jugglingと呼ぶ)。データ同士の比較については以下の通り。
==や!=を用いる。自動型変換を伴うため、たとえば0 == ''は真になる。関連して、0、''、false、nullはすべて偽として扱われるため以下が成り立つ。
0 == FALSE // true '' == FALSE // true false == FALSE // true null == FALSE // true
===や!==を用いると型変換を伴わず、かつ、データ型の違いを考慮にいれた比較を行うことができる。このため0 === ''は偽になる。
0 === FALSE // false '' === FALSE // false false === FALSE // true null === FALSE // false
PHPのような意味でのデータ型はなく、すべてクラスで表現されている。
自動変換は限られたケースを除いて行われない。たとえば以下の場面で変換が行われる。
coerceメソッドによる)printメソッドやputsメソッドなどによる文字列への変換(実際にはto_strメソッドによる)実際にはある種のメソッドが、引数で与えられたオブジェクトに対して内部で特定のメソッドを呼び、得られた戻り値を対象に処理を行おうとしているだけであって、言語そのものには自動変換はない。
PHPのvar_dump()関数にあたるメソッドしてはpがあり、オブジェクトのクラスやその内容を表示できる(ppライブラリを使うとより見やすい形になる)。
Rubyにおけるオブジェク同士の比較については、通常以下の三種類がある(クラスによっては比較に類するその他のメソッドを提供している場合もある)。
==や!=を用いると意味的に等しいかどうかという点での比較ができる(実際には各クラスで定義されるメソッドである)。
0 == 0.0 #=> true 1 == 1.000000000000000 #=> true 1 == 1.000000000000001 #=> false 1 == 1.0000000000000001 #=> true
eql?メソッドを用いると引数で与えらたオブジェクトとの厳密な比較を行う(ように各クラスで定義されている)。通常はクラスの違いなども考慮された結果となる。
1 == 1.0000000000000001 #=> true 1.eql?(1.0000000000000001) #=> false
equal?メソッドは引数で指定されたオブジェクトが自分自身であるときだけ真を返す(通常再定義してはならない)。
s1 = "abc" s2 = "abc" s1 == s2 #=> true s1.eql?(s2) #=> true s1.equal?(s2) #=> false
基本的に代入はコピーになる。ただしリファレンスカウント機能により、ぎりぎりまで実際のコピーを遅らせることができるようになっている(ユーザは気にしなくてよい)。
このため変数とデータは一対一に対応することになる。複数の変数から同じデータを扱いたとき(関数から元データをいじりたいときなど)にはリファレンスを使った代入を用いる。
$a = "foo";
$b = $a;
$c = &$a;
$a .= "bar"; /* $aと$cはどちらも"foobar"になり
$bは"foo"のままになる */
$c = "baz"; /* $aと$cはどちらも"baz"になり
$bは"foo"のままになる */
PHPのリファレンスは変数に対して別名を付けるイメージに近い。この点でC言語のポインタやRubyでのリファレンスとは意味合いが異るといえる。
PHP4でのオブジェクト型の扱いは他のデータと同様だが、PHP5のオブジェクト型については、オブジェクトそのものではなくオブジェクトハンドルを扱う形になっており、実質的にリファレンスの扱いに近くなっている。
class foo { var $name = null; }
$a = new foo;
$b = $a;
$a->name = "foo;
var_dump($a->name, $b->name);
PHP4ではそれぞれ"foo"とNULLを示し、PHP5ではどちらも"foo"を示す。
基本的に変数が持つのはオブジェクトへのリファレンスになるため、代入が扱うのもリファレンスになる。ここでいうリファレンスはPHPのようなものではなくC言語のものに近い。
a = "foo" c = a # (a) a << "bar" # (b) c = "baz" # (c)
aとcは(a)において同じ文字列オブジェクトを参照するようになったため、(b)での文字列オブジェクトそのものに対する変更(破壊的な変更と呼ぶ)により、aで参照してもcで参照しても同じく"foobar"が得られる。一方、(c)ではcによって別の文字列オブジェクトを参照するようしたため、この時点でaとcによってそれぞれ別のオブジェクト("foobar"と"baz")にアクセスできるようになる。
オブジェクトは一つ、リファレンスは複数というのが一般的な状態であり、オブジェクトのコピーを作りたいときには明示的に行う必要がある。
PHPの文字列表現は以下の通り。
'〜''abc $foo \n'は"abc $foo \\n"になる。
"〜""abc $foo \n"は"abc <fooの値> \n"に展開される。
`〜`<<<に続けて識別子を記述することで始める。続く行のうち、行頭に識別子だけまたは識別子と;だけがある行までの部分を文字列の記述として扱う。
$a = <<<E foo bar E;
$aに"foo\nbar\n"が代入される。
if (<<<E
foo
E
== <<<E
foo
E
) { echo "OK\n"; }
if ("foo\n" == "foo\n") { echo "OK\n";}と同じ。
.で連結することができる。"foo" . "bar"は"foobar"と同じ意味になる。
対応するRubyの文字列表現は以下の通り。
'〜'%q!〜!のような記法によっても同等の表現ができる(!にあたる部分には任意の文字を使用できる)。
"〜"%Q!〜!または%!〜!のような記法も使用できる。
`〜`%x!〜!のような記法も使用できる。
<<に続けて識別子を記述することで始める。続く行のうち、行頭に識別子だけがある行までの部分を文字列の記述として扱う。一行に複数のヒアドキュメントを書くこともできる。
a = <<E foo bar E
aで"foo\nbar\n"にアクセスできるようになる。
if <<E == <<F foo E foo F puts "OK" end
if "foo\n" == "foo\n" then puts "OK" endと同じ。
ヒアドキュメント記法について詳細は次のような文法となっている。
<<に続く識別子を'でくくるとエスケープ文字や#{〜}の展開を行わない`でくくるとヒアドキュメント文字列をシェルで実行した出力で置き換える-を置くと終端を示す行の識別子をインデントできるようになる"foo" "bar"は"foobar"になる。
Rubyでは"〜"であっても変数の展開は行われない。その種の展開を行うためには"〜"の中で#{〜}という記法を用いる。するとその内部に記述した部分がRubyのコードとして実行され、その値で置き換えられる。
"foo#{bar}baz" #=> "foo<barの値>baz"
"foo#{1+1}bar" #=> "foo2bar"
なお、文字列ではないが、Rubyでは正規表現に対する文法上のサポートがあり、正規表現を/〜/で表現できる。%r!〜!のような記法も使用できる。表規表現中では#{〜}の展開も行うことができる。
PHP、Rubyともに基本的な記法は同じである。
PHPの整数型データはアーキテクチャのワードサイズに依存する。
0x7FFFFFFF // int(2147483647) 0x7FFFFFFF+1 // float(2147483648) 1073741824*1073741824 // float(1.15292150461E+18)
整数および不動小数点数ともに、組み込みのデータ型で必要な精度が得られない場合にはGMPモジュールを使用する。
Rubyの整数オブジェクトにはFixnumとBignumの二種類があり、アーキテクチャのワードサイズの1/4(32bitなら-2^30〜2^30-1)はFixnum、それ以上はBignumとなる。これらは自動的に変換される。
1073741824*1073741824 #=> 1152921504606846976
不動小数点数のサポートもあるが、必要な精度が得られない場合にはBigDecimalモジュールを使用する。
PHP、Rubyとも真偽値はtrueとfalseで表現する(PHPは大文字小文字を無視する)。
PHPではコンテキストによりtrue→1、false→0の変換が行われる。
true + true; // 2
また0、''、null、falseは偽として、trueを含むそれ以外の表現は真として扱われる。
Rubyではfalseとnilが偽、それ以外が真として扱われる。0や''も真である。
PHPではいわゆる配列と連想配列の両方を配列型データで扱うことができる。PHPの配列型はハッシュ的な構造に加え、順序を保存するリストを持っている。
要素へのアクセスキーは整数型または文字列型のデータを用いる。具体的なキー指定方法については以下の通りである。
[〜]を使う
キー指定のない[〜]は配列への要素の追加(最大の整数キーの次の整数をキーとする代入)を行う
$a = array(); $a[] = "foo"; // $a[0]に"foo"が代入される
未使用の変数に対して[〜]を使用すると即座に配列で初期化される
$a[][] = "foo"; /* 以下と同じ $a = array(); $a[0] = array(); $a[0][0] = "foo" */
数字だけからなる文字列は数数型に変換される
$a["3"] = "foo"; // キーは整数の3になる
不動小数点型は整数型に変換される
$a[3.0] = "foo"; # キーは整数の3になる
RUbyでは、いわゆる配列はArrayオブジェクト、連想配列はHashオブジェクトでそれぞれ扱う。クラスは異るが各要素へのアクセスキーの指定にはいずれも[〜]を用いる。
配列のアクセスキーは整数
a = [] # 空の配列
a[1] = "foo" # a[1]が"foo"になる
# ("foo"へのリファレンスが設定される)
a[1.0] = "bar" # 例外になる
a["1"] = "baz" # 例外になる
配列への追加はpushメソッドや<<メソッドを使う
a = [] # 空の配列の生成 a << "foo" # a[0]が"foo"になる
ハッシュのアクセスキーはほとんどの(詳細にはhashメソッドを持つ)オブジェクトが使用できる
h = {} # 空のハッシュ
h[1] = "foo"
h[1.0] = "bar"
h["1"] = "baz"
# h[1]は"foo"、h[1.0]は"bar"、
# h["1"]は"bazになっている
なお、ハッシュは順序を保存しない
対応するキーがない場合には配列やハッシュのオブジェクトを生成したときに指定したデフォルト値が返されるが、特に指定しなければnilが返されるため、PHPのような明示的な初期化をともなわない配列へのアクセスのようなことはできない(デフォルト値を適切に与えることなどで同様のこ効果は実現できる)。
PHPの条件分岐はifで記述する次の形になる。
if (<式>) <文> else if (<式>) <文> else <文>
<文>が複数の場合は {...} でブロックを作る。
Rubyの条件分岐もifで、次のような形になる。
if <式> <文> elsif <式> <文> else <文> end
<文>は複数であってもよい。ifも値を返す(最後に実行された文の値を返す)ので次のような書き方もできる。
print if i%2 == 0
"even"
else
"odd"
end
ifの代わりにunlessを使用すると論理的に逆の条件となる。
PHPの多条件分岐はswitchで表現する。
switch (<式>) {
case <データ1>:
<文1>
break;
case <データ2>:
<文2>
case <データ3>:
<文3>
break;
default:
<文4>
}
caseに指定するのは実質的には何らかの値である。case節ではbreakにあたるまで実行が続く(<データ2>にあたる場合、<文2>と<文3>が実行される)。
Rubyの多条件分岐はcaseで表現する。
case <式> when <オブジェクト>, <オブジェクト>, ... <文> when <オブジェクト>, <オブジェクト>, ... <文> else <文> end
条件はwhenで指定する。複数の条件を指定することもできる。when節の<文>の実行は継続されないためPHPのように終了を明示しなくてよい。
なお条件判定には<オブジェクト>の===メソッドが使用されるため、文字列や数値に対する一致判定だけでなはえと、多様なオブジェクトによる多様な条件を設定することができる。
===は==と同じ意味になり、オブジェクトの意味上の一致を判定するしかし、たとえば正規表現オブジェクトや範囲オブジェクトでは===によってパターンマッチや範囲内にあるかどうかの判定が行えるようになっている
case Time.now.hour when 9..12 # 9〜12時の場合の処理 when 12..13 # 12〜13時の場合の処理 when 13..18 # 13〜18時の場合の処理 end
PHPでは特定のデータ型によらない条件変数によるループはC言語と同様のスタイルとなる。
for (<式1>; <式2>; <式3>) <文>
配列の全要素に対する繰り返しは以下のいずれかの形をとる。
foreach (<配列> as <変数>) <文> foreach (<配列> as <変数1> => <変数2>) <文>
一般的な条件による繰り返しはwhileで表現する。
while (<式>) <文>
各繰り返しとも中断(break)と新しい繰り返しの開始(continue)ができる。
次にRubyの繰り返しについて。RubyにはPHPのforにあたる記法は文法としては提供されていない。
配列やハッシュに対する繰り返しは以下の形をとる。
for <変数> in <オブジェクト> <文> end for <変数>, <変数> in <オブジェクト> <文> end
実際にはこの記法はコンテナ系オブジェクト一般に使用できる。また、オブジェクトの内容物に対する繰り返しという点ではfor以外に各クラスで定義されるeach系メソッドが多く用いられる。
条件での繰り返しはwhileまたはuntilで表現する。
while <式> <文> end until <式> <文> end
各繰り返しとも中断(break)と新しい繰り返しの開始(next)、その繰り返しの再実行(redo)ができる。また、繰り返し全体のやり直し(retry)ができる(retryはbegin〜endでも使用できる)。
関数呼び出しは関数名に()を付け、その間に引数を書く。
function_name(...)
関数定義は以下のように行う。
function <関数名>(<引数1>, <引数2>=<値>, ...) {
<定義>
}
デフォルト値を指定した引数(例中の<引数2>)は呼び出し時に省略可能となる。引数はコピー渡しだが、関数定義の引数リストで&を前置した記述をしておくとリファレンスで受けることができる。
関数の戻り値はreturn()で返す。
メソッド呼び出しはメソッド名に基本的に()を付け、その間に引数を書く。
method_name(...)
ただし引数がなく、かつ、あいまいさがない場合には()を省略できる
メソッド定義は以下のように行う。
def <メソッド名>(<引数1>, <引数2>=<値>,
..., *<引数x>, &<ブロック引数>)
<定義>
end
デフォルト値を指定した引数(例中の<引数2>)は呼び出し時に省略可能となる。*を前置した引数(<引数x>)は、呼び出し時の引数を配列で受ける。
def foo(a, b=1, *c); end foo(1) # a=1, b=1, c=[] foo(1,2) # a=1, b=2, c=[] foo(1,2,3) # a=1, b=2, c=[3] foo(1,2,3,4,5) # a=1, b=2, c=[3,4,5]
&を前置した引数(<ブロック引数>)は、呼び出し時のブロックを受けることができる。
def foo(a, b, &block); end
foo(1, 2) # a=1, b=2, block=nil
foo(1, 2) { <文> } # a=1, b=2
# block.callで<文>を評価できる
引数はすべて参照渡しである。そのため渡されたオブジェクト自体を変更するようなメソッド(破壊的なメソッド)を発行すると呼び出し元にも影響が及ぶことになる。
def foo(a, b)
# オブジェクト自身を変更するメソッド
# (破壊的なメソッド)
a.tr!('a-z', 'A-Z')
# オブジェクトの内容に変換をかけた内容を持つ
# 新しいメソッドを生成するメソッド
b.tr('a-z', 'A-Z')
end
x, y = "foo", "bar"
foo(x, y)
この場合、fooメソッドの戻り値は"BAR"、xは"FOO"になり、yは"bar"のままとなる。
メソッドの戻り値はreturnで返すが、明示されていない場合は最後に評価された式の値が戻り値になる。
PHP4はエラーハンドラのみサポートする。エラーの生成は以下のようにする。
trigger_error(<メッセージ>, <タイプ>)
PHP5からtry〜catchによる例外処理をサポートした。
try {
<文>
} catch (<クラス> <変数>) {
<文>
} catch (<クラス> <変数>) {
<文>
}
tryブロックで生じた例外はcatchブロックのうちの<クラス>条件にマッチするもので対処することができる。例外の生成はthrowによって行う。
RubyにはPHPのエラーハンドラのような仕組みはなく、すべて例外によって処理する。
begin <文> rescue <クラス> => <変数> <文> rescue <クラス> => <変数> <文> ensure <文> end
begin〜endで生じた例外は指定された<クラス>条件にマッチするrescue節で対処できる。起きた例外は<変数>で参照できるが、この変数の指定を省略した場合には$!で参照できる。
例外が起きたかどうかによらず実行したい処理はensure節に書く。例外の生成はraiseメソッドによって行う。以下、あまり現実味のない例を示す。
begin
f = open("/path/to/file", "r")
begin
# ファイルに対する処理
f.each_line do |line|
...
end
rescue Exception
# 処理中に何らかのエラーが起きた
...
ensure
# 開いたファイルはここで閉じるておきたい(とする)
f.close
end
rescue
# ファイルを開けなかった
end
PHP、Rubyともに以下のいずれかで終了する。
exit(<終了ステータス>)関数(メソッド)の実行ただしRubyにはexitメソッドのほかにexit!という別のメソッドがある。両者の違いは以下の通り。
exit(<終了ステータス>)<終了ステータス>をもってプロセスを終了する<終了ステータス>を指定しなかった場合には0になるexit!(<終了ステータス>)<終了ステータス>をもってプロセスを終了する<終了ステータス>を指定しなかった場合には1になるfork&execする場合などで使われる)PHPの一行コメントは以下のように記述する。
// コメント # コメント
Rubyの一行コメントは以下のように記述する。
# コメント
PHPの複数行コメントは以下のようにC言語と同じスタイルで記述する。
/* ... ... ... */
Rubyの複数行コメントは以下のように行頭からの=beginと行頭からの=endではさむ形で記述する。ただしこの記法はRDドキュメントを埋め込むためにも使われることがある。
=begin ... ... ... =end