【问题标题】:Yield within Set to eliminate in an Array集合内的屈服以消除数组
【发布时间】:2014-03-19 11:34:52
【问题描述】:

我找到了以下代码here 用于消除数组中的重复记录:

require 'set'

class Array
  def uniq_by
    seen = Set.new
    select{ |x| seen.add?( yield( x ) ) }
  end
end

我们可以使用上面的代码如下:

@messages = Messages.all.uniq_by { |h| h.body }

我想知道调用该方法时如何以及会发生什么。有人可以解释上面代码的内部吗?在uniq_by 方法中,我们没有做任何事情来处理块参数。 uniq_by 方法如何处理传递的参数?

【问题讨论】:

  • Array.uniqRuby 1.9.3 以来占用了一个块,因此不再需要此代码。

标签: ruby arrays set yield


【解决方案1】:

让我们分解一下:

seen = Set.new

创建一个空集

select{ |x| seen.add?( yield( x ) ) }

Array#select 将在块返回 true 时保留元素。

seen.add?(yield(x)) 如果块的结果可以添加到集合中,则返回 true,否则返回 false。

确实,yield(x) 将调用传递给uniq_by 方法的块,并将x 作为参数传递。

在我们的例子中,因为我们的块是{ |h| h.body },所以它和调用seen.add?(x.body)是一样的

由于一个集合是唯一的,当元素已经存在时调用add?将返回false。

因此它将尝试在数组的每个元素上调用.body 并将其添加到一个集合中,将元素保留在可能添加的位置。

【讨论】:

  • @pramod 它只是评估传递的块。
  • @MarekLipka uniq_by 方法在哪里处理块参数?
  • @pramod 通过调用“yield”。这就是“yield”的意思:请调用传递的块
  • @Interpidd:为了处理块参数,我们没有在 uniq_by 方法中做任何事情。那么在这种情况下如何处理bock参数(x)以及yield的返回值是多少
  • @pramod yield 将调用 {|h| h.body},用 h 代替当前的 x,因此返回 x.body
【解决方案2】:

uniq_by 方法接受一个块参数。这允许指定您希望通过什么标准将两个元素标识为“唯一”。

yield 语句将评估元素的给定块的值并返回元素body 属性的值。 因此,如果您像上面一样调用unique_by,则说明元素的属性body 必须是唯一的,才能使元素唯一。

要回答您的更具体问题:yield 将像方法一样调用传递的块{|h| h.body},用h 替换当前的x,因此返回x.body

【讨论】:

  • 你为什么不赞成这个而不给出一个简单的评论?这是一个完全正确的答案。
【解决方案3】:

在 Ruby 中,当您将 yield 关键字放入任何方法(例如 #bar)时,您明确告诉 #bar,您将使用带有方法 #bar 的块。所以yield 知道,方法块内部会被转换为Proc 对象,而yield 必须调用那个Proc 对象。

例子:

def bar
   yield
end

p bar { "hello" } # "hello" 
p bar # bar': no block given (yield) (LocalJumpError)

uniq_by 方法中,我们没有做任何事情来处理块参数。 uniq_by 方法如何处理传递的参数?

你确实做到了,那就是你输入了yield。一旦你把这个yield 放进去,现在方法很聪明,知道它应该是什么。在Messages.all.uniq_by { |h| h.body } 行中,您传递了一个块{ |h| h.body },在uniq_by 的方法定义中,该块已转换为Proc 对象,而yield 执行Proc#call

证明:

def bar
   p block_given? # true
   yield
end

bar { "hello" } # "hello"

更好理解:

class Array
  def uniq_by
    seen = Set.new
    select{ |x| seen.add?( yield( x ) ) }
  end
end

一样

class Array
  def uniq_by
    seen = Set.new
    # Below you are telling uniq_by, you will be using a block with it
    # by using `yield`.
    select{ |x| var = yield(x); seen.add?(var) }
  end
end

阅读yield的文档

从方法体内调用,将控制权交给作为方法调用的一部分提供的代码块(如果有)。如果没有提供代码块,调用yield 会引发异常。 yield 可以带参数;因此产生的任何值都绑定到块的参数。调用yield 的值就是执行代码块的值。

【讨论】:

  • 但是那个方法的最后一个参数是select,所以如果你在没有参数的情况下调用uniq_by方法会发生什么?
  • @pramod Not argument,说without block..你会得到一个错误..现在试试。
  • 是的,但是这个错误是因为 select 还是我们的 yield 调用?
  • @pramod 不是..#select,因为#select 得到了它的block,但是uniq_by 没有得到,如果你没有' t 调用没有阻塞的方法。
  • @pramod 当你调用yield时,必须已经通过了一个block,否则你会得到“no block given (yield) (LocalJumpError)”
【解决方案4】:

Array#select 返回一个新数组,其中包含给定块返回真值的数组的所有元素。

select 的块参数使用Set#add? 来确定元素是否已经存在。 add? 如果集合中已经存在相同的元素,则返回nil,否则返回集合本身并将元素添加到集合中。

块再次使用yield将参数(数组的一个元素)传递给另一个块(传递给uniq_by的块); yield 的返回值是块的返回值 ({|h| h.body })

select .. 语句与以下语句基本相似:

select{ |x| seen.add?(x.body) }

但是通过使用yield,代码避免了.body的硬编码,并将决策推迟到块。

【讨论】:

  • uniq_by 方法在哪里处理块参数?
  • @pramod,yield (x) 的参数被传递给块 ({ |h| ... })。
  • @pramod 在yield(x)。您评估块,将x 作为参数传递给它。
  • @pramod yield 以 x 作为参数显式调用传递的块
猜你喜欢
  • 2015-06-01
  • 2021-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多