21

test.rb という名前の Ruby ファイルがあります。

ff="ff"
def test
  puts ff
end

私はそれを実行し、エラーが発生しました:

test.rb:3:in `test': undefined local variable or method `ff' for main:Object (NameError)

これの理由は何ですか?それを説明するドキュメントはありますか?

4

4 に答える 4

22

メソッド定義ff内でアクセスできない理由は、(キーワードで作成された) メソッドが新しいスコープを作成するためです。それぞれキーワードとキーワードを使用してクラスとモジュールを定義する場合と同じです。testdefclassmodule

(トップレベルのオブジェクト)の役割はmain、この状況ではスコープの問題とはほとんど無関係です。

testメソッドが定義コンテキストで定義されたローカルにアクセスできるようにしたい場合は、 define_method(またはあなたの場合はdefine_singleton_methodメソッド)を使用してください。ここを参照してください。

ff = "hi"
define_singleton_method("test") { ff }
test #=> "hi"

defキーワードとは異なり、define_methodメソッドのファミリは新しいスコープを作成しませんが、代わりに現在のスコープを閉じて、ローカル変数をキャプチャします。

@ff@soup によって与えられた次の例でusing が機能した理由は、それmainがどういうわけか「特別なケース」ではなく、トップレベルで定義された ivar が の ivar でありmain、呼び出されたメソッドにアクセスできるためですmain

しかし、testメソッドととの関係は何mainですか? それ自体のメソッドmainはありません。実際には、 Objectクラスで定義されたプライベート インスタンス メソッドです。これは、そのtestメソッドが (プライベート メソッドとして) Ruby プログラムのほぼすべてのオブジェクトで利用できることを意味します。最上位 ( main) で定義されたすべてのメソッドは、実際にはObjectクラスのプライベート インスタンス メソッドとして定義されます。

Ruby トップレベルの詳細については、次の記事を参照してください: http://banisterfiend.wordpress.com/2010/11/23/what-is-the-ruby-top-level/

于 2012-05-28T10:11:58.447 に答える
14

Ruby のスコープは単純でもあり複雑でもあります。

まず、すべてがオブジェクトであり、すべてにスコープがあることを覚えておく必要があります。

あなたの質問に直接答えるにmainは、オブジェクトなどdef x...です。オブジェクトのメソッドを定義しているときmain

pyr/irb で観察:

# note the error describes 
[1] pry(main)> main 'main:Object'
NameError: undefined local variable or method 'main' for main:Object
from (pry):1:in '<main>'

# self is the current object, which is main
[2] pry(main)> self
=> main 

# note main is an object of type Object
[3] pry(main)> self.class
=> Object 

# main has methods
[4] pry(main)> self.methods
=> [:to_s, :public, etc etc]

だからあなたが書くとき

ff = "ff"
def test
    puts ff
end

あなたが本当にしていることは

class main
    ff = "ff"
    def test
        puts ff
    end
end

ff範囲外であるため、これは機能しません。これを修正するffには、インスタンス変数に変換する必要があるため、その名前の前に@. 以下が機能します。

@ff = "ff"
def test
    puts @ff
end

test

これは、通常のクラスよりも特殊なケースのように思われることに注意してmainください。以下を参照してください。

独自のテストクラスがある場合:

class Test1
    ff = "ff"
    def test
        puts ff
    end
end

Test1.new.test # undefined variable/method 'ff' error

ff期待どおりに正しいスコープで定義されていないため、これは失敗します。代わりに、パーサーがクラス宣言を実行しているときにスコープが設定されます。

それでは、上記の修正を試してみましょう。

class Test1Fixed
    @ff = "ff"
    def test
        puts @ff
    end
end

Test1Fixed.new.test # results in blank line

それは変だ。

このメソッドを追加しましょう:

def ff?
    puts "ff is: #{@ff.nil? ? "nil" : "not nill"}"
end

Test1Fixed.new.ff? # => ff is: nil

なぜ @ff nil なのですか?

以下はこれをより明確にするかもしれません:

class Test2
    puts "Where am i?"
    @instance = "instance"

    def initialize
        puts "Initalize"
    end

    puts "pants on"

    def test
        puts "This will NOT output 'instance': #{@instance}"
    end

    def self.class_test
        puts "This WILL output 'instance': #{@instance}"
    end
end

puts "Creating new Test2 Object"
t = Test2.new
puts "Calling test on Test2 Object"
t.test

puts "Calling class_test on Test2 Class object" 
Test2.class_test

これを実行すると、次の出力が得られます。

$ ruby scope.rb 
Where am i?
pants on
Creating new Test2 Object
Initalize
Calling test on Test2 Object
This will NOT output 'instance': 
Calling class_test on Test2 Class object
This WILL output 'instance': instance

ご覧のとおり、インターパーターは、他の場所と同様に、クラス宣言を順番に実行し、putsステートメントを出力します。メソッドの呼び出しを開始すると、これはさらに興味深いものになります。書く@instance = ...ことは を書くことと同じなself.instance = ...ので、インスタンスを定義した場所を推測できますか? はい、Test2 クラス オブジェクト (Test2 オブジェクトではありません) についてです。

これが、 を呼び出したときにtest何も出力されない理由です。これは、内部の がインスタンス化されたオブジェクトtestself参照しているためです (@instance を何かに設定した場所です!)。これが、実際のオブジェクトを指す内にインスタンス変数を設定する理由です。Test2Test2 class objectinitializeself

を介してクラス メソッドを定義self.class_testし、Test2 クラス オブジェクト ( Test2.class_test) でそのメソッドを呼び出すと、予想される出力が得られることがわかります。これは、そのスコープで@instanceが定義されているためです。

これが何らかの意味を持っていることを願っています。

http://rubykoans.com/スコープ セクションは、この知識の一部を固めるのに役立つ場合があります。

于 2012-05-28T04:45:24.820 に答える
2

Ruby では、シジルのない名前には常に「ローカル」スコープがあります。Ruby は字句スコープを使用するため、メソッド内では、名前は常にそのメソッドのみを参照します。

他のスコープは、グローバル、インスタンス、およびクラスです。それらについての良いドキュメントがあります: http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Operators#Scope

問題を解決するにはいくつかの方法があります。私のお気に入りは、印刷されているものを引数として渡すことです。

ff = "ff"
def test(ff)
  puts ff
end

おそらく、クラスで行っていることは何でもカプセル化し、インスタンス変数を使用するでしょう。

グローバル変数 ($ff) を使用することもできますが、その方法は狂気です。

于 2012-05-28T04:49:39.960 に答える
0

Ruby でメソッド内のローカル変数にアクセスできないのはなぜですか?

ローカル変数だからです。ローカル変数は、それらが定義されているスコープに対してローカルです。それが、結局、「ローカル変数」と呼ばれる理由です。

この場合、スクリプト スコープでローカル変数を定義したため、それはスクリプト スコープで表示され、それ以外の場所では表示されません

于 2012-05-28T12:04:00.103 に答える