如何在Ruby中将条件表达式作为参数传递?

时间:2010-06-10 19:06:25

标签: ruby

例如我正在尝试做的事,

def method_a(condition, params={}, &block)
   if condition
      method_b(params, &block)
   else
      yield
   end
end

我想尝试这样的方法,

method_a(#{@date > Date.today}, {:param1 => 'value1', :param2 => 'value2'}) do

end

结果是条件总是被评估为真。我如何使它工作?

3 个答案:

答案 0 :(得分:4)

作为条件评估的结果,你不能只传递condition吗?

method_a(@date > Date.today, {:param1 => 'value1', :param2 => 'value2'}) do
    puts "Do stuff"
end

答案 1 :(得分:1)

我认为这样做的合理方法是使用Proc或lambda:

def method_a(condition, params={}, &block)
   if condition.call
      method_b(params, &block)
   else
      yield
   end
end

method_a(lambda { @date > Date.today }, { :param1 => 'value1', :param2 => 'value2' }) do
  # ...
end

在您致电call之前,不会评估lambda。

答案 2 :(得分:1)

实际上,如果你在行中间没有那个评论,它几乎可以正常工作:

method_a(@date > Date.today, {:param1 => 'value1', :param2 => 'value2'}) do; end

顺便说一句:如果方法的最后一个参数是一个哈希,你可以不用花括号,这使得它几乎像Python风格的关键字参数一样读取:

method_a(@date > Date.today, :param1 => 'value1', :param2 => 'value2') do; end

如果您使用Ruby 1.9,并且您有一个哈希,其中键是符号,您可以使用新的替代哈希语法:

method_a(@date > Date.today, {param1: 'value1', param2: 'value2'}) do; end

将两个组合看起来像关键字参数:

method_a(@date > Date.today, param1: 'value1', param2: 'value2') do; end

method_a中,您可以通过使用保护子句而不是大喇叭if表达式来极大地提高可读性:

def method_a(condition, params={}, &block)
  return method_b(params, &block) if condition
  yield
end

或者相反,无论你认为哪个读得更好:

def method_a(condition, params={}, &block)
  return yield unless condition
  method_b(params, &block)
end

然而,这是一个巨大的代码味道。一个方法总是做一件事,而且只做一件事。 采用布尔参数的每个方法都违反了这个规则,因为它几乎按照定义执行两个事情:如果条件为真,则为一件事;如果条件为真,则为另一件事假的。

在原始代码中,这显然是显而易见的,因为整个方法周围有巨大的if表达式,而且两个分支中的代码完全不同。它比这更明显,因为else分支不仅具有与then分支完全不同的代码,它还完全忽略了传递给方法的参数!因此,该方法不仅根据条件而表现不同,甚至还有不同的签名

您真正想要做的是将方法拆分为两种方法。 method_a的用户无论如何都需要知道 这两种情况之间的不同行为是什么,并且他必须自己提供条件。相反,他可以首先调用正确的方法。所以,我会将method_a分成两部分:

def method_one(params={}, &block)
  method_b(params, &block)
end

def method_two
  yield
end

客户可以决定拨打哪一个:

if @date > Date.today then
  method_two(param1: 'value1', param2: 'value2')
else
  method_one do
    # something
  end
end

但是,如果你仔细观察method_one,你会发现它所做的只是将其参数未经修改地转发给method_b。因此,我们可以完全摆脱method_one,并让客户直接致电method_b

同样适用于method_two:它所做的就是调用块。客户端首先可以运行代码。

所以,现在我们的库代码如下所示:

# there is no spoon

没错!没有库代码! (method_b除外,这不是你问题的一部分。)

客户端代码如下所示:

if @date > Date.today then
  method_b(param1: 'value1', param2: 'value2')
else
  # something
end

违反此规则的方法的一个非常好的示例是Ruby核心库中的Module#instance_methods。它告诉您在特定模块和类中定义的所有实例方法,它采用一个布尔参数来决定此列表是否包含从超类继承的方法。 没人可以永远记住是否通过falsetrue没有人。 Jim Weirich在他关于良好设计的讨论中使用了这个例子,他通常会问听众哪个是继承的案例,哪个是直接案例。通常,高百分比会弄错。有时,百分比比仅仅翻转硬币更差

如果你看一下the documentation,那就太混乱了。我永远不会记住有条件的方式,我总是必须在文档中查找。哪个不是真的有用,因为官方文档,它是YARV和MRI的实际源代码的一部分,也有错误的方式回合