3

ifブロック内で代入演算をいじっていたところ、以下の結果を発見し、驚きました:

C:\>irb --simple-prompt
if false
x = 10
end
#=> nil
p x
nil
x.object_id
#=> 4
#=> nil
p y
NameError: undefined local variable or method `y' for main:Object
        from (irb):5
        from C:/Ruby193/bin/irb:12:in `<main>'

上記のコードでは、falsyブロックxでのみ割り当てられているにもかかわらず、ローカル変数が作成されていることがわかります。代入が行われていないと信じ込ませたものの内容を見ようとしましたが、変数ifは存在します。であることも証明しました。xp xxx.object_id

ここで私の質問は、ブロック エントリ ポイントが意図的に永久に閉じられxているにもかかわらず、そのローカル変数がどのように作成されたかということです。if

p xの出力は の出力に似ていると予想しましたp y。しかし、その代わりに から驚くべき答えが返ってきp xました。

この概念がどのように機能するかを誰かに説明してもらえますか?

編集

いいえ、ここに別のテストがあります。これは、local変数のみの場合には当てはまりません。instanceおよびclass変数でも同じことが起こりました。以下を参照してください。

class Foo
  def show
    @X = 10 if false
    p @X,"hi",@X.object_id
  end
end
#=> nil
Foo.new.show
nil
"hi"
4
#=> [nil, "hi", 4]

class Foo
  def self.show
    @@X = 10 if false
    p @@X,"hi",@@X.object_id
  end
end
#=> nil
Foo.show
nil
"hi"
4
#=> [nil, "hi", 4]

成功例:

class Foo
  def self.show
    @@X = 10 if true
    p @@X,"hi",@@X.object_id
  end
end
#=> nil
Foo.show
10
"hi"
4
#=> [10, "hi", 4]
4

3 に答える 3

8

Ruby では、ローカル変数はパーサーが最初に代入に遭遇したときに定義され、その時点からスコープ内にあります。

ここに少しデモンストレーションがあります:

foo # NameError: undefined local variable or method `foo' for main:Object

if false
  foo = 42
end

foo # => nil

ご覧のとおり、 4 行目の代入は実行されていませんが、ローカル変数は7 行目に存在します。ただし、解析されたため、ローカル変数fooが存在します。しかし、代入は実行されなかったため、変数は初期化されておらず、評価結果はniland notになり42ます。

Ruby では、初期化されていない、または存在しない変数のほとんどが に評価されnilます。これは、ローカル変数、インスタンス変数、およびグローバル変数に当てはまります。

defined? foo       #=> nil
local_variables    #=> []
if false
  foo = 42
end
defined? foo       #=> 'local-variable'
local_variables    #=> [:foo]
foo                #=> nil
foo.nil?           #=> true

defined? @bar      #=> nil
instance_variables #=> []
@bar               #=> nil
@bar.nil?          #=> true
# warning: instance variable @bar not initialized

defined? $baz      #=> nil
$baz               #=> nil
# warning: global variable `$baz' not initialized
$baz.nil?          #=> true
# warning: global variable `$baz' not initialized

ただし、クラス階層の変数と定数には当てはまりません。

defined? @@wah     #=> nil
@@wah
# NameError: uninitialized class variable @@wah in Object

defined? QUUX      #=> nil
QUUX
# NameError: uninitialized constant Object::QUUX

これは赤いニシンです:

defined? fnord     #=> nil
local_variables    #=> []
fnord
# NameError: undefined local variable or method `fnord' for main:Object

ここでエラーが発生する理由は、初期化されたローカル変数が に評価されないからではなく、あいまいなためです:デフォルトの受信者 (つまり と同等)に送信される引数なしのメッセージか、またはへのアクセスのいずれかです。ローカル変数。nilfnordself.fnord()fnord

これを明確にするために、受信者または引数リスト (空の場合でも) を追加して、それがメッセージ送信であることを Ruby に伝える必要があります。

self.fnord
# NoMethodError: undefined method `fnord' for main:Object
fnord()
# NoMethodError: undefined method `fnord' for main:Object

または、使用前にパーサー(エバリュエーターではなく) が割り当てを解析する (実行しない) ことを確認して、 Ruby にそれがローカル変数であることを伝えます。

if false
  fnord = 42
end
fnord              #=> nil

もちろん、nilはオブジェクト ( class の唯一のインスタンスNilClass) であるため、object_idメソッドがあります。

于 2013-03-03T13:37:51.053 に答える
3

Ruby は常にすべてのコードを解析します。内部の内容を解析しないというサインとして false を見ていません。それを評価し、内部のコードが実行されるべきではないことを確認します。

于 2013-03-03T08:31:07.560 に答える
1

Rubyにはローカル変数「巻き上げ」があります。メソッド内の任意の場所にローカル変数への割り当てがある場合、その変数は、割り当ての前であっても、実際に割り当てが実行されなくても、メソッド内のどこにでも存在します。変数が割り当てられる前の値はnilです。

編集:

上記は完全に正しくありません。Ruby には、ローカル変数の割り当てが存在するが実行されない場合にローカル変数を定義するという点で、変数ホイストの形式があります。ただし、変数は、割り当てが発生する上記のメソッドのポイントで定義されていることがわかりません。

于 2013-03-03T07:55:22.963 に答える