在 Ruby 中,&&= 的使用是否合理?

7
Stack Overflow问题2068165中,一个回答提出了使用类似以下代码的想法:
params[:task][:completed_at] &&= Time.parse(params[:task][:completed_at])

作为DRYer的说法
params[:task][:completed_at] = Time.parse(params[:task][:completed_at]) if params[:task][:completed_at]

参数哈希将来自于(Rails/ActionView)表单。

这是一个著名的 ||= 习语的推论,如果左侧不为 nil/false,则设置值。

像这样使用 &&= 是否实际上是一种公认的 Ruby 习语,我是否错过了或者忘记了更常用的习语?现在时间已经很晚了...


解析器做了一些奇怪的事情:如果a有效,则a = 1,如果从未失败,则b = 1。 - Sam Saffron
雪上加霜,如果xxx;xxx=1;end 失败 - Sam Saffron
2个回答

5

这应该是可以的。如果没有其他问题,使用&&=表单时只会对params[:task]进行一次求值。

澄清一下:

params[:task][:completed_at] = params[:task][:completed_at] && ...

params上两次调用[](:task),并在params[:task]上分别调用[](:completed_at)[]=(:completed_at)一次。

params[:task][:completed_at] &&= ...

params 上调用 [](:task) 一次,并将其值存储为 [](:completed_at)[]=(:completed_at) 调用的结果。


实际示例,描述我试图说明的内容(基于 Marc-Andre 的示例代码;非常感谢):

class X
  def get
    puts "get"
    @hash ||= {}
  end
end

irb(main):008:0> x = X.new
=> #<X:0x7f43c496b130>
irb(main):009:0> x.get
get
=> {}
irb(main):010:0> x.get[:foo] = 'foo'
get
=> "foo"
irb(main):011:0> x.get[:foo]
get
=> "foo"
irb(main):012:0> x.get[:foo] &&= 'bar'
get
=> "bar"
irb(main):013:0> x.get[:foo] = x.get[:foo] && 'bar'
get
get
=> "bar"

请注意,使用“expanded”表单会导致“get”被打印两次,但是使用紧凑形式只会打印一次。

你的答案是不正确的。两种形式是等价的,params[:task][:completed_at]会被评估两次(假设它是真实的)(请参见我的答案)。 - Marc-André Lafortune
我想我需要澄清一下我正在回答什么。:-P - C. K. Young
我有点希望你是对的,但不,[:task] getter 在 &&= 形式中也会像 'if' 形式一样被执行多次。我已经更新了我的答案来展示这一点。 - Marc-André Lafortune
我会更新我的答案以展示我的反例,因为我认为你的更新答案仍然回答了一个不同于我回答的问题。 :-) - C. K. Young
顺便说一句:我并不是在提到 Time.parse(params[:task][:completed_at]) 函数调用;当然它会生成额外的访问。我只是在比较 &&= 形式与 = ... && ... 形式所产生的访问次数。 - C. K. Young

1

使用&&=,当LHS为false时,仅被读取一次,但不会被设置。这应该能更清楚地说明...

class Test
  def initialize(value)
    @v = value
  end
  def v=(value)
    puts "set"
    @v = value
  end
  def v
    puts "get=>#{@v}"
    @v
  end
end
t = Test.new(true)

t.v = t.v && true
puts '----'

t.v &&= true
puts '----'

t = Test.new(false) # lets make LHS false
t.v = t.v && true
puts '----'

t = Test.new(false) # lets make LHS false
t.v &&= true

结果:

get=>true
set
----
get=>true
set
----
get=>false
set
----
get=>false

当LHS为false时,这是可以的:如果它不存在,我们不想设置它,但是如果它存在但不是我们真正想要的形式,例如我们将日期/时间作为字符串获取并希望将其传递给模型以期望的类型,则可能需要转换/验证/解析它。 - Mike Woodhouse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接