ruby运算符|| =是否是智能的?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (14)

我有一个关于ruby中的|| =语句的问题,当我使用它写入memcache时。我想知道的是,确实|| =首先检查接收器,看它在调用setter之前是否已设置,或者它是否是字面上的别名x = x || y

这在正常变量的情况下并不重要,但是使用如下的东西:

CACHE[:some_key] ||= "Some String"

可能会做一个比简单变量集更昂贵的memcache写入。我奇怪地找不到有关|| =的rubyapi,所以我一直无法自己回答。

我当然知道:

CACHE[:some_key] = "Some String" if CACHE[:some_key].nil?

只是寻找最简洁的语法。

提问于
用户回答回答于

非常容易测试:

class MyCache
  def initialize
    @hash = {}
  end

  def []=(key, value)
    puts "Cache key '#{key}' written"
    @hash[key] = value
  end

  def [](key)
    puts "Cache key '#{key}' read"
    @hash[key]
  end
end

现在尝试一下||=语法:

cache = MyCache.new
cache["my key"] ||= "my value"  # cache value was nil (unset)
# Cache key 'my key' read
# Cache key 'my key' written

cache["my key"] ||= "my value"  # cache value is already set
# Cache key 'my key' read

所以我们可以得出这样的结论:如果缓存键已经存在,就不会进行分配。

describe "Conditional operator assignment 'obj.meth op= expr'" do
  # ...
  it "may not assign at all, depending on the truthiness of lhs" do
    m = mock("object")
    m.should_receive(:foo).and_return(:truthy)
    m.should_not_receive(:foo=)
    m.foo ||= 42

    m.should_receive(:bar).and_return(false)
    m.should_not_receive(:bar=)
    m.bar &&= 42
  end
  # ...
end

在同一文件中,有一个类似的规范[],并[]=认为强制要求相同的行为。

尽管Rubyspec仍然是一项正在进行的工作,但Ruby的主要实施项目打算遵守它已经变得很清楚。

用户回答回答于

根据ISO规范草案的 §11.3.1.2.2 ,

CACHE[:some_key] ||= "Some String"

扩展到

o = CACHE
*l = :some_key
v = o.[](*l)
w = "Some String"
x = v || w
l << x
o.[]=(*l)
x

或者,在更一般的情况下

primary_expression[indexing_argument_list] ω= expression

(我用ω在这里表示任何操作,因此它可能是||=+=*=>>=%=,...)

扩展到:

o = primary_expression
*l = indexing_argument_list
v = o.[](*l)
w = expression
x = v ω w
l << x
o.[]=(*l)
x

因此,根据该规范,[]=永远被调用。但实际情况并非如此(我测试了MRI,YARV,Rubinius,JRuby和IronRuby):

def (h = {}).[]=(k, v) p "Setting #{k} to #{v}"; super end
h[:key] ||= :value # => :value
# "Setting key to value"
h[:key] ||= :value # => :value

所以,很明显,无论规范是错误的还是所有五个当前发布的实现都是错误的。由于规范的目的是描述现有实现的行为,显然这个规范必须是错误的。

一般来说,作为第一个近似值

a ||= b

扩展到

a || a = b

然而,涉及的各种转租,例如,是否a未定义,a是简单变量还是更复杂的表达式,如foo[bar]or foo.bar等等。

扫码关注云+社区