2017-04-19 47 views
1

我的元編程讀來,穿過這個練習中傳來:紅寶石:從類檢索本地變量

http://ruby-metaprogramming.rubylearning.com/html/Exercise_1.html

的問題是:

鑑於這種類的定義:

class A 
    def initialize 
    @a = 11 
    @@a = 22 
    a = 33 
    end 
    @a = 1 
    @@a = 2 
    a = 3 
end 

從外部檢索所有變量,輸出如下:

1 
2 
3 
11 
22 
33 

現在,動態獲取實例和類變量,即使是構造函數中的局部變量(它是initialize方法的返回值)也非常簡單。但我難以理解如何獲得局部變量a=3

據我所知,這是不可能的,因爲局部變量在第一次讀取類定義後不再存在。

,我已經做這項工作的唯一的迂迴方法是將一個變量設置爲「回報」值(因爲缺乏一個更好的詞)的類被聲明的時候,像這樣:

val = class A 
    a = 3 
end 

puts val # => 3 

這是唯一的方法嗎?

+0

你可以嘗試在類級別定義'def a =(val)'。這將抓住它,但不知道你如何使它像一個正常的本地任務一樣工作。 – mahemoff

回答

0

一個可能會在類的所有局部變量的訪問通過返回它在類的結合(見Binding#local_variables瞭解詳細信息):

a = class A 
    v1 = 3.14 
    v2 = 42 
    binding 
end 

a.local_variable_get(:v1) 
#⇒ 3.14 
a.local_variable_get(:v2) 
#⇒ 42 

但主要的問題是,爲什麼你會想這樣做那? 本地變量旨在保持本地。這是它應該如何表現的。另外,無法修改原始binding中的局部變量(返回的是原始綁定的只讀副本。)

+0

我想知道爲什麼這個練習會說「你能從A課外獲得價值嗎?「和」編寫你的代碼,輸出是這樣的:「我想這意味着不要改變類定義,這使得任務不可能完成AFAIKT。 –

+1

@AndreyDeineko很好,可以在NIF中讀取堆棧:)這些練習已經過時(2009)並且通常看起來不是最好的練習來源 – mudasobwa

+0

@mudasobwa:什麼是NIF?沒有在Google上找到它,但是IMO要求可能存在的局部變量的值是沒有意義的當從外部看到這個類時,即沒有實際執行一個方法時,這些變量可能不會出現,這些變量本身並不存在,並且詢問它們的值是沒有意義的。 – user1934428

1

您的問題尚不清楚。在標題中,你寫了「局部變量」,但在這個例子中,你只提到了實例變量和類變量。

至於實例變量,您可以使用Object#instance_variables獲取已知實例變量的列表。但請注意,實例變量是動態創建的,而不是在類聲明時創建的。例如,如果給定類

class AA; 
    def initialize; @x=1; end; 
    def f; @y=1; end; 
end 

表達

AA.new.instance_variables 

將返回[:@x] - 的:@y丟失,因爲它還不存在。

您沒有辦法自動(即不修改類)檢索本地變量。由於mudasobwa在他的答案解釋,你將不得不明確傳回一個綁定。

+0

' BU在這個例子中,你只提及實例和類變量。「真的嗎? :)什麼是'a = 33'和'a = 3',那麼,如果不是局部變量呢? –

+0

你是對的,我錯過了他們! – user1934428

+2

即使你的答案可能對其他讀者有價值,但絕對不適用於OP,因爲對他來說,引用:*「動態獲取實例和類變量,即使是構造函數中的局部變量也非常簡單。 ]但是我很難理解如何獲得局部變量'a = 3'。「*。如此有效,這個答案與問題無關:) –

2

的問題似乎歸結爲這樣:給出如下:

class A 
    attr_reader :instance_var 
    def initialize 
    @instance_var = (@instance_var ||= 0) + 1 
    instance_local_var = 33 
    puts "instance_local_variables = #{ local_variables }" 
    instance_local_var = 33 
    end 
    class_local_var = 3 
    puts "class_local_variables = #{ local_variables }" 
    class_local_var = 3 
end 
    # class_local_variables = [:class_local_var] 
    #=> 3 

可以在一個確定的instance_local_varclass_local_var值是多少?

確定class_local_var

的價值這個問題的答案顯然是「不」,因爲class_local_var不再存在(已被標記爲垃圾收集)end執行後:

A.send(:local_variables) 
    #=> [] 

確定instance_local_var的值

a = A.new 
    # instance_local_variables = [:instance_local_var] 
    #=> #<A:0x007ff3ea8dbb80 @instance_var=1> 

請注意,@instance_var #=> 1

A.new不返回的instance_local_var值,但由於該變量在initialize最後一行分配值,可以通過再次執行initialize來獲得該值。

instance_local_var = a.send(:initialize) 
    #=> 33 

有個問題,但是:

a.instance_var 
    #=> 2 

執行initialize的第二時間已經引起不想要的副作用。我對initialize的定義是人爲的,但它強調了可以通過第二次執行initialize來發生許多不良副作用的事實。

現在讓我們來獲取一個新實例。

b = A.new 
    # instance_local_variables = [:instance_local_var] 
    #=> #<A:0x007fee0996e7c8 @instance_var=1> 

再次,@instance_var=1。對給定實例調用initialize兩次的副作用的一種可能的解決方法是對子類A進行子類化並使用super

class B < A 
    attr_reader :b 
    def initialize 
    @b = super 
    end 
end 

B.new.b 
    #=> 33 
a.instance_var 
    #=> 1 

沒有保證不良副作用可以用這種方法來避免(例如,initialize任何情況下可以執行應該只出現一次數據庫操作),但它似乎離開初始實例a不受影響。這當然都是假設的。

1. send因爲A.private_methods.include?(:local_variables) #=> true

A.new.send(:initialize) 2是必需的因爲iniitialize是私人必須使用。