2012-01-06 49 views
6

我的背景是PHP和C#,但我真的很想學習RoR。爲此,我已經開始閱讀官方文檔。我對一些代碼示例有一些疑問。一些簡單的Ruby問題 - 迭代器,塊和符號

首先是與迭代器:

class Array 
    def inject(n) 
    each { |value| n = yield(n, value) } 
    n 
    end 

    def sum 
    inject(0) { |n, value| n + value } 
    end 

    def product 
    inject(1) { |n, value| n * value } 
    end 
end 

我明白yield意思是「這裏執行相關的塊。」最引人注目的是each|value| n =部分。其他塊讓我更有意義,因爲他們似乎模仿C#風格lambda表達式:

public int sum(int n, int value) 
{ 
    return Inject((n, value) => n + value); 
} 

但第一個例子是混亂給我。

另一個是符號。我什麼時候想要使用它們?爲什麼我不能做這樣的事情:

class Example 
    attr_reader @member 

    # more code 
end 

回答

3

inject or reduce方法,n表示累計值;這意味着每次迭代的結果都在n變量中累積。在你的例子中,這可能是數組中元素的總和或乘積。

yield返回塊的結果,該結果存儲在n中,並用於下一次迭代中。這就是結果「累積」的原因。

a = [ 1, 2, 3 ] 
a.sum # inject(0) { |n, v| n + v } 
# n == 0; n = 0 + 1 
# n == 1; n = 1 + 2 
# n == 3; n = 3 + 3 
=> 6 

此外,要計算總和,您也可以寫入a.reduce :+。這適用於任何二進制操作。如果您的方法被命名爲symbol,則編寫a.reduce :symbol與編寫a.reduce { |n, v| n.symbol v }相同。

attr和公司實際上是方法。在引擎蓋下,他們爲您動態地定義方法。它使用您傳遞的符號來計算實例變量和方法的名稱。 :member導致@member實例變量和membermember =方法。

你無法寫入的原因attr_reader @member是因爲@member本身不是一個對象,也不能轉換爲符號;它實際上告訴ruby獲取self對象的實例變量@member的值,該對象在類範圍內是類本身。

舉例說明:

class Example 
    @member = :member 
    attr_accessor @member 
end 

e = Example.new 
e.member = :value 
e.member 
=> :value 

記住訪問未設置的實例變量產生nil,而且由於attr方法家庭只接受符號,您可以:TypeError: nil is not a symbol

關於Symbol的用法,你可以排序使用它們就像字符串。他們製作出優秀的散列鍵,因爲與字符串不同,相同的符號總是指向同一個對象。

:a.object_id == :a.object_id 
=> true 
'a'.object_id == 'a'.object_id 
=> false 

他們也常用來指代法名稱,can actually be converted to Procs,它可以傳遞給方法。這使我們能夠編寫諸如array.map &:to_s之類的東西。

查看this article瞭解符號的更多解釋。

1

對於inject的定義,你基本上建立鏈接的塊。具體而言,{|value| n = yield(n, value)}中的變量n本質上是傳遞給inject的塊的累加器。因此,例如,對於product,inject(1) {|value| n * value}的定義,我們假設您有一個數組my_array = [1, 2, 3, 4]。當您撥打my_array.product時,首先調用inject,其中n = 1。each產生到在inject中定義的塊,該塊繼而產生到傳遞給inject本身的塊,其中n(1)和數組中的第一個值在這種情況下)。該塊{|n, value| n * value}返回1 == 1 * 1,即將其設置爲inject的n變量。接下來,從每個產生2,並且在inject中定義的塊的產量爲yield(1, 2),其返回2並將其分配給n。接下來的3從each得出,該塊產生值(2,3)並返回6,其存儲在n中以用於下一個值,等等。從本質上講,追蹤特定例程(sumproduct)中正在執行的計算的整體值是不可知的。沒有這個,你必須聲明例如

def sum 
    n = 0 
    each {|val| n += val} 
end 

def product 
    n = 1 
    each {|val| n *= val} 
end 

這是煩人的重複。

對於第二個問題,attr_reader及其系列本身是在內部定義使用define_method的適當訪問器例程的方法,在稱爲元編程的過程中;他們不是語言聲明,而只是普通的舊方法。這些函數希望傳遞一個符號(或者,也許是一個字符串),它提供了您正在創建的訪問器的名稱。理論上,您可以在這裏使用實例變量,例如@member,雖然它將是中傳入和使用的點的值的@member。有關這些實現的示例,this page顯示了attr_ *方法的一些示例。

1
def inject(accumulator) 
    each { |value| accumulator = yield(accumulator, value) } 
    accumulator 
end 

這只是得到的accumulator的電流值和陣列項的注入塊,然後將結果存儲回累加器一次。

class Example 
    attr_reader @member 
end 

attr_reader只是它的參數是你要設置的訪問者的名稱的方法。所以,在一個人爲的方式,你可以做

class Example 
    @ivar_name = 'foo' 
    attr_reader @ivar_name 
end 

創建稱爲getter方法foo

1

您與第一個例子的混淆可能是由於您將|value| n作爲單個表達式讀取,但事實並非如此。

該重新格式化的版本可能更清楚你:

def inject(n) 
    each do |value| 
    n = yield(n, value) 
    end 
    return n 
end 

value是數組中的元素,它是產生與正到任何塊被傳遞給inject,其結果被設置爲n 。如果還不清楚,請閱讀each方法,該方法接收一個數據塊並將數組中的每個項目都輸出給它。那麼它應該更清楚地說明積累的作用。

attr_reader當你認爲它是一種生成存取方法的方法時,這種方法不太奇怪。它本身不是一個訪問器。它不需要處理@member變量的值,只是它的名字。 :member只是字符串'member'的實參版本,它是變量的名稱。

您可以將符號看作更輕量級的字符串,並且額外的獎勵是每個相同的標籤都是同一個對象 - :foo.object_id == :foo.object_id,而'foo'.object_id != 'foo'.object_id,因爲每個'foo'都是新對象。你可以在irb中嘗試一下。把它們看作標籤或原始字符串。它們非常有用,並且出現了很多,例如用於元編程或作爲哈希鍵。正如其他地方所指出的,調用object.send :foo是與調用object.foo

這可能是值得一讀從'pickaxe' book一些早期的章節多學習一些紅寶石,它會幫助你理解和欣賞多餘的東西軌補充道。

0

首先,您需要了解在哪裏使用符號以及哪裏沒有符號。 符號尤其用於表示某些內容。例如::姓名,年齡。這裏我們不打算用這個來執行任何操作。 字符串僅用於數據處理。例如:'a = name'。在這裏我將使用這個變量'a'進一步用於ruby中的其他字符串操作。 此外,符號比字符串更有效率,它是不可變的。這就是爲什麼Ruby開發者喜歡符號而不是字符串。

您甚至可以使用注入法計算總和爲(1..5).to_a.inject(:+)