5

在 Ruby(尤其是 Rails)中,您经常必须检查是否存在某些内容,然后对其执行操作,例如:

if @objects.any?
  puts "We have these objects:"
  @objects.each { |o| puts "hello: #{o}"
end

这是尽可能短的,一切都很好,但是如果你有@objects.some_association.something.hit_database.process而不是@objects呢?我将不得不在if表达式中第二次重复它,如果我不知道实现细节并且方法调用很昂贵怎么办?

显而易见的选择是创建一个变量,然后对其进行测试,然后对其进行处理,但是你必须想出一个变量名(呃),它也会在内存中徘徊,直到作用域结束。

为什么不这样:

@objects.some_association.something.hit_database.process.with :any? do |objects|
    puts "We have these objects:"
    objects.each { ... }
end

你会怎么做?

4

4 回答 4

3

any?请注意,如果您只打算发送,则没有理由检查数组是否至少有一个元素each,因为发送each到空数组是无操作的。

要回答您的问题,也许您正在寻找https://github.com/raganwald/andand

于 2012-12-11T21:27:15.727 回答
3

确实,使用变量会污染命名空间,但我仍然认为if (var = value).predicateis 是一个非常常见的习惯用法,通常完全没问题:

if (objects = @objects.some_association.hit_database).present?
  puts "We have these objects: #{objects}"
end

选项 2:如果您想以声明方式创建自己的抽象,也可以使用块:

@objects.some_association.hit_database.as(:if => :present?) do |objects|
  puts "We have these objects: #{objects}"
end

Object#as(options = {})的很直白。

于 2012-12-11T22:25:16.523 回答
2

编辑:如果您使用的是 Ruby 1.9,该Object#tap方法提供的功能与下面列出的代码相同。

听起来您只是希望能够在不污染范围的情况下保存对对象的引用,对吗?我们如何打开Object类并添加一个方法do,它只会将自己屈服于块:

class Object
  def do
    yield self if block_given?
    return self # allow chaining
  end
end

然后我们可以调用,例如:

[1,2,3].do { |a| puts a.length if a.any? }
=> 3
[].do { |a| puts a.length if a.any? }
=> nil
于 2012-12-11T21:30:28.313 回答
2

水龙头呢?

@objects.some_association.something.hit_database.process.tap do |objects|
  if objects.any?
    puts "We have these objects:"
    objects.each { ... }
  end
end
于 2012-12-11T21:38:27.053 回答