「Perl, Python, Ruby の比較」があまりにもいい加減な件

ちょっと遅くなったが以前宣言したとおり、書いておく。
Perl, Python, Ruby の比較」はどうも調査が足りないのか、結果的にかなり嘘のまじった文章になっている。あくまで、初心者の私見ですので気軽に聞き流してください。とはあるものの、はてブのコメント等で「参考になる」とか言っている人もいる*1ので一応指摘しておこうと思う。

3.2 次に Python

変数はデフォルトで局所変数となるので、 Perl のように my で宣言する必要はありません。

my を強制することで変数の typo を防げるという視点が抜けるのは若干フェアではないですね。

それから、Python には 対話モードがあり、個々の関数をテストできます。 この機能は大きめのプログラムを書くときに便利です。 これは PerlRuby には見られない長所です。

テストするなら doctest でも使っとけよ、というツッコミはさておき、PerlRuby でも対話モードを使ったテストは普通にできる。
Perl の場合、Term::Readline::Gnu に含まれる perlsh を使えば Python の場合とほぼ同じことができるが、標準機能だけでもある程度のことはできる。こんな感じ。

% cat Foo.pm
package Foo;

use strict;
use base qw(Exporter);
use warnings;

our @EXPORT = qw(foo);

sub foo { "foo"; }

1;
% perl -MFoo -de 1

Loading DB routines from perl5db.pl version 1.28
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(-e:1):   1
  DB<1> print foo;
foo

  DB<2>

Ruby の場合は標準で irb が付いてくるので、それを使えばよい。

% cat foo.rb
def foo
  return "foo"
end
% irb
irb(main):001:0> require 'foo'
=> true
irb(main):002:0> foo
=> "foo"
irb(main):003:0>

コマンドラインでロードするモジュールを直接指定しても良い。

% irb -r foo
irb(main):001:0> foo
=> "foo"
irb(main):002:0>

他にも Emacs を使っていれば inf-ruby を使って編集中のファイルの部分評価なんかもできる。PerlPython の場合もできるだろうし、Emacs に限らずそういうことができるエディタは少なくない。

main に相当する部分を、 if __name__=='__main__': のブロックに入れることによって、 Python によって直接読み込まれたとき以外は動作しないようにすることが出来ます。

これも別に PerlRuby でもできる。
Perl の場合

if ($0 eq __FILE__) {
    # do something
}

Ruby の場合

if $0 == __FILE__
  # do something
end

6.1 大規模プログラムの作成

PythonPerlRuby に比べて大規模プログラムが容易に作れるという利点があります。また、小さなスクリプトを大きなプログラムに育てることがやりやすい ということも Python の特徴です。 Python で大規模プログラムが作りやすいのは次の理由に拠ります。

  1. ソースファイルごとに名前空間が割り当てられる。
  2. 対話モードで関数を一つずつをテストすることが出来る。
  3. if __name__=='__main__' 以下にファイルごとのテストコードを書くことが出来る。

PerlRuby では名前空間を分けるための宣言をわざわざ書かなければいけません。 Python では1ファイル1名前空間と割り切り、 ファイル名を名前空間名とすることによって名前の衝突を上手に回避しています。

1ファイルで強制的に1つのモジュールになるのは悪くないと思う。が、module 宣言で囲ったり、package 宣言書いて、最後に「1;」を加えるのがそんなに面倒か。
PerlRuby でも対話モードが使え、スクリプトとしてロードされた場合の挙動を別に書けることは前述したが、そもそも大きなプログラムの一部のモジュールなら最初からモジュールとして意識して書く。ちょっとしたスクリプトから機能を切り出してモジュール化することもあるが、その場合もスクリプトをそのままモジュールにできるかといえばそんなことは無い。

6.2 ファイルの読み込み

Perl では基本的に while(<>) を使って一行ずつファイルを読み込みますが、 PythonRuby では read() を使ってファイルを一気に読み込むことも出来ます。 これは、PythonRuby が登場した 1990 年代はメモリーが安くなったために贅沢な使い方が出来るようになったためだと 思われます。

<>はリストコンテキストで評価すると全部一気に読み込む。Python/Ruby の read と同じことをしたければ以下のコードで良い。

