簡體   English   中英

傳遞接受塊的方法

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM