$Id: codingstyle.rd,v 1.6 2003/06/18 23:03:32 aamine Exp $
今日 (1999/02/01)、Linux 2.2.1 のソースをダウンロードしてきた ついでに始めてドキュメント群を読んでみたのだが、そこに CodingStyle なんて文書があった。Linux の作者の Linus 氏が Linux カーネルを 書く時のコーディングスタイルについて書いているのだが、これが なかなかおもしろい。さっそくまねをして、Ruby におけるコーディ ングスタイルについて書いてみようと思う。
とは言っても筆者はヒトのコーディングスタイルをどうこう言えるほど 偉くはないし、これを読む人に「ソースコードはこのように書け」と 言うつもりもない。この文書はあくまで「おれはこうじゃなきゃやだ!」 という自己主張である。
インデントは基本的に 2。インデントがでかすぎると end が離れて 美しくない。でも {....} のときは 4 のほうが格好いいようなので 4 だ。このへんは最近になってかなり趣味が変わった。
※ 有名な Ruby hacker の前田修吾氏はかつてインデントを「3」に していた。この理由について筆者(あおき)は
if true while true unless false return 1 end end end
のように end
がピッタリそろうのが素敵かなあ、と評したのだが
実際の理由は全然違ったようだ (
[ruby-list:18603]
参照)。
※※ 素敵という言葉は江戸時代にできたそうだ。 筆者はこのステキな響きが気にいっている。
Ruby ではメソッドの引数を囲う括弧を省略できる。最初はなんとなく 気にくわなかったが、一度味を知ってしまったらもうやめられない。 括弧はとってもつかれるキーなのだ。なにしろ、シフトがある。しかも ( と )で二回も。そのうえ隣りにあるのですぐ間違える。さらに、筆者は 106 キーボードで 101 キー配列を使っている都合上、ひとつだけずれて 印刷されていて気持ち悪い。そりゃー確かにほとんど自分のせいだったり するわけだけどとにかく括弧はいやなんだ。括弧なんてあっちいけー!
そうだ、書き忘れたがこれは呼びだしの場合だけだ。定義の時の仮引数の 括弧は絶対に省略しない。なぜならそれは単に見慣れなくて気持ちわるい から、ではなくて(それもあるが)、メソッド名を強調したいからである。 括弧をなくすとメソッド名と引数が並列に見えてしまう。ただし引数がない ときだけは省略する。
(追記 1) しばらく Ruby を書いていてときどき C を使ったりすると知らず 知らずのうちに括弧を省略して書いてしまい、パースエラーになって始めて 気付く……という恐ろしい噂がある。
(追記 2) そういうわけなので当然 Lisp は嫌いだ。でも Scheme は好きだ。
(追記 3) 同じ悩みを持つひとはキーボード最上段の記号キーと数字キーを 全部いれかえるとよいかもしれない。数字キーは普通はあまり使わない。 ……と書いてはみたものの、実際やってみたらすごく使いにくい。論理的 にはよろしくても、キーボードに関しては慣れがかなり影響する。
例えば引数がたくさんあるメソッド呼びだしでは
this_is_method_call_with_many_argument( this_is_argument_no_1, this_is_argument_no_2, this_is_argument_no_3 )
のようにするのか、
this_is_method_with_many_argument( this_is_argument_no_1, this_is_argument_no_2, this_is_argument_no_3 )
のようにするのか。また閉じ括弧の位置は最後の引数のうしろか、独立の
行か。Array
の []
や Hash
の {}
でも同様の問題が発生する。この問題
は自分でも行ったり来たりしていて決定打がないのだが、現在の結論とし
ては後の形式を使うことにしている。この書きかたのほうがずっと見やすい。
特にメソッドが値を返すときは後の形式のほうがイメージがとらえやすく
なる。また、if
や case
を右辺に書くときもこの形式(下図)。
val = case arg when String then arg when Fixnum then arg.id2name else arg.must String, Fixnum end
また閉じ括弧を独立させるのは絶対にやらないようになった。同じケタに 開き括弧と閉じ括弧を縦におくと、人間の目の錯覚で閉じ括弧のほうが 前にあるように見える。この錯覚のせいで見た目が気持ちわるい。また、 閉じ括弧が最後の文の後ろにあるほうが「かたまり感」があるような気がする。
それから、括弧のあとの空白を入れる・入れない も重要である。筆者はプ ログラムを書き始めた時からなぜか「括弧のあとには空白を入れるものだ!」 と信じていたのだが、C 言語を使い始めてからそうでもないことに気付いた。 特に K&R で空白を入れていなかったのがショックで、少なくとも C を使う ときは入れないようになった。
しかし Ruby ではやっぱり空白はあける。括弧を省略した場合とのつりあ い、見た目のよさなどを考えるとこれが一番よい。……と考えていたのだ が、最近はどんどん空白を消している。目が慣れると空白が間延びして見 えてくるみたいだ。
ちなみに、開き括弧の前に空白を入れたりするのは絶対にやってはいけない。 カスケードしたときに「かたまり感」がなくなるし、実際問題としても それを変な場所でやるとパースエラーになったりして不便である (これはまつもとさんが秘かに「思想をふりかけている」結果らしい)。
メソッドの前につけるピリオドは
receiver . method_name( arg1, arg2 )
のようにまわりに空白をおくことができる。挙句のはてには
receiver . method_name( arg1, arg2 )
などと次の行にメソッドを書くこともできる。まあ前のものは桁を揃え
たいとかその時々の理由があるなら許容範囲だが、後のほうの形式は絶
対に使ってはならない。例外は Just Another Ruby Hacker をつくると
きと「Ruby 言語不明瞭コンテスト」に出典するときだけである。いつ
もこんな邪悪なコーディングをしている人は Bignum
がオーバーフロー
してゼロに戻ってくるまで Ruby インタプリタに呪われるであろう (呪
われると一歩あるくたびに経験値が減る、わけではなくて、文末にセミ
コロンを書かないとパースエラーが出るようになるらしい)。
と書いておいたら前田(修)さんからメールが。メソッドチェーンが長く なった時に継続するのに便利だという反論である。しかし、ピリオドが 一個だけ置いてあっても見落す可能性大だし、筆者はそこまでしてつな がなくてもいいじゃんと思うのでやっぱりダメー。それでもやっぱりつ なげたいときは、せめてバックスラッシュを置いて継続を明示するのが よいと思う。
Ruby の特徴と言えば、徹底したオブジェクト指向もさることながら、 イテレータを忘れてはならない。特にイテレータは二通りの書きかたが できるのでいかにも論議を呼びそうで楽しみである。
イテレータは、一行におさめるときは {....} で、二行以上のときは
do....end
、が基本。なんたって美しい。それに、if
や while
が end
で終わるので、それとそろえたほうがよい。ただし例外として、end
が
重なり、かつ「範囲型」または「コンテキスト型」
のイテレータの時は {....} でもよいことにしている。
一行で書くときに {....} をつかうのは、そのほうが見た目がいいから
である。まず、かっこのほうが文字の密度が低い。これは重要だ。だが
もっと重要なのは式のバランスである。do
と end
を見てなにか
気付かないだろうか。両方とも重心は下にあるくせに、上にのびる棒が
端にある。それゆえ一行に書くとどうしても「いまにも倒れそう」な
イメージになるのだ。逆に、行をわけたときは do .... end
のほうが
バランスよく見える。
ところで最近また悩みの種が増えた。{|i| ... }
形式のときに、
each{|i| ... } each {|i| ... }
のどちらを使うかということだ。筆者は最近は下の形式が好みだ。 特に、イテレータへの引数がないときは断然下。 そのほうが密度が低くなって見やすい。
each{ |i| ... }
というのも一瞬考えてはみたんだけどこれは気持ちわるすぎるので却下。
なのでやっぱりメソッド名と {}
の間には空白を入れる。まとめると以下。
ret = arr.map {|i| i.dup } arr.map {|i| i.dup }.each {|i| p i }
うむっ。
イテレータブロックの引数の位置。大多数は
arr.each do |i|
だと思うが、ごくまれに
arr.each do |i|
というのを目にする。これは聞くところによると Smalltalk の 流儀らしいのだが、おれは絶対にやらない。こんなの気持ちわるいし、 Ruby は Smalltalk ではないので Ruby 流儀でゆくのが人の道である。
イテレータの引数が二つ以上ある場合、間に空白は入れるべきだろうか。
arr.each_with_index do |item, idx| arr.each_with_index do |item,idx|
これにはいまだ結論が出ていないがだいたいのルールはできてきた。 引数名が短い時は空けず、長い時は空ける。それから * が付く時も 空ける。それから最後に , が付く時 (それ以降の引数は捨てる、の印) にはその後にもスペース。
some_iterator do |a,b,c| some_iterator do |a, b, *c| some_iterator do |param, param, param, |
Ruby では文字列がいろんな書きかたができる。代表は「'」と「"」だが その他にも「% 文字列」や「ヒアドキュメント」もある。一番よく見かける のは、C や Perl の影響なのだろう、なんでもかんでも「"」にすること だが、そんなのは不可である。せっかくいろいろあるんだから全部場面に よって使いわけるのがよい。
まず、エスケープや式展開がなく、一行のときは常に「’」。このほう が見やすいしちょっと速い。式展開があるときは「”」。「”」が文字 列中に出てきてバックスラッシュが多くなるときは迷わず「%」を使う。 「%」は module_eval なんかと一緒に使うと特に相性がいい。最後に 使うのがヒアドキュメント。これを使うとインデントの最中にどーんと わりこんでくるのでめちゃくちゃ気持ちわるい。<<-HEREDOC を使えば インデントはできるが、インデント部分が取り除かれるわけではないので、 たとえば出力するための文字列には使えない。
コメントは # をつけて文のあとにちょこっとかく。これはかの有名な 「ソフトフェア作法」にもそれがよいと書いてあるからよいのだ。
内容についてはもうすでにいろいろ言われているので、少しだけにする。 コメントには自明のことはいちいち書かない。本当に重要なところ、 忘れてはいけないこと、注意をひきたいところに「だけ」書く。
Ruby ではソースコードがコメントである。 データ構造やコードの見かけに注意して、 自然と流れが読めてしまうコードを書くのがRuby はっかーとしての務めなのだ。 Ruby 作者のまつもとさんも 「ソースコードがドキュメントだ、バグも全て記述されている」 という名言を残しているではないかっ。
また Ruby ではクラス(モジュール)に特異メソッドを定義することで
定型メソッドを簡単に定義するメソッドを作ることができるので、
活用しよう。例えば def prop() @prop end
より attr_reader :prop
のほうが見やすい。
こういうことをやってコードを簡潔に、読みやすくできるということも
「Ruby ではソースコードがコメント」という主張の一部をなしているのである。
ちなみに埋め込みドキュメント ,(=begin...=end) は特に目的を持って 使われるもの(RD)なので、通常のコメントには # を使うほうがいい。 一時的に部分をまとめてコメントアウトしたいときは、,=begin c …… のように後ろに適当なもんを書いて使うと将来いいことがあるかもしれぬ。
(後記) しかし最近は RDoc が主流だ。
Ruby では演算子もいろいろ別の書き方ができる。and
は &&
でもいいし、
not は ! でもいいし、Perl や sh や Lisp みたく if
を and
にする
記法も有効だ。どっちにするかちょっと考えちゃうのだが、まず基本は
「アルファベットで書く」。記号が多すぎるとわけわかになる。そんで、
結合順位上困るときだけ && ||
を使う。
と言っても結合順位の概念はなかなか初心者にはわかりにくいので、簡 単に判定する方法を教えよう。読みくだすのである。そしてその時、記 号類はあせって読む。アルファベットはゆっくり読む。それでちゃんと 文章が意味ごとにまとまって読まれていたなら、たぶんそれで大丈夫だ。
C なんかは演算子の優先順位が腐ってるので(これは作者も認めてるらしい) 括弧を頻繁に使わないと安心できないけど、Ruby ではそのへんはよく 考えられているのでそうそう括弧を使うことはないと思う。非常にあいまいで ないかぎり、無駄な括弧をつけるのは避けてできるだけ見ためがすっきり するようにしたい。
then や do は最近は全て省略するようになった。 後置 if に変更しやすいのが理由である。 以下は以前書いていたものだが、記録のために残しておく。
if
・unless や while
などにつく then
と do
。これは絶対に省略しては
いけない。人間の美感の中では、バランスが非常に重要な役割を果たす。
if
や unless
はそのあとが(普通は)インデントされ、しかも最後の行が
end
だけになる。つまり、よほど条件が長くない限り、if
文全体は
「>」型になる。これは非常に見た目が悪い。
そんなことないというやつはバランス感覚がないのであるっ!
でも when
だけは特別に省略する。when
はずらずら縦に並ぶことがほ
とんどなので > 型にはならないし、全部に then
をつけるとくどくな
り密度も濃すぎる。
ただしもちろんこれは条件式の直後にコードが続かない場合の話である。
同じ行にコードを書くときは必然的に then
などが必要になるので考え
る必要はない。……え? セミコロン? なんだっけそれ?
本文が一文の時、修飾の if
・unless と普通の if
・unless、さらに and
と or
のどれを使うべきか。まず、メソッドの最初に置くガード文は修
飾の if unless
。メソッドの途中に置く異常値 return
は and or
。next
break
なんかが本文のときは修飾の if
。それ以外で本文がメインアル
ゴリズムの一部であるときは普通の if
。そして普通の if
文はどんな
に本文が短かくとも複数行に分ける。if
文を一行にまとめて書くとバ
ランスが変。
条件中の代入。当然使うでしょ。
while line = arr.shift ... end
とか、とっても便利。最近は ,-w つけても警告が出なくなった。わーい。
if
と unless
どっちを使うか。unless
が使えるときは積極的に使う。
else
も入ってきたら重要なほうが前に来るように使いわける。
これは C にもある、あれのこと。これを嫌う人は(かつての筆者と同様) いっぱいいるのだが、使い方さえ間違えなければ便利なものだ。 例えばこんなふうに。
ret = (i > 0) ? i : 0
Ruby では if
も値を持つのでそれでもいいのだけど、先に書いたように
筆者は if
を一行に書くのは嫌いなので、どうしてもこっちになる。
ちなみに意味も考えればこの例は次のようにできる。
j = [i, 0].max
実際に使うときはこっちだな。
Ruby ではメソッドの最後の return
は使っても使わなくても全く同じ
意味に書ける。よってこれは全部省略する。例えば次のように。
def expand( str ) File.expand_path(str) end def ==( obj ) MyClass === obj and @name == obj.name end
理由は、まず簡潔さ。それから「Ruby ではすべての式が値を持つ」と
いう基本思想を適切に表現するからである。if
も while
もイテレータ
もメソッドも同様に値を持つ。ならば書式も全部同じにするのが適切で
ある。あらゆる式の記述の中にオブジェクトの流れを見るのだ。
それと return
なしのほうがちょっとだけ効率もいい。それはなぜかと
いうと、Ruby Hacking Guide
でも見てくれればわかるのだが、return
は結局 longjmp
だからだ。
break
や next
も longjmp
。
Ruby では self
はたいてい省略できる。あたりまえみたいだが
Python なんかではそうではないらしい。実に情けない仕様である。
と書いているところからも想定できるように、self は全部省略する。
理由の第一は、その方がかっこいい。第二に、「デフォルトのターゲット は自分のいるところ」という規定は非常に人間の生理にかなったものであ る。たとえば、単に「郵便局に手紙だしてきて」と言った場合「郵便局」 はたぶん最寄りの郵便局であろう。「市役所どこですか」と言えばそれは その市内の市役所であろう。ニュースで「政府首脳が…」と言った場合、 普通は日本の政府首脳を指すであろう。だからこのようなデフォルト設定 は非常に「正しい」。同時に、それを利用するのも「正しい」。また第三 に、そうすることによってレシーバ指定の形式は全てオブジェクト外部か らのメソッドコールということになり、オブジェクトの隠蔽性を「体感」 できる。
ただし例外として、引数なしのメソッドの値を使うときは以下のように
self
を使う。
val = self.some_value
some_value()
としてもいいけど、どうもこの ,() は好きになれない。
C なら全然気にならないんだが。
ちなみに、これは有名な話だけど、セッターメソッド ( foo=
みたいなやつ)
では self
を省略できない。たとえば次のような場合。
def initialize( arg ) self.value = arg end
こういうとこで self を省略するとローカル変数の代入とみなされてしまう。 筆者ははまらないが他の言語から入った人ははまりやすいらしいので注意だ。
重大なことを忘れていた。文末の「;」(せみころん)について。 これは省略「しなければならない」。
筆者は空行を入れるのが好きだ。メソッドの途中でもガンガン空行を入れ る。以前つい魔がさして「ソースコードの中のコードと空行とコメントの 割合」などというものを測定するスクリプトを書いてしまったのだが、い ままで書いたスクリプトの結果を合計すると(含んでないのもあるが)、そ の結果は な、なんと!
コード 8652 行、空行 2710 行、コメント 208 行
という恐るべきものであった。コメントの少なさもさることながら、コー ドと空行が 3 : 1 ってのはちょっとびびる。単純に言えば、三行コード を書いたら一行あけてるわけだ。
こういう結果が出てみれば確かに思いあたることはある。筆者の「空行追 加ルール」を書いてみよう。
とまあ、これだけやれば空行が多くなるのはあたりまえかもしれない。し かし、空行の多さは決して悪い結果を呼ばないと思う。わかりやすいコー ドを書くためには、内容がわかりやすいだけではいけない。見た目にもわ かりやすくなくてはいけないのだ。スペースがなく、ぎっちりつまった文 章が読みにくいように、漢字だらけの文章が読みにくいように、あるいは こういうふうに句読点が少ない文は意味が把握しにくいように、文字がぎっ しりつまったソースコードも読みにくくなる。
しかもソースコードはいわば「論理のかたまり」だ。つまり、普通の文章 以上に内容が濃いはずなのである。それならばソースコードを読みやすく するためには、普通の文章以上にレイアウトや字の密度に気を配らなけれ ばならないのは当然ではないか。
最近はちょっとこなれてきて空行は前より少なくなりつつあるのだが、そ れでもやっぱり重要な区切りには必ず空行を入れる。以前どこかでメソッ ドの間に空行を入れずにぎっちりつめている例を見たのだが、はっきり言っ てそんなの論外である。メソッド間には当然空行を入れるべし。クラス間 はもっと当然である。
あとひとつ書くと、alias
と private
のまわりの空行。メソッド定義の
あと alias
や private
を書くときは以下のように alias
では空行を入れ
private
では入れない。
def foo .... end private :foo def bar .... end alias any bar
理由は、alias
は別のメソッド(インターフェイス)の定義だから。
一方 private
のほうはメソッド定義に付随する情報なのでくっつける。
alias
で思いだした。
最近 alias
の引数にシンボルを渡しているコードを見かける。
alias :new :old
attr
系からの連想でこうしてしまうようだが……こんなことをしてはいけない。
alias
はメソッド呼び出しではなくて構文である。
だからこそ引数の間にカンマがないのだ。
そこはわかっていながらなぜエセメソッドのような使いかたをするのか。
こんなのは def
のメソッド名部分に文字列を渡すようなもんである。
絶対にやめよう。
for 文の話。知っていると思うが、
Ruby の for
文は each
をラップするループ文である。
すなわち、
obj.each do |i| .... end
は単純に
for i in obj do .... end
に変形できる。違いは for
のほうが新スコープを導入しない
(結果としてちょっと速い) という点だけだ。
さて、for
と each
どっちを使うかだが、おれは常に each
派だ。
for
はテスト以外では一回も使ったことがない。理由は特になし。
Perl や sh などで for
を使った経験がないからかもしれない。
知っているだろうか、is_a?
が ,=== で代用できることを。
これを知ったのはメーリングリストで教えてもらったのが最初だが、
それ以来筆者は is_a?
を一回も使っていない。
なにしろ is_a?
は疲れるのだ。たかだか五文字にシフトが二回もある。
一方 === ならばシフトなしのうえに三文字ですむ。
ああ、=== はなんて偉いんだろう……と今日も思う。
(追記 1) ,=~ も使わないことにしている。 どこかに「,=~ は ,=== の古い書きかた」と書いてあった覚えがあるからだ。 そう言われると害はないとはいえついつい「新しい」ほうを使いたくなってしまう。 現代人の悲しい性である。
(追記 2) そのあと $'
系が使いたいときは ,=== でなく regexp.match
にする。
Regexp.last_match
って書くのは長くて疲れるからだ。
しかし when
に正規表現を置くときだけはどうにもならないので
あきらめて $'
を使う。