RubyCodingStyle

2005-05-14 15:48:46 +0900 (4272d); rev 16

青木が使っている Ruby のコーディングスタイルです。

インデント

インデントは 2。 インデントがでかすぎると end が離れて美しくない。 {....} のインデントだけを 4 にしてみた時期もあったが、 やっぱり全部 2 にしたほうが単純だし統一感がある。

またタブは一切信用せずに全部スペースにする。

※ 有名な Ruby hacker の前田修吾氏はかつてインデントを「3」にしていた。 この理由について青木は

if true
   while true
      unless false
         return 1
      end
   end
end

のように end がピッタリそろうのが素敵かなあ、 と評したのだが、実際の理由は全然違ったようだ。 → ?[ruby-list:18603]

※※ 素敵という言葉は江戸時代にできたそうだ。 筆者はこのステキな響きが気にいっている。

識別子

識別子の命名規則について。 これはわりと Ruby のスタンダードに近いな。

モジュール名・クラス名には CamelCase を使う。 それを含むファイル名には、メインクラス名を downcase したものを使う。

定数は基本的に CONST_NAME スタイルを使う。

変数名やメソッド名では this_is_identifier スタイルを使う。

括弧の省略

Ruby ではメソッドの引数を囲う括弧を省略できる。 最初はなんとなく気に食わなくて意地で全部につけていたが、 一度味を知ってしまったらもうやめられない。

その後「省略できるなら全部省略」期を経て、 いまは「値が必要ないときは省略」ルールを使っている。 つまり、function の呼び出しでは必ず括弧を付けて、 procedure の呼び出しでは必ず括弧を省略する (別名 VB ルール)。

lvar = m()
lvar = m(arg)
lvar = obj.m(arg)

def m
  call(arg)   # m が値を返すことを示す
end

def m
  call arg   # m の返り値は重要でない
end

def m
  call   # これも同じ。m の返り値は重要でない。
end

ただしいくつか例外がある。

  1. レシーバがあり、引数がないときは省略する。
  2. 「*」引数や「&」引数が最初に来るときは括弧を付けないと警告が出るので必ず付ける。

つまり以下のようにする。

# (1)
lvar = obj.m   # 必ず省略する

# (2)
call(*args)
call(&block)

引数がないときに括弧を省略するのは、 属性ぽく使うためである。 それと Eiffelっぽいというのもある。

なお、定義の時の仮引数の括弧は絶対に省略しない。

def m(arg)   # 絶対に省略しない!
  ....
end

なぜならそれは単に見慣れなくて気持ちわるいから、 ではなくて (それもあるが)、メソッド名を強調したいからである。 括弧をなくすとメソッド名と引数が並列に見えてしまう。

また、この場合も引数がないときだけは省略する。

def m   # 引数がないときは必ず省略
  ....
end

def m()   # これはやらない
  ....
end

メソッド括弧と空白

以前は括弧の内側に空白を入れていたが、 最近は全く入れなくなった。

def m(arg)
  ....
end

m(arg)
obj.m(arg)

両方やってみてわかったのは、 コードに目が慣れると空白がないほうが読みやすい、ということだ。

ちなみに、開き括弧の前に空白を入れるのは絶対にやらない。 カスケードしたときに「かたまり感」がなくなるし、 実際問題としてもそれを変な場所でやるとパースエラーになったりして不便である (これはまつもとさんが秘かに「思想をふりかけている」結果らしい)。

行の折り返し

例えば引数がたくさんあるメソッド呼びだしでは

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 cmd
      when 'view' then handle_view(cgi)
      when 'edit' then handle_edit(cgi)
      else
        raise "unexpected command: #{cmd}"
      end

また閉じ括弧を独立させるのは絶対にやらないようになった。 同じ桁に開き括弧と閉じ括弧を縦におくと、 人間の目の錯覚で閉じ括弧のほうが前にあるように見える。 この錯覚のせいで見た目が気持ちわるい。 また、閉じ括弧が最後の文の後ろにあるほうが「かたまり感」があるような気がする。

メソッド呼び出しピリオドのまわりの空白

メソッドの前につけるピリオドは

receiver   .    method_name(arg1, arg2)

のようにまわりに空白をおくことができる。挙句のはてには

receiver   .
    method_name(arg1, arg2)

などと次の行にメソッドを書くこともできる。 まあ前のものは桁を揃えたいとかその時々の理由があるならいいと思うが、 後者はあまりやりたくない。 ピリオドが一個だけ置いてあっても見落す可能性大だ。 どうしてもつなげたいときは、 バックスラッシュを置いて継続を明示するほうがマシだと思う。

# 最近こういうのをよくやる
def entries
  Dir.entries(@wc_read)\
      .select {|ent| File.file?("#{@wc_read}/#{ent}") }\
      .map {|ent| decode_filename(ent) }
end

イテレータ

Ruby の特徴と言えば、徹底したオブジェクト指向もさることながら、 イテレータを忘れてはならない。特にイテレータは二通りの書きかたが できるのでいかにも論議を呼びそうで楽しみである。

