わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

Re: nilガード

「a ||= 5」は「a = a || 5」ではなく「a || (a = 5)」*1であることの根拠,あるいはそういう取り決めがいつなされたのかについて,Rubyのソースや英語の文法書(Web上の情報を含む)で探したものの,見つかりませんでした.
手元の本を何冊か,読み直してみて,直接的な答えはやっぱりなかったのですが,OR演算子の変遷をほんの少し,知ることができました.

  • &&-||演算子とand-or演算子には,興味深い違いがあります.前者のほうが汎用性が高く,tureまたはfalse以外の結果を返すことがあります.後者は常にtrueまたはfalseの結果を返します.後者は,条件での論理式の結合専用です(そのため,オペランドの評価がtrueまたはfalseにならない場合は構文エラーになるのでミスを発見しやすい).次のコードを見てみましょう.
print (false || "string1\n")     # string1が出力される
# print (false or "string2\n")   #   構文エラー
print (true && "string3\n")      # string3が出力される
# print (true and "string4\n")   #   構文エラー
print (true || "string5\n")      # trueが出力される
# print (true or "string6\n")    #   構文エラー
print (false && "string5\n")     # falseが出力される
# print (false and "string6\n")  #   構文エラー
  • and-or演算子は,&&-||演算子よりも優先順位が低くなっています.次のコードを見てみましょう.
a = true
b = false
c = true
d = true
a1 = a && b or c && d    # &&が先に評価される
a2 = a && (b or c) && d  # orが先に評価される
p a1                     # falseが出力される
p a2                     # trueが出力される

(The Ruby Way―Ruby道への招待, p.45)

上記の本は,2002年11月28日 初版第1刷発行です.Appendix Eが「Ruby 1.8の変更点」となっているので,Ruby 1.6で解説していると思われます.
当時は,and,or,notは純粋に論理演算子であったのですね.ちなみに手元のirbで,「print (false || "string1\n")」から始まる8つの文を(先頭が#のものはそれを取り除いて)順に試すと,エラーにはならず,右オペランドの文字列か,"true"か"false"が出力されました.
現在は,&&とand,||とorの違いは,優先順位だけです.リファレンスマニュアルにも書かれていますし,本で確認するなら,2009年6月発行のものでどうでしょうか.

「and」「or」は優先順位が演算子の中で最低です.「not」は「and」「or」より優先順位が1つ上です.代入よりも低いことに注意してください.優先順位が最低なので,式の区切りとして認識するとよいでしょう.次の例で,上の式は下の式のように解釈されます.

1 and 2 or 3 and 4            # => 4
((1 and 2) or 3) and 4        # => 4

(Ruby逆引きハンドブック, pp.81-82)

オペランドがtrue/falseではなく整数値で,いずれも真か偽かというと真になる値です.ついでに,「1 && 2 || 3 && 4」と「(1 and 2) or (3 and 4)」はともに2となります.
さて||=に戻って…国際的に,nilガードが使われ始め,なぜそれがうまくいくのかが周知されたのは,いつなのでしょうか.
Rubyクックブック ―エキスパートのための応用レシピ集』(2007年4月)でいくつか見かけました.

# p.65
  doctype ||= %(<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd")
# p.67
  (@event_dispatcher_listners[event] ||= []) << callback
# p.128
  @user_defined ||= {}
# ...
  @contracts ||= {}

その用法について,前後に解説はありません.なのでこのころには確立していたわけですね.

*1:「5」に意味はありません.空配列を地の文で書くのを避けるため,適当に置き換えただけです.