2011年10月23日日曜日

Ruby ベストプラクティス2章

Rubyベストプラクティスの2章はAPIの作り方ということで、
  • Rubyの関数・メソッドの引数の特徴
  • コードブロックの効果的な使い方
などを示しながら、API(≒ メソッドシグネチャ?)を自然に分かりやすく構築するコツについて書いてありました。

2.2節 Rubyの引数

Ruby では引数のデフォルト値を指定できる「オプション引数」を宣言できたりします。
def foo(bar="defaultvalue")
   ...
end
こうやって宣言した関数 foo は、 foo "hoge" とも foo (= foo "defaultvalue" と同じ)とも関数呼び出しができます。
ただ、オプション引数は複数存在するとその順番によって、省略できなくなってしまいます。

擬似的なキーワード引数

オプション引数とは別の話として、ハッシュを使った擬似的なキーワード引数の話が載っています。
キーワード引数の良いところは、順番に依存しない関数・メソッド呼び出しが行えることです。
「累乗を計算する power 関数は、第一引数が底だっけ?指数の方だっけ?」と迷うことなく
power({ :base => 2, :exp => 10 }) 
と記述することができ、うっかり 10の2乗を計算することを回避できます。

合わせ技

ハッシュによるキーワード引数とオプション引数(の考え方)とを組み合わせると、任意の場所の引数についてデフォルト値を定められる(= 省略可能にできる)関数やメソッドを定義できる、というのが 2.2 節のまとめです。

2.1節 統一された呼び出しインターフェース(API)

順番が逆ですが、2.2節で出てきたような引数処理のテクニックを利用すると、関数・メソッドを同一の呼び出し方法で異なる使い方が可能になる、という例を Table() メソッドを例に示しています。
異なる使い方:
  • hash データを引数として受け取り、それを解釈して、Table を構築
  • csv ファイル名を引数として受け取り、それを読み込んで、Table を構築
  • csv フォーマットの string を引数として受け取り、それを解釈して、Table を構築

2.3節 コードブロック

話題は変わって。

本の中でも「Enumeratable を使ったことがあるなら、自然とコードブロックを使っているはずだろう」と触れられているように、普通の Ruby プログラムであればコードブロックはいたるところに出てきます。
僕の中で一番最初に思いつくのは Array#each でしょうか。

この本では、コードブロックを使ってリファクタリング(共通部分の抽出)を行う例が挙げてありました。
元 Java の人としては、exec(...) を宣言した ISomeMethod インターフェースを実装するクラスを作って、そのインスタンスを対象のメソッドの引数として渡し、そのメソッドの中で exec(...) を呼び出す形に対応付けて覚えることにしました。

2.4節 Rubyらしいコード

また、話題は変わって。

「Ruby らしいコード」になるためのテクニックをいくつか紹介しています。

attr_*

Ruby のクラスにクラス変数 ( @varname ) を定義するとき、たいていの Rubyist は(Javaプログラマと違って)
class Message
  attr_accessor :message

  def initialize(m)
    @message = m
  end
end
と書くそうです。
こう書けば、(本来カプセル化されてアクセスできないはずの)Message#message を参照・書き込みできます。
読み出し・書き込みのどちらか一方だけ自動生成したい場合は、attr_reader / attr_writer があります。

メソッドの後ろの "!" と "?"

Ruby の「暗黙のルール」として、
  • methodname? は bool 値を返す
  • methodname! は「注意するべき操作」を示している
    • 一般的には、破壊的操作(元のデータを書き換える操作)を示していることが多い
    • 逆に ! のついていない対応する methodname は「(元のデータそのものではなく)コピーされた値を処理する操作」
というのが慣習になっています。

演算子もメソッド

つまり、再定義によるカスタマイズが可能、ということです。 + や << などで表現できる「操作」があれば、それをカスタマイズされた演算子として定義するとコードがすっきりしそうです。

おまけ:inject

2章の途中で、(かなり嘘が入っていますが、ざっくり言うと)こんなサンプルプログラムが出てきます。
a = [ 1, 3, 4, 5, 7]
a.inject(0) { |sum, element| sum + element }
# => 20
あまり Ruby の経験のない自分ですが、このコードが異様なのは何となく分かります。「sum って一体どこから出てきたのだろう。どうやら要素の足し算をしているようだけど…」

inject について調べてみたら、関数型言語で言うところの fold 関数であるらしいことが分かりました。
fold 関数については関数型言語好きな後輩から「foldはすばらしいですよ」と解説を受けたことがあったので、…存在だけは知っています(ごめんなさい、自信を持って理解してるって言えません)。「畳み込み」っていう言葉もイメージだけはできている気分です。

僕の中では、(配列・リストの)要素に対して構文木を作るイメージなんですが、それで伝わる人ってどのくらいいるんでしょうか…。

0 件のコメント:

コメントを投稿