簡體   English   中英

Ruby方法可以接受塊或參數嗎?

[英]Can a Ruby method accept either a block OR an argument?

我正在Odin項目中上課,現在我必須自己編寫一個新的#count方法(帶有另一個名字),其行為類似於Enumerable模塊中的正常方法。

有關計數的文檔說明如下( http://ruby-doc.org/core-2.4.0/Enumerable.html#method-i-count ):

count→int
count(item)→int
count {| obj | block}→int

通過枚舉返回enum的項目數。 如果給出了參數,則enum中與item相等的item數。 如果給出了一個塊,它會計算產生真值的元素數量。

我想我可以將所有這些作為單獨的方法編寫,但我主要想知道一個方法定義是否可以結合count的最后兩次使用 - 使用item和block。 當然,我想知道這三個是否可以合並在一個定義中,但我最感興趣的是最后兩個。 到目前為止,我似乎找不到可能的答案。

文檔頁面包含以下示例:

ary = [1, 2, 4, 2]
ary.count               #=> 4
ary.count(2)            #=> 2
ary.count{ |x| x%2==0 } #=> 3

當然有可能。 您所要做的就是檢查是否給出了參數,並檢查是否給出了一個塊。

def call_me(arg=nil)
  puts "arg given" unless arg.nil?
  puts "block given" if block_given?
end

call_me(1)
# => arg given
call_me { "foo" }
# => block given
call_me(1) { "foo" }
# => arg given
#    block given

要么:

def call_me(arg=nil, &block)
  puts "arg given" unless arg.nil?
  puts "block given" unless block.nil?
end

后者很有用,因為它將塊轉換為可以重復使用的Proc(命名block ),如下所示。

您可以像這樣實現自己的count方法:

module Enumerable
  def my_count(*args, &block)
    return size if args.empty? && block.nil?
    raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 1)" if args.size > 1

    counter = block.nil? ? ->(i) { i == args[0] } : block
    sum {|i| counter.call(i) ? 1 : 0 }
  end
end

arr = [1,2,3,4,5]
p arr.my_count # => 5
p arr.my_count(2) # => 1
p arr.my_count(&:even?) # => 2
p arr.my_count(2, 3) # => ArgumentError: wrong number of arguments (given 2, expected 1)

在repl.it上看到它: https ://repl.it/@jrunning/YellowishPricklyPenguin-1

是的,可以通過使參數可選(塊總是可選的)並檢查是否傳遞位置參數或塊參數來實現。

不過,這有點亂。 大多數Ruby實現通過實現具有對實現的私有內部的特權訪問的相關方法來解決這個問題,這使得檢查參數是否被傳遞變得更加容易。 例如,JRuby和IronRuby都有辦法根據參數的數量和類型將多個重載的 Java / CLI方法綁定到單個 Ruby方法,從而可以將這三個“ count模式”實現為三個簡單的重載方法。 這里有一個例子count從IronRuby的 ,這是count從JRuby的

但是,Ruby不支持重載,所以你必須手動實現它,這可能有點尷尬。 像這樣的東西:

module Enumerable
  def count(item = (item_not_given = true; nil))
    item_given = !item_not_given
    warn 'given block not used' if block_given? && item_given

    return count(&item.method(:==)) if item_given
    return inject(0) {|acc, el| if yield el then acc + 1 else acc end } if block_given?
    count(&:itself)
  end
end

如你所見,它有點尷尬。 我為什么不干脆用nil作為可選默認參數item參數? 好吧,因為nil是一個有效的參數,我無法區分沒有參數的人和傳遞nil作為參數的人。

為了比較,這里是如何在Rubinius中實現count

 def count(item = undefined) seq = 0 if !undefined.equal?(item) each do element = Rubinius.single_block_arg seq += 1 if item == element end elsif block_given? each { |element| seq += 1 if yield(element) } else each { seq += 1 } end seq end 

在我(ab)使用可選參數的默認參數是具有副作用(如設置變量)的任意Ruby表達式的事實中,Rubinius使用由Rubinius運行時提供的特殊undefined對象並且是equal? 只對自己。

謝謝您的幫助! 就在我來檢查是否有任何答案之前,我提出了以下解決方案。 它可以肯定地改進,我會嘗試縮短它,但我更喜歡先在這里發布它,因為我提出它,它可能對像我這樣的其他新手有幫助。 在下面的代碼中,我使用的#my_each方法與普通的#each相同。

def my_count(arg=nil)
    sum = 0
    if block_given? && arg == nil
        self.my_each do |elem|
            if yield(elem)
                sum += 1
            end
        end
    elsif !block_given? && arg != nil
        self.my_each do |elem|
            if arg == elem
                sum += 1
            end
        end
    else
        self.my_each do |elem|
            sum += 1
        end
    end
    sum
end

我還發現這兩個鏈接很有用: 帶有可選參數的方法http://augustl.com/blog/2008/procs_blocks_and_anonymous_functions/ (這提醒我一個方法可以產生一個塊,即使它沒有被定義為參數,例如&塊)。 我看到Jorg也在第一個鏈接的討論中發表了評論。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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