雑草SEの備忘録

東大を卒業し、SEとして働くことになった。備忘録的に作業を綴る。

Rubyのインスタンス変数の直接参照について

会社で議論になり、未だに私の中で決着がついていない問題を取り上げてみます。

Rubyインスタンス変数を直接参照することについてです。

例えば、コンストラクタでインスタンス変数を設定します。

そして、インスタンスメソッドでそのインスタンス変数を直接参照して処理をして値を返却するということをします。

良い例ではないかもしれませんが、こんな感じのイメージです。

class PriceCalculation
  def initialize(price, tax)
    @price = price
    @tax = tax
  end

  def tax_included_price
    @price * (1 + @tax)
  end
end

前提として、@priceや@taxはこのクラス内のみで使用され、外部からは参照されないものとします。

これに対し、同僚のドイツ人がインスタンス変数を直接参照するのは良くないから、attr_readerを使えと指摘してきました。 彼が言うにはこんな感じです。

class PriceCalculation
  attr_reader :price, :tax

  def initialize(price, tax)
    @price = price
    @tax = tax
  end

  def tax_included_price
    price * (1 + tax)
  end
end

しかし、私は、attr_readerを使ってしまうと、PriceCalculationの外部からpriceやtaxが参照される必要が無いにもかかわらず参照できてしまい、そちらのほうがよろしく無いのでと思いました。

インスタンス変数をそのまま参照する方法で、attr_readerを使わない場合は参照することはできません。

(もちろんrubyなので参照できないわけではないですが)

例1: この方法だと呼び出せない

[1] pry(main)> class PriceCalculation
[1] pry(main)*   def initialize(price, tax)
[1] pry(main)*     @price = price
[1] pry(main)*     @tax = tax
[1] pry(main)*   end
[1] pry(main)*
[1] pry(main)*   def tax_included_price
[1] pry(main)*     @price * (1 + @tax)
[1] pry(main)*   end
[1] pry(main)* end
=> :tax_included_price
[2] pry(main)> pc = PriceCalculation.new(200, 0.08)
=> #<PriceCalculation:0x007f9b41ae0ae0 @price=200, @tax=0.08>
[3] pry(main)> pc.price
NoMethodError: undefined method `price' for #<PriceCalculation:0x007f9b41ae0ae0 @price=200, @tax=0.08>
from (pry):12:in `__pry__'
[4] pry(main)> pc.tax
NoMethodError: undefined method `tax' for #<PriceCalculation:0x007f9b41ae0ae0 @price=200, @tax=0.08>
from (pry):13:in `__pry__'
[5] pry(main)> # rubyなので読み出す方法はもちろんあるが・・・。
[6] pry(main)> pc.instance_eval("@price")
=> 200

例1: この方法だと呼び出せる

[1] pry(main)> class PriceCalculation
[1] pry(main)*   attr_reader :price, :tax
[1] pry(main)*
[1] pry(main)*   def initialize(price, tax)
[1] pry(main)*     @price = price
[1] pry(main)*     @tax = tax
[1] pry(main)*   end
[1] pry(main)*
[1] pry(main)*   def tax_included_price
[1] pry(main)*     price * (1 + tax)
[1] pry(main)*   end
[1] pry(main)* end
=> :tax_included_price
[2] pry(main)> pc = PriceCalculation.new(200, 0.08)
=> #<PriceCalculation:0x007fa20222e830 @price=200, @tax=0.08>
[3] pry(main)> pc.price
=> 200
[4] pry(main)> pc.tax
=> 0.08

彼の言い分としては、これはパターンなのだと。attr_readerを使ったほうが変化に耐えうるソースコードなのだと、そう言っていました。そして、Sandi Metzの本を参照してきました。 GitHubから引用させていただきます。 poodr/chapter_2.rb at master · skmetz/poodr · GitHub

############## Page 24 ##############
class Gear
  def initialize(chainring, cog)
    @chainring = chainring
    @cog       = cog
  end

  def ratio
    @chainring / @cog.to_f      # <-- road to ruin
  end
end

############## Page 25 ##############
class Gear
  attr_reader :chainring, :cog  # <-------
  def initialize(chainring, cog)
    @chainring = chainring
    @cog       = cog
  end

  def ratio
    chainring / cog.to_f        # <-------
  end
end

road to ruinとは一直線に破滅に向かうという意味です。

その理由としては、インスタンス変数を編集してクラスで共通に使いたくなるときにメソッドだけ修正すればいいから変化に耐えうるということが挙げられていました。

############## Page 25 ##############
  # a simple reimplementation of cog
  def cog
    @cog * unanticipated_adjustment_factor
  end

############## Page 25 ##############
  # a more complex one
  def cog
    @cog * (foo? ? bar_adjustment : baz_adjustment)
  end