my $data = join('', <STDIN>);

<追記>

my $data = do { local $/; <STDIN> };

コメントにて上のコードでいいのではないかとの指摘を受けた。そっか、一時的に行の区切り文字($/)を無効にすることで全部読めるのか。確かにこちらのほうがいい。
Perl のドキュメントにも下記のようなサンプルコードがある。

my $content = '';
open my $fh, "foo" or die $!;
{
    local $/;
    $content = <$fh>;
}
close $fh;

</追記>

6.3 リストの操作

Lisp にある mapcar, remove-if-not などのリストを操作してリストを返す関数は、 Perl, Python, Ruby では以下のようなっています。

  • Perl は map や grep 等の関数があるが、 foreach ブロックを使うのが一般的
  • Python にも map や filter 等の関数があるが、内包表現を使うのがよい。
  • Ruby は map, select, grep などのブロック付きメソッドを使ってあらわす。

このなかで Ruby が一番 Lisp に近い仕様になっています。PythonLisp とは一線を画しているようです。

Python で内包表現を使うのが普通ってのはそのとおりだけど、Perl だって map や grep を使うのは普通。もちろん使わない人もいるけど。
「要素が非負の実数の場合、その平方根を返す」例も自分ならこう書く(出力部分は省略)。

# perl
my @l = -3..3;
my @result = map { sqrt $_ } grep { $_ >= 0 } @l;

# ruby
l = (-3..3).to_a
result = l.select{|x| x >= 0 }.map{|x| Math.sqrt(x)}

# python
from math import sqrt
l = range(-3, 4)
result = [sqrt(x) for x in l if x >= 0]

6.4 引数の渡し方

  • Perl: 引数をフラットなリストに変換して値渡し。参照渡しをするにはプロトタイプを用いる。少し複雑。

別にプロトタイプを使わなくても参照を渡すことはできる。

  • Python: シークエンスは参照渡し、それ以外は値渡し。 レストパラメータ、オプショナルパラメータ、キーワードパラメータをサポート。 詳しくは Python チュートリアル 4.6 関数を定義する や Python 早めぐり 5. 関数定義 を見てください。

Sequence だけ参照渡しなんて事実は無い。Python の変数(オブジェクト)は全てインスタンスへの参照に過ぎないので、「参照の値」を渡しているというのが正確なところ。

  • Ruby: シークエンスは参照渡し、それ以外は値渡し。 レストパラメータ、オプショナルパラメータ、キーワードパラメータをサポート。書式は Python と同じ。 ただし、Web 上の document に記載無し。

参照渡し、値渡しの間違いは上と同じ。ドキュメントは探せばちゃんと見つかる。

編集履歴を見る限り、2004年9月の時点ではすでにきちんとした記述があるように見える。

6.6 高階関数

Python: 関数名が関数へのポインターをあらわす。
例えば、baz という関数を定義したとき、 baz(....) はその関数の呼び出し、 baz は関数へのポインターになる。関数のポインターを引数として渡したり、 for ループの中で 用いることが可能。(Eight Queen 本体 93 行目を参照)

ポインターPython において関数ってのはただの callable なオブジェクト。baz(...) は baz.__call__(...) と等価というだけの話。

Ruby: 出来ない?

出切る。ただし、Symbol を method メソッドに渡して Method オブジェクトを取得、 call メソッドを呼ぶという手順になるので Python ほど直感的とはいえないかもしれない。これは Ruby の場合はメソッドコールの括弧を省略できるのと、変数の名前空間とメソッドの名前空間が異なるため。

def foo()
  puts "foo"
end

m = method(:foo)
m.call

詳しい話は Yugui さんの「Rubyの呼び出し可能オブジェクトの比較(1) @ 2006年11月 @ ratio - rational - irrational @ IDM」を参照。

6.7 その他

Pythonオブジェクト指向を強要しないので Ruby より初学者には学びやすいかもしれません。

Rubyオブジェクト指向を強要し、Python は強要しない?

# Python
len(l)

# Ruby
l.length

といった違いだろうか。そんなに違うかなぁ?クラスを定義しないならそんなに変わらないと思うのだが。

*1:当然その逆のコメントもある。自分のコメントもそのひとつ