次のコードはRubyで何を意味しますか?
||=
構文に意味や理由はありますか?
この質問はRubyメーリングリストやRubyブログで頻繁に議論されているため、現在、Rubyメーリングリストにもスレッドがあります。この問題について議論するRubyメーリングリスト上の他のすべてのスレッドへのリンクを収集します。
以下にその1つを示します。 =(または等しい)スレッドとページの決定的なリスト
本当に何が起こっているのか知りたい場合は、 Ruby Language Draft Specification のセクション11.4.2.3「略語の割り当て」をご覧ください。
最初の近似として、
a ||= b
に等しい
a || a = b
およびnotと同等
a = a || b
ただし、特にa
が未定義の場合、これは最初の近似にすぎません。また、セマンティクスは、単純な変数の割り当て、メソッドの割り当て、またはインデックスの割り当てによって異なります。
a ||= b
a.c ||= b
a[c] ||= b
すべて異なる方法で処理されます。
a ||= b
は条件付き代入演算子です。 a
が未定義または falsey の場合、を意味し、b
を評価し、結果にa
を設定します。同様に、a
が定義され、真と評価される場合、b
は評価されず、割り当ては行われません。例えば:
a ||= nil # => nil
a ||= 0 # => 0
a ||= 2 # => 0
foo = false # => false
foo ||= true # => true
foo ||= false # => true
紛らわしいことに、他の割り当て演算子(+=
など)に似ていますが、動作が異なります。
a += b
はa = a + b
に変換されますa ||= b
は、おおよそa || a = b
に変換されますa || a = b
の略記法です。違いは、a
が未定義の場合、a || a = b
はNameError
を発生させますが、a ||= b
はa
をb
に設定します。 a
とb
が両方ともローカル変数である場合、この区別は重要ではありませんが、どちらかがクラスのゲッター/セッターメソッドである場合は重要です。
参考文献:
a ||= b
以下の行のeachと同じ方法で評価します
a || a = b
a ? a : a = b
if a then a else a = b end
-
一方、
a = a || b
以下の行のeachと同じ方法で評価します
a = a ? a : b
if a then a = a else a = b end
-
編集:AJedi32がコメントで指摘したように、これは以下の場合にのみ当てはまります。1. aが定義済み変数である。 2. 1回と2回評価しても、プログラムまたはシステムの状態に違いはありません。
つまり、a||=b
は次を意味します。a
がundefined, nil or false
である場合、b
をa
に割り当てます。それ以外の場合は、a
をそのまま保持します。
x ||= y
は
x
に値がある場合、そのままにして値を変更しないでください。そうでない場合は、x
をy
に設定します
それは次と等しいことを意味します。左側の値が定義されているかどうかを確認してから、それを使用します。そうでない場合は、右側の値を使用します。 Railsで使用して、モデルのインスタンス変数をキャッシュできます。
Railsベースの簡単な例。現在ログインしているユーザーを取得する関数を作成します。
class User > ActiveRecord::Base
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
end
@current_userインスタンス変数が設定されているかどうかを確認します。もしそうなら、それはそれを返し、それによってデータベース呼び出しを保存します。ただし、設定されていない場合は、呼び出しを行ってから@current_user変数を設定します。これは非常に単純なキャッシュ手法ですが、アプリケーション全体で同じインスタンス変数を複数回フェッチする場合に最適です。
x ||= y
は
x || x = y
「xがfalseまたは未定義の場合、xはyを指す」
a = 2
とb = 3
があるとします
その後、a ||= b
はa
の値、つまり2
になります。
がfalse
またはnil
にならない値に評価されるとき。それがll
がb
の値を評価しない理由です。
a = nil
およびb = 3
と仮定します。
a ||= b
は3
、つまりb
の値になります。
最初にnil
..になったaの値を評価しようとするため、b
の値を評価しました。
RORアプリで使用される最良の例は次のとおりです。
#To get currently logged in iser
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
# Make current_user available in templates as a helper
helper_method :current_user
ここで、User.find_by_id(session[:user_id])
は、@current_user
が以前に初期化されていない場合にのみ起動されます。
unless x x = y end
xに値(nilまたはfalseではない)がなければ、yに等しく設定します
に等しい
x ||= y
a || = b
「a」に値が存在し、その値を使用して値を変更したくない場合、「a」に値がない場合は「b」の値を使用します。
単純な単語は、左側がヌルでない場合は既存の値を指し、そうでない場合は右側の値を指します。
それは怠instantなインスタンス化のようなものです。変数が既に定義されている場合、値を再度作成する代わりに、その値が使用されます。
||=
はアトミック操作ではないため、スレッドセーフではないことも覚えておいてください。経験則として、クラスメソッドには使用しないでください。
これはデフォルトの割り当て表記です
例:x || = 1
これは、xがnilかどうかを確認します。 xが実際にnilの場合、新しい値(この例では1)を割り当てます。
より明示的に:
if x == nil
x = 1
終わり
=は条件付き代入演算子
x ||= y
に等しい
x = x || y
または代替
if defined?(x) and x
x = x
else
x = y
end
a ||= b
に等しい
a || a = b
ではなく
a = a || b
デフォルトでハッシュを定義する状況のため(ハッシュは未定義のキーに対してデフォルトを返します)
a = Hash.new(true) #Which is: {}
使用する場合:
a[10] ||= 10 #same as a[10] || a[10] = 10
aはまだです:
{}
しかし、次のように書くとき:
a[10] = a[10] || 10
になります:
{10 => true}
キー10
に自身の値を割り当てたため、デフォルトではtrueに設定されているため、最初に割り当てを実行するのではなく、キー10
に対してハッシュが定義されます。
よくある誤解として、a ||= b
はa = a || b
と同等ではありませんが、a || a = b
のように動作します。
しかし、ここでは扱いにくいケースがあります。 a
が定義されていない場合、a || a = 42
はNameError
を上げ、a ||= 42
は42
を返します。したがって、それらは同等の式ではないようです。
b = 5
a ||= b
これは次のように変換されます:
a = a || b
どっちが
a = nil || 5
だからついに
a = 5
もう一度これを呼び出すと:
a ||= b
a = a || b
a = 5 || 5
a = 5
b = 6
もう一度これを呼び出すと:
a ||= b
a = a || b
a = 5 || 6
a = 5
確認すると、b
値はa
に割り当てられません。 a
は5
のままです。
アクセサーを高速化するためにRubyで使用されているメモ型パターン。
def users
@users ||= User.all
end
これは基本的に次のように変換されます:
@users = @users || User.all
したがって、このメソッドを初めて呼び出すときは、データベースを呼び出します。
今後このメソッドを呼び出すと、@users
インスタンス変数の値が返されます。
||=
は、条件付き代入演算子と呼ばれます。
基本的には=
として機能しますが、変数既に割り当てられているが何もしない場合を除きます。
最初の例:
x ||= 10
2番目の例:
x = 20
x ||= 10
最初の例では、x
は10になりました。ただし、2番目の例では、x
はすでに20として定義されています。したがって、条件演算子は効果がありません。 x
は、x ||= 10
の実行後も20です。
irb(main):001:0> a = 1
=> 1
irb(main):002:0> a ||= 2
=> 1
a
は既に1
に設定されていたため
irb(main):003:0> a = nil
=> nil
irb(main):004:0> a ||= 2
=> 2
a
はnil
だったため
a ||= b
は、a = b if a.nil?
またはa = b unless a
と同じです。
しかし、3つのオプションはすべて同じパフォーマンスを示していますか? Ruby 2.5.1でこれ
1000000.times do
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
end
pCで0.099秒かかりますが、
1000000.times do
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
end
0.062秒かかります。それはほぼ40%高速です。
また、次のものもあります。
1000000.times do
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
end
0.166秒かかります。
これが一般にパフォーマンスに大きな影響を与えるというわけではありませんが、最後の最適化が必要な場合は、この結果を考慮してください。ちなみにa = 1 unless a
は初心者にとって読みやすく、一目瞭然です。
注1:割り当て行を複数回繰り返す理由は、測定された時間のループのオーバーヘッドを減らすためです。
注2:各割り当ての前にa=nil
nilを実行した場合の結果は似ています。
||=
は、left == nil(またはundefinedまたはfalse)の場合にのみ値を右に割り当てます。