こんな風に@cogをそのまま使うのではなく、編集して使いたい時に、attr_readerを使ったほうが良いということでした。

ただ、私個人の意見としては、attr_readerで外から見えるようにすると、どこで使われているかすぐにはわからなくなり、保守性が下がってしまうこと、また、この例で言う@cogをPage 25の例のように何か処理をして使いたくなる場合は、ほとんどないと思うこと、あったとしてもすべてのcogを編集したcogで使いたいと思うケースはないと思うので、この利点についてはまったく利点と感じませんでした。むしろ、なにか処理をしたい場合はその処理を意味する名前を冠したメソッド名をつけておかないと何を意味するのかが皆目分からなくなってしまいます。

  def some_decorated_cog
    @cog * (foo? ? bar_adjustment : baz_adjustment)
  end

のように処理の意味を付け加えておかないと可読性が落ちます。

※コンストラクタで設定するということもできますが、これは良いのでしょうか、悪いのでしょうか・・・。

  def initialize(chainring, cog)
    @chainring = chainring
    @cog = cog * (foo? ? bar_adjustment : baz_adjustment)
  end

このあたり、私はSandi Metzの主張するパターンには同意しかねます。Sandi Metzの本には、このパターンについて2つの問題点が挙げられていました。今手元に無いので詳細は覚えていないのですが、もしかしたら、そちらの問題点のほうが重要なのではないかとさえ思いました。

そこで、いくつか参考例を見てみようと思いDraperのコードをあたってみました。

draper/factory.rb at master · drapergem/draper · GitHub

すると、attr_readerをprivateにするというやり方をしていました。

確かにこれだと外部から参照されないことが約束されるので、保守性が高まります。(黒魔術が使われない前提。)

[1] pry(main)> class PriceCalculation
[1] pry(main)*   def initialize(price, tax)
[1] pry(main)*     @price = price
[1] pry(main)*     @tax = tax
[1] pry(main)*   end
[1] pry(main)*
[1] pry(main)*   def tax_included_price
[1] pry(main)*     price * (1 + tax)
[1] pry(main)*   end
[1] pry(main)*
[1] pry(main)*   private
[1] pry(main)*
[1] pry(main)*   attr_reader :price, :tax
[1] pry(main)* end
=> nil
[2] pry(main)> pc = PriceCalculation.new(200, 0.08)
=> #<PriceCalculation:0x007fb6739cc3c8 @price=200, @tax=0.08>
[3] pry(main)> pc.price
NoMethodError: private method `price' called for #<PriceCalculation:0x007fb6739cc3c8 @price=200, @tax=0.08>
from (pry):16:in `__pry__'
[4] pry(main)> pc.tax
NoMethodError: private method `tax' called for #<PriceCalculation:0x007fb6739cc3c8 @price=200, @tax=0.08>
from (pry):17:in `__pry__'
[5] pry(main)> pc.tax_included_price
=> 216.0

正直、私はインスタンス変数になにかの処理をして同名のゲッターメソッドでそれを呼び出すことに大きな抵抗を感じるので、インスタンス変数を直接参照することにくらべてどのくらいの利点があるのだろうと思いました。 ゲッターメソッド経由で参照されるので処理も少し遅くなります。

このあたりの議論をrubyではないですが、C++での議論がありました。コメントでの議論も含めて考えさせられましたが、私はC++の素養がゼロなのであまり理解できておりません・・・。

メンバ変数を隠蔽する理由 - Qiita

この辺りも参考になりそうです。 http://qiita.com/Yahagi_pg/items/1bf59fc75d7f17c3b731

正直rubyでどう書くのがベストなのか、結論づけられていません・・・。

なお、車内で日本人エンジニア4人ほどと議論したところ、皆インスタンス変数直接参照すれば良いという意見でした。

※念のため繰り返しますが、インスタンス変数が外部から参照される必要が無い場合です。外部から参照される場合は、素直にattr_readerを使えば良いです。

達人に学ぶDB設計

久しく更新が止まっていましたが、久しぶりに更新です。

今日はデータベース関連の書籍を読み、とてつもない良書だったので紹介です。

その本がこちら。

@t_wadaさんに勧められて土日で一気に読破しました。

サブタイトルに「初級者で終わりたくないあなたへ」とありますが、データベースを軽く知っている程度(基本情報技術者試験で勉強した程度)でも、十分についていける内容です。

データベースを日頃から生業としている人にとっては、前半の正規化の説明などは釈迦に説法かと思いますが、改めて読むと復習も兼ねて得られるものも多いかと思います。

この正規化の話も含め前半はデータベースの基本的な話で、後半からがこの本のクライマックスになります。

