[英]passing methods that accepts blocks
傳遞接受塊的方法的好方法是什么?
即將方法視為變量,因此可以以這種方式使用:
(@glob&.each || crawl_dir(@base_dir)) do |file|
puts "#{file}"
end
一個可以嘗試的簡單示例:
> require 'csv'
=> true
> CSV {|v|v}
=> <#CSV io_type:$stdout encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
> a=CSV
=> CSV
> a==CSV
=> true
> a {|v|v}
Traceback (most recent call last):
1: from (irb):14
NoMethodError (undefined method `a' for main:Object)
這可能嗎?
在您的示例中,您使用了不同的東西,這些東西都稱為CSV
。
CSV {|v|v}
在這里,您使用一個塊調用名為CSV
的方法。 你會得到那個方法的返回值。 諸如此類的方法通常用作轉換器。 例如,有一個Integer()
方法,它接受一個參數(例如一個字符串),該參數被轉換為一個Integer
對象。 通常,這些方法的調用方式與它們返回的對象類相同。
a=CSV
在這里,您將CSV
常量(即CSV
類)的值分配給a
變量。 在您的代碼中,您可以使用CSV
常量或a
變量來引用類對象。
在這些情況下,您有相同的名稱指代不同的事物(分別是一個類和一個方法),Ruby 可以根據您如何使用它來區分調用/返回哪個。 只有您明確且明確地調用“事物”(即通過傳遞塊或任何其他參數),Ruby 才會調用該方法。
在所有其他情況下,如果您引用名稱以大寫字母開頭的事物,Ruby 期望它是一個常量並返回其引用的對象(在這種情況下是CSV
類對象)。
a {|v|v}
在這里,您會收到錯誤消息,因為 Ruby 嘗試調用名為a
(不存在)的方法。 即使這可行, a
變量此時也是對CSV
類(不能直接調用)的引用。
(注意:要調用名稱存儲在變量中的方法,可以使用send
方法,例如my_receiver.send(a)
。)
現在,為了解決您最初的問題,您可以創建一個方法對象並使用它來調用所需的方法,例如
method_proc = @glob&.method(:each) || method(:crawl_dir).curry(@base_dir)
method_proc.call do |file|
puts file
end
然而,這並不是非常慣用的 Ruby。 在這里,方法引用很少被攜帶(而不是在 Javascript 或 Python 中,您經常使用傳遞的函數對象執行此操作)。 Ruby 中更慣用的實現可能是:
def handle_file(file)
puts file
end
if @glob.respond_to(:each)
@glob.each { |file| handle_file(file) }
else
crawl_dir(@base_dir) { |file| handle_file(file) }
end
如果您的crawl_dir
方法(可選)返回一個Enumerator
對象,那么更慣用的是,在這種情況下,您可以簡化調用代碼。
在這里,我假設@glob
要么是nil
要么是一個Enumerable
對象(例如一個Array
或一個Enumerator
因此直接響應each
)。 這使我們能夠進一步簡化代碼。
def crawl_dir(directory)
# Return an Enumerator object for the current method invocation if no
# block was passed, similar to how the standard `Enumerable#each` method
# works.
return enum_for(__method__) unless block_given?
# the rest of the method here...
end
enumerable = @glob || crawl_dir(@base_dir)
enumerable.each do |file|
puts file
end
這里發生的情況是,您要么采用@glob
(我們假設它是一個Enumerable
對象,如果它存在,則響應執行each
)或crawl_dir(@base_dir)
沒有塊。 從您的crawl_dir
方法中,您將獲得一個Enumerator
對象, crawl_dir
是一個Enumerable
。 然后,您可以使用each
遍歷此對象。 因此,您的兩個對象都具有相同的返回類型,因此可以類似地使用。
如果要獲取對方法的引用,可以使用Object#method
方法。 然后,您可以使用Method#call
方法或.()
語法糖調用此方法:
require 'csv'
a = method(:CSV)
a.() {|v| v }
#=> #<CSV io_type:$stdout encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
您始終可以在任何對象上使用tap
:
a.tap do |v|
p v
end
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.