まず基本的な使いわけとして、 複数回呼ぶ可能性があるとき (例えば Enumerable#each) は do....end を使う。 呼ぶのが一回だけのとき (例えば File.open) は {....} を使う。 ここで、後者の「一回だけのとき」とは RubyIteratorPattern で言う 「範囲型」あるいは「コンテキスト型」のことである。

加えて、一行に収めるときは {....} を使う。

さらに、返り値を使うときも {....} を使う。

イテレータ括弧

イテレータが {|i| ... } 形式のとき、どこに空白を入れるか。 これはわりと昔から同じで、以下のようにしている。

list.each {|i| ... }
list.inject(0) {|sum, n| sum + n }

ちょっとだけ

each{ |i| ... }

というのも考えたことはあるのだが、気持ち悪いので却下。

イテレータ引数

イテレータブロックの引数の位置をどうするか。 大多数は

arr.each do |i|

だと思うが、ごくまれに

arr.each do
|i|

というのを目にする。 これは聞くところによると Smalltalk の流儀らしいのだが、 おれは絶対にやらない。 Ruby は Smalltalk ではないので Ruby 流儀でゆくのが人の道である。

※ ちなみに、Smalltalk の |var| はブロック変数ではなくて ローカル変数の宣言なんだそうです。(thanks: sumim さん)

あと、なぜか知らんが外人は「|」の内側にスペースを入れたがる傾向が あるように思える。俺は決して入れない。

イテレータ引数 (2)

イテレータの引数が二つ以上ある場合、間に空白は入れるべきだろうか。

arr.each_with_index do |item, idx|
arr.each_with_index do |item,idx|

メソッドの引数では悩むことすらなく当然のように空白を入れるのだが、 なぜかイテレータの引数だと悩んでしまう。

……と、書いていたのだが、最近は常に入れている。

文字列リテラル

Ruby では文字列がいろんな書きかたができる。 代表は「'」と「"」だが、その他にも「% 文字列」や「ヒアドキュメント」もある。 一番よく見かけるのは、C や Perl の影響なのだろう、 なんでもかんでも「"」にすることだが、そんなのは不可である。 せっかくいろいろあるんだから全部場面によって使いわけるのがよい。

まず、エスケープや式展開がなく、一行のときは常に「'」。 このほうが見やすいじゃないか。

エスケープや式展開があるときは「""」にする。 ちなみに、\n はできるだけ使わず puts を使うほうが好きだ。

「"」が文字列中に出てきてバックスラッシュが多くなるときは 迷わず % 文字列を使う。

最後に使うのがヒアドキュメント。 これを使うとインデントの最中に割り込んでくるので気持ちわるい。 <<-HEREDOC を使えばインデントはできるが、 インデント部分が取り除かれるわけではないので、 例えば出力するための文字列には使えない。

※ unindent メソッドを書けばインデントできないこともないけどね。

コメント

コメントは # をつけて文のあとにちょこっとかく。これはかの有名な 「ソフトフェア作法」にもそれがよいと書いてあるからよいのだ。

内容についてはもうすでにいろいろ言われているので、少しだけにする。 コメントには自明のことはいちいち書かない。本当に重要なところ、 忘れてはいけないこと、注意をひきたいところにだけ書く。

Ruby ではソースコードがコメントである。 データ構造やコードの見かけに注意して、 自然と流れが読めてしまうコードを書くのがRuby はっかーとしての務めなのだ。 Ruby 作者のまつもとさんも 「ソースコードがドキュメントだ、バグも全て記述されている」 という名言を残しているではないかっ。

また Ruby ではクラス (モジュール) に特異メソッドを定義することで 定型メソッドを簡単に定義するメソッドを作ることができるので、 活用しよう。例えば

def prop() @prop end

よりも

attr_reader :prop

のほうが見やすい。 こういうことをやってコードを簡潔に、読みやすくできるということも 「Ruby ではソースコードがコメント」という主張の一部をなしているのである。

ちなみに埋め込みドキュメント (=begin...=end) はあまり使わない。 最近はソース埋め込みドキュメントは RDoc で Go、が主流だし、 使うのはせいぜいデバッグ中に一部をまとめてコメントアウトするときくらいか。

演算子とか

Ruby では演算子もいろいろ別の書き方ができる。 and は && でもいいし、not は ! でもいい。 Perl や sh や Lisp みたいに if を and にする記法も有効だ。

ではどちらを使うかと言うと、これも括弧の省略規則と同じで、 全体の値を使うときは && || ! で、捨てるときは and or not にする。 つまり、if や unless のように使うときは and or not を使い、 それ以外では && || ! ということだ。

と思ったのだが、よく考えるとなぜか条件式では and or not を使っているような気がする。なんでだろう。

then と do

then や do は最近は全て省略するようになった。 後置 if に変更しやすいのが理由である。

if cond
  ....
end

unless cond
  ....
end

while true
  ....
end

ちなみに loop より while true が好きだ。 なんでかって言うと、たぶんスコープが変わらないからだな。

その他 if にからむ問題

if と unless どっちを使うか。 unless が使えるときは積極的に使う。 else も入ってきたら重要なほうが前に来るように使いわける。

if/unless と修飾 if/unless、and/or の使い分けについて。 まず、メソッドの最初に置くガード文や、 異常系を捨てるときは修飾 if/unless を使う。 正常系の例外処理を捨てるときは and/or を使う。

def m(arg)
  # ガード文
  raise ArgumentError, "arg is nil" unless arg
  # m を得るほうが正常系なので前に出す
  m = arg.match(/some (re)gexp/) or
      raise ArgumentError, "arg not match"
  ....
end

普通の if 文はどんなに本文が短かくとも複数行に分ける。 if 文を一行にまとめて書くとなんかバランスが変。

if arg
  1
else
  2
end

if arg then 1 else 2 end   # 絶対やらない!

# ちなみにこの程度なら三項演算子を使う
(arg ? 1 : 2)

条件中の代入。当然使うでしょ。

while line = f.gets
  ...
end

とか、とっても便利。 最近は -w つけても警告が出なくなった。わーい。

三項演算子

三項演算子とは、C にもある cond ? t : f のこと。 これを嫌う人は (かつての筆者と同様) よくいるのだが、 使い方さえ間違えなければ便利なものだ。 例えばこんなふうに。

height = (h > 0) ? h : 0

Ruby では if 文も値を持つのでそれでもいいのだけど、 先に書いたように if を一行に書くのは嫌いなので、どうしてもこっちになる。 あと、最近は条件式に括弧を付けることが多くなった。

ちなみに意味も考えれば上記の例は次のようにできる。

height = [h, 0].max

実際に使うときはこっちだな。

return

return を書くか書かないか。 これは全部省略する。例えば次のように。

def expand(str)
  File.expand_path(str)
end

def ==(obj)
  obj.kind_of?(MyClass) and obj.name == @name
end

理由は、まず簡潔であること。 それと、Ruby ではすべての式が値を持つのだから、return という単語は不適切だ。 いつまでも C を引きずってちゃいかん。

それと return なしのほうがちょっとだけ効率もいい。 それはなぜかというと RubyHackingGuide でも見てくれればわかるのだが、 return は結局 longjmp だからだ。break や next も longjmp。

self

self を省略するかしないか。 self は基本的に全て省略する。

第一の理由は、その方がかっこいいからだ。 第二に、「デフォルトのターゲットは自分のいるところ」 という規定は非常に人間の生理にかなっているからだ。

例えば単に「郵便局に手紙だしてきて」と言ったとしよう。 この場合「郵便局」はたぶん最寄りの郵便局であろう。 「市役所はどこですか」と聞かれたらそれはその市の市役所であろう。 ニュースで「政府首脳が…」と言った場合、 普通は日本の政府首脳を指すであろう。 だからこのようなデフォルト設定は非常に「正しい」。 同時に、それを利用するのも「正しい」。

ちなみに、これは有名な話だけど、 セッターメソッド (foo= みたいなやつ) では self を省略できない。 たとえば次のような場合。

def initialize(arg)
  self.attribute = arg
end

こういうとこで self を省略するとローカル変数の代入とみなされてしまう。 筆者はハマらないが他の言語から入った人ははまりやすいらしいので注意だ。

セミコロン

重大なことを忘れていた。 文末の「;」(せみころん) について。 これは省略「しなければならない」。

空行

昔は空行が好きで入れまくっていたのだが、最近は入れない。 どうもメソッド括弧の空白と同じで、目が慣れると冗長に見えてくるようだ。

が、それでもやっぱり空行は重要だ。 メソッド間には当然空行を入れるべし。 クラス間はもっと当然である。

それから、alias と private のまわりの空行について書こう。 メソッド定義のあとに alias や private を書くときは、 alias では空行を入れ、private では入れない。

def foo
  expr
end
private :foo

def bar
  expr
end

alias any bar

なぜならば、alias は別のメソッド (インターフェイス) の定義だからだ。 private はメソッド定義に付帯する情報なのでくっつける。

alias

alias で思いだした。 最近 alias の引数にシンボルを渡しているコードを見かける。

alias :newname :oldname

attr 系からの連想でこうしてしまうようだが……こんなことをしてはいけない。 alias はメソッド呼び出しではなくて構文である。 だからこそ引数の間にカンマがないのだ。 そこはわかっていながらなぜエセメソッドのような使いかたをするのか。 こんなのは def のメソッド名部分で文字列を書くようなもんである。 絶対にやめよう。

# alias は必ずこの形式で!
alias newname origname

ちなみにどうしてもコロンが付けたければ alias_method を使えばいいんではないかと思う。

alias_method :newname, :origname

(ツッコミ) 『たのしいRuby』p.335、『Rubyレシピブック』p.31に 書かれてあるからじゃないでしょうか。by 安達

(返事) いや、これを書いたときはまだレシピブックは出てないので、 レシピブックはありえません。「たのしい」のほうもギリギリのはず。

for

for を使うか使わないか。結論は「使わない」。 イテレータはいっぱいあるのに each だけ特別扱いをすることに違和感を感じる。


system revision 1.162