「指南書」と銘打っておきながら、いきなりバッドノウハウが紹介されます。さらに、バッドノウハウが終わってもまだグレーノウハウが紹介されます。それだけデータベース設計の中にはバッドノウハウやグレーノウハウが満載ということなのでしょう。

きちんとデータベースの正規化をして、バッドノウハウやグレーノウハウにもなっていなければ、それなりに良いデータベース設計ということになるのではないでしょうか。

 

最後の方には、リレーショナルデータベースが不得意とするツリー構造の実装方法についても載っています。入れ子区間モデルの話は恥ずかしながら初めて出会ったので、ツリー構造を実装しなければいけない時が来たら、採用を検討してみたいと思います。

 

なお、最後の参考文献案内で

Bill Karwin 『SQL Antipatterns』

が紹介されており、「惜しむらくは、この本も邦訳がありません。」とありますが、@t_wadaさんががっつり訳してくれています。

ちなみに、他に@t_wadaさんがオススメしていたのは、

でした。これらの本も読み終わり次第、紹介していきたいと思います。

ruby 1.9系のラムダ記法

今時rubyの1.9系を使っている人なんていないと思いますが、私は仕事の都合上、1.9系を使うときがあります。
しかも、同じリポジトリでサーバーによってrubyのバージョンが2.1系だったり、1.9系だったりするので厄介です。

今回、仕事でrubyのラムダ記法で2系ならOKなのに1.9系だったらだめだった書き方があったので、備忘録的に紹介します。

まずは、アロー関数ではなくlambdaと書いた場合は問題ありません。

$ ruby -v
ruby 1.9.3p551 (2014-11-13 revision 48407) [x86_64-darwin15.2.0]
$ irb
irb(main):001:0> function = lambda do |x|
irb(main):002:1* puts x * 2
irb(main):003:1> end
=> #<Proc:0x007fabc195d9d0@(irb):1 (lambda)>
irb(main):004:0> function.(3)
6
=> nil

そして、アロー関数での書き方。

irb(main):005:0>
irb(main):006:0* function = ->(x) {
irb(main):007:1* puts x * 2
irb(main):008:1> }
=> #<Proc:0x007fabc1963560@(irb):6 (lambda)>
irb(main):009:0> function.(3)
6
=> nil

このように書くとうまくいきます。
しかし、

irb(main):010:0> function = -> (x) {
irb(main):011:1* puts x * 2
irb(main):012:1> }
SyntaxError: (irb):10: syntax error, unexpected tLPAREN_ARG, expecting keyword_do_LAMBDA or tLAMBEG
function = -> (x) {
               ^
(irb):10: syntax error, unexpected tLAMBEG, expecting $end
	from /Users/username/.rbenv/versions/1.9.3-p551/bin/irb:12:in `<main>'

このように書くとSyntaxErrorになります。何が違うのかというと、ラムダの矢印のあとの引数のカッコが矢印にくっついているかいないか。1.9系だと、くっついていないとSyntaxErrorになります。

2系以上だと、離れていてもSyntaxErrorにはなりません。

$ ruby -v
ruby 2.0.0p648 (2015-12-16 revision 53162) [x86_64-darwin15.2.0]
$ irb
irb(main):001:0> function = -> (x) {
irb(main):002:1* puts x * 2
irb(main):003:1> }
=> #<Proc:0x007faf791cb288@(irb):1 (lambda)>
irb(main):005:0> function.(3)
6
=> nil

今は1.9系を使うことなんてないかもしれませんが、行けると思って知らないとはまるのでご紹介でした。

unless A && Bを止めたい

タイトルの通りである。unless A && Bはわかりにくい。

例えばRuby on Railsで開発している時。

array1 = ["text1", "text2"]

array2 = []

という二つの配列があったとする。

puts "TEST1" unless array1.present? && array2.present? #1

puts "TEST2" unless array1.present? && array2.blank? #2

puts "TEST3" unless array1.blank? && array2.present? #3

puts "TEST4" unless array1.blank? && array2.blank? #4

puts "TEST5" unless array1.present? || array2.present? #5

puts "TEST6" unless array1.present? || array2.blank? #6

puts "TEST7" unless array1.blank? || array2.present? #7

puts "TEST8" unless array1.blank? || array2.blank? #8

この8つのうちどれがputsで文字列が表示されるのか?頭の中だけで考えてみると私は混乱する。

例えば#1だと、array1は存在するからarray1.present?はtrue でarray2は存在しないからarray2.present?はfalse。true && falseなのでtrue。そのunlessなのでこれは表示されない??

いや違う。これは表示される。

pry(main)> puts "TEST1" unless array1.present? && array2.present?
TEST1

これはわかりにくい・・・。unless A && Bは非常にわかりにくいのだ!(私には)

そこでunlessをifで書き換える。

puts "TEST1" if array1.blank? || array2.blank? #1

puts "TEST2" if array1.blank? || array2.present? #2

puts "TEST3" if array1.present? || array2.blank? #3

puts "TEST4" if array1.present? || array2.present? #4

puts "TEST5" if array1.blank? && array2.blank? #5

puts "TEST6" if array1.blank? && array2.present? #6

puts "TEST7" if array1.present? && array2.blank? #7

puts "TEST8" if array1.present? && array2.present? #8

そうすると#1はarray1はblank?そうじゃないね。じゃあarray?はblank?そうだね。TEST1が表示されるね。

とわかる。個人的にはこちらの方がわかりやすい。

unless A && B

if !A || !B

にしたほうがわかりやすいと思う。
これは高校の数学Aで習うド・モルガンの法則というやつだ。

\overline{A \land B} = \overline{A} \lor \overline{B}
個人的には、unless A && Bとなるときはif !A || !Bとするようにルールを決めても良いくらいだと思っている。

Ruby on Rails初心者が一歩進むために~2~

タイトルの通り。Ruby on Railsを勉強していてこの使い方知ってるとだいぶ幅が広がるなというのがあったので、メモ。

今回は、reduceです。エイリアスでinjectというのもあって、両者は同じ挙動をします。

1.reduce, inject

http://ref.xaio.jp/ruby/classes/enumerable/inject

reduceやinjectの使い方ですが、上記サイトには、次のように書かれている。

injectメソッドは、ブロックを使って繰り返し計算を行うのに使います。ブロックに順に「要素1、要素2」、「ブロックの前回の戻り値、要素3」、「ブロックの前回の戻り値、要素4」、...を渡します。メソッドの戻り値はブロックが最後に返した値になります。

うん。。よくわからない。
それで、Exampleを見てみる。

numbers = [4, 3, 9, 8, 5, 6, 1, 7, 2]
puts numbers.inject {|sum, n| sum + n }
puts numbers.inject(100) {|diff, n| diff - n }

これですぐ理解できるという人はすごい。私は少し悩んだ。
numbersという配列があって、その要素を一つ一つをとりだすのが、

numbers.inject {|sum, n| sum + n }

のnの部分。それで、sumはどこからでてくるのかというと、injectの引数。一つ目の例は、引数が省略されているが、引数が無い時は、

numbers.inject(0) {|sum, n| sum + n }

と同じ。sumはこのinjectの引数(この場合は0)を受ける。injectはブロックをとるので、その中で処理を書く。今回の例の場合は、
0に4を足して4、
4に3を足して9、
・・・
43に2を足して45
という感じ。つまり、各要素に対して繰り返し同じ処理をしたいときに使える。
引数を二つとってこんな書き方もできる。

numbers.inject(0, :+)

一つ目の引数は初期値。二つ目の引数はメソッド名。今回の場合は足し算。
Rubyはメソッドをシンボルにして呼び出すことが良くある。public_sendなんかはその最たる例であろう。

2.each_with_object

http://ref.xaio.jp/ruby/classes/enumerable/each_with_object

tags = [
  {href: "http://example1.com", tag: "tag1", style: "class1"},
  {href: "http://example2.com", tag: nil, style: "class2"},
  {href: nil, tag: "tag3", style: "class3"},
  {href: "http://example4.com", tag: "tag4", style: "class4"}
]

このような配列があったとする。hrefやtagがnilのない配列を作りたいとする。
そういうときは、each_with_objectを使ってこのように書くことができる。

tags.each_with_object([]) do |tag, array|
  array << { href: tag[:href], tag: tag[:tag], style: tag[:style] } unless tag[:href].nil? || tag[:tag].nil?
end

結果はこちら。

=> [{:href=>"http://example1.com", :tag=>"tag1", :style=>"class1"},
 {:href=>"http://example4.com", :tag=>"tag4", :style=>"class4"}]

each_with_objectではブロックの第2引数の処理結果が返り値となる。
今回の場合だと、arrayという変数だ。初期値は[]で空の配列。
次のように書くのと同じ動きをする。

# 変数の初期化
array = []
tags.each do |tag|
  next if tag[:href].nil? || tag[:tag].nil?
  array << { href: tag[:href], tag: tag[:tag], style: tag[:style] }
end

変数arrayを見ると、

[25] pry(main)> array
=> [{:href=>"http://example1.com", :tag=>"tag1", :style=>"class1"},
 {:href=>"http://example4.com", :tag=>"tag4", :style=>"class4"}]

こんな感じだ。
このやり方だと変数を初期化してループでぐるぐるまわすというのがいかにもrubyっぽくない。そこでeach_with_objectの登場というわけだ。
reduceで書くこともできる。

tags.reduce([]) do |array, tag|
  array << tag unless tag[:href].nil? || tag[:tag].nil?
  array
end

ただし、reduceの場合、ブロックの最後にarrayを返さないといけない。またdoのあとの引数もeach_with_objectとはちょっと違うの注意が必要だ。

The Philosophy of Rubyを読んでみた。

Rubyを使い始めて4か月目に入りました。Rubyを勉強していくうちに2003年に行われたRubyの生みの親であるまつもとゆきひろ氏のインタビュー記事があり、日本語訳が見当たらなかったので、勉強がてら和訳してみました。

 

 以下、長いですが、引用です。

Rubyの哲学

www.artima.com

要約

まつもとゆきひろRubyの生みの親)はRubyの設計哲学についてBill Vennersと対談した。設計の不完全性、直行性の危険性、コンピューターの努力における人間の重要性について話した。

まつもとゆきひろ(ネット上ではMatzと呼ばれている)はRubyの生みの親だ。Rubyは、日々プログラミングをするのにもフルスケールのアプリケーションを作るのにも両方に適したオブジェクト指向の言語だ。MatzのRuby開発は1993年にまで遡る。書くのが楽しく、生産性を向上させる言語が欲しかったからだ。最初は日本で人気になったが、Rubyは世界中のプログラマーのハートをつかんだ。

2003年9月24日、Bill VennersはデンマークのAarhusで開かれたJAOO会議でMatzと対談した。この対談で、MatzはRubyの設計哲学、Rubyの特徴、より良いプログラマーになることについて論じた。この記事ではMatzは設計の不完全性、直行性の危険性、ある程度の道しるべを与えながらも自由を与えること、驚き最小限の原則、コンピューターの努力における人間の重要性について述べる。

 

完璧な言語なんてない

Bill: Dave Thomas(プログラミングRuby の共著者)は、あなたが言語の設計が完璧であるはずはないと思っていると言った。なぜだ?

Matz: 言語設計者は完璧な言語を設計したいと思う。言語設計者はできるなら、「私の言語は完璧だ。なんでもできる」と言いたい。だが、完璧な言語を設計するのはただただ不可能だ。なぜなら言語を見るのに二つの側面があるからだ。一つはその言語で何を成すことができるか。もう一つは、その言語を使ってどう感じるか、つまり、プログラミングの最中に人間がどう感じるかだ。

チューニング完全理論のために、チューニング完全言語でできることはすべて、理論上は他のチューニング完全言語でもできる。しかし、コストが異なる。アセンブリ言語でもすべてできる。だが、もはやだれもアセンブリ言語でプログラミングをしたいとは思わない。何が出来るかという観点で考えれば言語は異なる。ただ、違いは限られている。例えば、PythonRubyプログラマーにとってほとんど同じ力を提供してくれる。

私は「何」の側面ではなく、「どのように」の側面を強調したい。つまり、プログラミングするときにどう感じるかだ。それがRubyの他の言語との主な違いだ。私はフィーリング、特にRubyを使って私がどう感じるかを強調したい。私はRubyをすべての人にとって完璧にするために頑張りたくはない。私が感じることとあなたが感じることは違う。すべての人にとって完璧な言語なんてない。私はRubyを私にとって完璧にしたい。おそらくそれはあなたにとっては完璧ではないだろう。Guido van Rossumにとって完璧な言語はPythonであるように。

直行性VS調和性

Bill: Dave Thomasはもし私が直行性のある特徴を加えるようにあなたにお願いするなら、あなたはそれをしないだろうとも言った。あなたがしたいのは調和のとれたことである。調和とは何を意味するのか?

Matz: 私は、一貫性と直行性は設計の道具にすぎず、設計の最優先の目的ではないと思っている。

Bill: ではこの文脈で直行性とは何を意味する?

Matz: 直行性の一例はちょっとした特徴やシンタックスを結び付けれるようにすることだ。例えば、C++は関数に対し、デフォルトパラメータ値とパラメーターに基づいた関数名のオーバーロードすることの両方ができる。両方とも一つの言語の持つ良い特徴だが、それらは直行性があるので、同時に適用することはできない。コンパイラはそれらを同時に適用する術を知っている。もしそれが少し曖昧なら、コンパイラはエラーを起こすだろう。しかし、コードを見ると私は私の頭でそのルールを適用する必要がある。つまり、私はコンパイラがどう動作するかを推測する必要がある。もしその推測が正しいなら私は十分に賢い。そういうときは問題はない。しかし、私があまり賢くなければ、そして私は実際に賢くないが、それは混乱の元となる。その結果は普通の人間にとっては予想のしないことだ。これがどのように直行性が良くないかを示す一例だ。

Bill: つまり、直行性は書き手がそれらを理解して作用させたとき、うまく動作すると。けれど、プログラマーにとっては、それを見たときに理解するのは難しい。コンパイルされるから。そして、私はこれらの二つの事象がどう一緒に動くか判断しなければならないから。

Matz: 直行性という特徴は、結び付けられた時とても複雑になる。

Bill: では、代替のものは何か?さらなる調和か。

Matz: 言語に組み込むために、それら二つのうち一つをピックアップする。あなたは考えているすべてをする必要はない。それらのうち一つを選べば良い。両方とも良かったとしても。

自由と安らぎ

Bill: Pythonコミュニティの設計哲学の一つは、一つだけ提供すること。ものごとをするのに一つだけやり方を提供すること。もしあなたが、同じことをするのに50の異なる方法を提供したら、書き手には便利さを提供することになる。みんな好きな方法で書くことができる。トレードオフは読み手だ。例えば、私がある人のコードを読むとき、その人はある一つの方法で書く。次に別人のコードを読むとき、その人は別の方法で書く。だから読み手として、私はタスクを完了させるために結局すべての方法に精通する必要がある。私の好きな方法だけじゃなくて。それが設計のトレードオフだ。Pythonコミュニティは一つの方法を好むようだ。たった一つの。しかし、Rubyは同じことをするのに複数の方法を提供しているように見える。

Matz: RubyPerlの哲学を継承している。それは同じことをするのに複数個提供するということだ。わたしは、Larry Wallからその哲学を受け継いだ。Larry Wallは私のヒーローだ。私は、Rubyユーザーに自由になってほしい。私は彼らに選択の自由を与えたい。人間はみな違う。人間はみな様々な基準を持っている。しかし、多くの代替方法の中にはより良い方法がある。私は快適にすることによってその方法を奨励したい。だから、それが私が挑戦してきたことだ。おそらくPythonのコードはRubyより読みやすいだろう。みんな同じスタイルで書く。だからたぶん読みやすい。しかし、人と人との違いはとても大きい。たとえPythonを使っているとしても、一つのやり方だけを提供することはほとんど助けにはならない。私はそう思う。私は可能ならばたくさんの方法を提供したい。けれども、良い方法を推奨するユーザーや道しるべを提示してくれるユーザがより良い方法を選んでくれるだろう。

Rubyの楽しみ

Bill: あなたは、Ruby導入記事で、「私にとって、人生の目的の一部は喜びを持つことだ。プログラマーはプログラミングという創造に集中することができると、よく楽しみを感じる。だから、Rubyプログラマーを幸せにするために設計されている」と書いた。Rubyはどのようにプログラマを幸せにできますか?

Matz: あなたは人生を幸せにしたいよね。もしあなたが仕事を素早くできたら、あなたの仕事は楽しい。良いと思わないかい?それが人生の目的の一つだ。あなたの人生はより良くなる。

私はコンピュータを使うことで日々の生活で直面する問題を解決したい。だから私はプログラムを書く。Rubyを使うことで、その言語の摩訶不思議な規則ではなく、私がすることに集中したい。"print hello world"と言うためにpublic void なんちゃらという呪文から始めるのではなく、私は単に"print this!"と言いたい。私は周囲の摩訶不思議なキーワードはいらない。私はタスクに集中したいだけなのだ。それが基本的な考え方だ。だから私はRubyコードを簡潔にすることに挑戦してきた。

Bill: 簡潔なコードが書けることは、プログラマーを幸せにする方法の一つだ。

Matz: そうだ。結果、彼らは解決しなければならない課題そのものに集中できる。プログラマはときどき紙に疑似コードを書くことがある。もしその疑似コードが直接パソコンで動いたらすごいと思わないかい?Rubyはそうできるようになることを目指している。Pythonがそう言われるように。

Bill: そう、Pythonは実行可能な疑似コードだとみんな言っている。Rubyで他にプログラマを幸せにすることは何だい?

Matz: プログラマとしての日々の生活の中で、私たちはたくさんのテキスト文字列を処理する。だから私はテキスト処理に一生懸命取り組もうとしている。つまり、Stringクラスと正規表現だ。正規表現は言語にビルトインされていて、使用に適したチューニングが施されている。私たちは何度もOSを呼び寄せる必要もある。RubyUnix上のあらゆるシステムコールWindows APIのほとんどを呼ぶことができる。これで、OSの力や機能を言語環境の解釈に割ける。だからあなたは日々のシステム管理やテキスト処理プログラミングができる。それが少なくとも私の人生の中の主な領域だ。だから私はそれを良くするために一生懸命活動する。

Bill: だから基本的に、自分の仕事を早くそして楽しく終わらせるのに役立つことで、Rubyは私の生活を楽しませてくれると?

Matz: 私にとってはね。あなたにとってそうかどうかは分からない。そうであることを望むけど。

ヒューマンファクター

Bill: インタビューの中で、あなたは次のように述べた。「ヒューマンファクターを過小評価してはいけない。たとえパソコンの前にいたとしても、パソコンは機械だ。私たちは人間のために人間と一緒に働く。」これはどういうことを意味する?

Matz: メールを書いているときを想像してほしい。そのときあなたはパソコンの前にいる。パソコンを操作している。マウスをクリックしたり、キーボードを打ったりしてね。だけれども、メールのメッセージはインターネットの先の人間に届く。だからあなたはパソコンの前で働いていると言ってもそのパソコンの後ろには人がいる。私たちがしていることのほとんどは人間のためにやっていることだ。例えば税金計算によって政府は私の財布からお金を引き出す。政府は人間で構成される。

私たちの仕事のほとんどは結局人間に関係している。だからプログラミングによって、私たちは、パソコンに人間のために働くよう求めたり、コンピュータが実行できるレベルのとても明確な方法で私たちの考えを表現することができる。
一つ目のケースとしては、人間のために働くコンピュータを作ることだ。ターゲットはコンピュータの向こうの人間だ。もう一つのケースは、コンピュータによって理解され、実行される程度に考えを明確に表現することで、私たちは自分の脳から意図を表現し、結果的にコンピュータに実行される。両方のケースでの対象はやはり人間だ。

Bill: そのように考えるのは何が重要なのか。あなたはヒューマンファクターを過小評価するなと言った。なぜか。

Matz: コンピュータは、コンピュータとコミュニケーションをとろうと努力しなければいけないかどうかやコミュニケーションがとりやすいかどうかは気にしない。コンピュータは私がファイル内のバイト列の命令番号を入力し、実行させるためにそれを送信しなければいけないかなんて気にしない。あるいは、とても高級な言語が命令を生成したかなんて気にしない。コンピュータは気にしないのだ。私たち人間は私達がする努力を気にする。人々、特にコンピュータエンジニアは、機械に焦点を当てる。「これをすることで、そのマシンはもっと早くなる。これをすることでそのマシンはもっと効率よく動く。これをすることで、そのマシンは・・・」と考える。彼らは機械にフォーカスする。しかし実際には私たちは人間に、そして、人間がどのようにプログラミングをするかや、どのようにアプリケーションを操作するかに焦点を当てる必要がある。私たちが支配者で、機械が奴隷なのだ。

Bill: しばらくの間はね。

Matz: しばらくの間、そう、ターミネータ世代のうちは。

驚き最小の原則

Bill: インタビューの中で、あなたはこう述べた。「私は自分の驚きを最小限にするためにRubyを設計した。私は、世界中の人々がRubyが驚きを減らし、プログラミングの喜びを高めたと教えてくれた時とても驚いた。今や私はプログラマーの心は世界中で似ていると確信している。」

なぜ驚き最小の原則なのか。

Matz: 実際には、私はRubyが驚き最小の原則に従うと主張してはいなかった。何人かの人がRubyの設計がその哲学に従っていて、そう言いだした。実際は私はそれを持ち出してはいない。

私はプログラミング中の自分のフラストレーションを最小にしたかった。だから私はプログラミングにおける努力を最小にしたい。それがRubyを設計する上での私の主な目標だ。私はプログラミングでもっと楽しみたい。Rubyをリリースしてから、世界中のたくさんの人がRubyを知るようになって、私が感じるように彼らも感じると言った。彼らは驚き最小の原則というフレーズを思いついた。しかし実際にはしばしば誤解される。

Bill: どう誤解される?

Matz: 人々はみな個々人のバックグラウンドを持っている。ある人はPythonからきたかもしれないし、またほかのある人はPerlからきたかもしれない。そして言語のさまざまな側面に驚くだろう。そして私の所に来てこう言うのだ。「私はRubyのこの特徴に驚いた。だからRubyは驚き最小の原則に反する」と。ちょっと待ってほしい。驚き最小の原則はあなただけのものじゃない。驚き最小の原則は私の驚き最小の原則を意味する。そしてそれはあなたがRubyをしっかりと勉強してからわかる。例えば、私はRubyの設計を開始する前、C++プログラマだった。私は2~3年間、C++だけでプログラミングをしてきた。そしてC++のプログラミングを2年間やってもまだ私は驚いていた。

Ruby on Rails初心者が一歩進むために~1~

タイトルの通り。Ruby on Railsを勉強していてこの使い方知ってるとだいぶ幅が広がるなというのがあったので、メモ。

今回は、define_methodとdefine_singleton_methodです。キーワードはメソッドの動的生成。

1.define_method

define_method (Module) - Rubyリファレンス

メソッドを定義することができる。
同じ処理を何度もしたい!けどいちいち一つずつ定義するのは面倒だ!なんてときに使います。繰り返し似たようなメソッドを作るときには売ってつけです。
たとえば、次のようなCurrencyモデルがあったとします。

Currencyモデル

id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
code varchar(255)
appelation varchar(255)

currenciesテーブルのデータ

id code appellation
1 JPY
2 USD ドル
3 TWD 台湾ドル

そして、例えば、一つデータをとってくる。

[2] pry(main)> currency = Currency.first
  Currency Load (0.4ms)  SELECT  "currencies".* FROM "currencies"   ORDER BY "currencies"."id" ASC LIMIT 1
=> #<Currency id: 1, code: "JPY", appelation: "円">

ここで、とってきたデータがJPYかどうかを調べたいというとき。Currencyモデルにjpy?というメソッドを作る。

class Currency < ActiveRecord::Base
  def jpy?
    code == 'JPY'
  end
end

こんな感じでjpy?というメソッドを追加します。
さて、同様にusd?とかtwd?とかいうメソッドも追加したい。だけれども、今後通貨が増えてくるし、いちいちメソッドを定義するのは面倒だ!という時に、一気にメソッドを定義してくれるのがdefine_method。
例えば、CODESという通貨コードをもった配列を用意して、くるくるまわす。

class Currency < ActiveRecord::Base
  CODES = ['JPY', 'USD', 'TWD']

  CODES.each do |currency_code|
    define_method("#{currency_code.downcase}?".to_sym) { code == currency_code }
  end

  # 上のブロック内のdefine_methodによって以下が定義されたのと同じ。
  #def jpy?
  #  code == 'JPY'
  #end
  #def usd?
  #  code == 'USD'
  #end
  #def twd?
  #  code == 'TWD'
  #end
end

結果がこちら。きちんとメソッドが定義されていて、期待通りの動きになっていることが分かる。

[1] pry(main)> currency = Currency.find(2)
  Currency Load (0.8ms)  SELECT  "currencies".* FROM "currencies"  WHERE "currencies"."id" = ? LIMIT 1  [["id", 2]]
=> #<Currency id: 2, code: "USD", appelation: "ドル">
[2] pry(main)> currency.jpy?
=> false
[3] pry(main)> currency.usd?
=> true
[4] pry(main)> currency.twd?
=> false
2.define_singleton_method

define_singleton_method (Object) - Rubyリファレンス

こちらも基本的には、define_methodと同じです。ただし、特異メソッドを作ってくれます。Railsだと、例えば、モデルにクラスメソッドを動的に作成したいときに威力を発揮します。
今回の例ですと

Currency.twd

とやると、台湾ドルのデータをとってこれるようにしたいという場合を考えます。
単純にクラスメソッドを定義してやればいいです。

class Currency < ActiveRecord::Base
  def self.twd
    where(code: 'TWD').first
  end
end

ただ、これも同様に日本円やUSドルも同様に作りたいですよね。define_methodと同じようにすればいいです。define_singleton_methodをつかうと、クラスメソッドをはやすことができます。

class Currency < ActiveRecord::Base
  CODES = ['JPY', 'USD', 'TWD']

  CODES.each do |currency_code|
    define_singleton_method("#{currency_code.downcase}".to_sym) do
      where(code: currency_code).first
    end
  end

  # 上のブロック内のdefine_singleton_methodによって以下が定義されたのと同じ。
  #def self.jpy
  #  where(code: 'JPY').first
  #end
  #def self.usd
  #  where(code: 'USD').first
  #end
  #def self.twd
  #  where(code: 'TWD').first
  #end
end

rails consoleで動作を確認してみます。

[1] pry(main)> Currency.jpy
  Currency Load (0.2ms)  SELECT  "currencies".* FROM "currencies"  WHERE "currencies"."code" = 'JPY'  ORDER BY "currencies"."id" ASC LIMIT 1
=> #<Currency id: 1, code: "JPY", appelation: "円">
[2] pry(main)> Currency.usd
  Currency Load (0.5ms)  SELECT  "currencies".* FROM "currencies"  WHERE "currencies"."code" = 'USD'  ORDER BY "currencies"."id" ASC LIMIT 1
=> #<Currency id: 2, code: "USD", appelation: "ドル">
[3] pry(main)> Currency.twd
  Currency Load (0.4ms)  SELECT  "currencies".* FROM "currencies"  WHERE "currencies"."code" = 'TWD'  ORDER BY "currencies"."id" ASC LIMIT 1
=> #<Currency id: 3, code: "TWD", appelation: "台湾ドル">