我有一个关于ruby中的|| =语句的问题,这对我来说特别有意义,因为我正在使用它来写入memcache.我想知道的是,是否|| =先检查接收器,看它是否在调用setter之前设置,或者它实际上是别名x = x || y
这在普通变量的情况下并不重要,但使用类似的东西:
CACHE[:some_key] ||= "Some String"
Run Code Online (Sandbox Code Playgroud)
可能会做一个比简单变量集更昂贵的memcache写入.奇怪的是我在ruby api中找不到关于|| =的任何内容,所以我自己无法回答这个问题.
我当然知道:
CACHE[:some_key] = "Some String" if CACHE[:some_key].nil?
Run Code Online (Sandbox Code Playgroud)
会实现这一点,我只是在寻找最简洁的语法.
mol*_*olf 15
这非常容易测试:
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
Run Code Online (Sandbox Code Playgroud)
现在只需尝试||=
语法:
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
Run Code Online (Sandbox Code Playgroud)
因此,我们可以得出结论,如果缓存密钥已经存在,则不会进行任何分配.
Rubyspec的以下摘录表明这是设计使然,不应该依赖于Ruby实现:
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
Run Code Online (Sandbox Code Playgroud)
在同一个文件中,有一个类似的规范,[]
并[]=
强制要求相同的行为.
虽然Rubyspec仍在进行中,但很明显主要的Ruby实现项目打算遵守它.
根据ISO规范草案的 §11.3.1.2.2 ,
CACHE[:some_key] ||= "Some String"
Run Code Online (Sandbox Code Playgroud)
扩展到
o = CACHE
*l = :some_key
v = o.[](*l)
w = "Some String"
x = v || w
l << x
o.[]=(*l)
x
Run Code Online (Sandbox Code Playgroud)
或者,在更一般的情况下
primary_expression[indexing_argument_list] ?= expression
Run Code Online (Sandbox Code Playgroud)
(我用?
在这里表示任何操作,因此它可能是||=
,+=
,*=
,>>=
, %=
,...)
扩展到:
o = primary_expression
*l = indexing_argument_list
v = o.[](*l)
w = expression
x = v ? w
l << x
o.[]=(*l)
x
Run Code Online (Sandbox Code Playgroud)
因此,根据规范,[]=
将始终被调用.但实际情况并非如此(我测试了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
Run Code Online (Sandbox Code Playgroud)
因此,显然规范是错误的,或者所有五个当前发布的实现都是错误的.由于规范的目的是描述现有实现的行为,因此显然规范必定是错误的.
通常,作为第一近似值
a ||= b
Run Code Online (Sandbox Code Playgroud)
扩展到
a || a = b
Run Code Online (Sandbox Code Playgroud)
然而,有各种涉及,例如subleties的,无论是否a
是不确定的,是否a
是一个简单的变量或更复杂的表达像foo[bar]
或foo.bar
等等.
另请参阅这个问题的其他一些实例,这些实例已经在StackOverflow上得到了解答(例如,这一个).此外,在ruby-talk邮件列表上已经多次讨论过这个问题,现在有讨论主题,其唯一目的是总结其他讨论主题.(虽然请注意,该列表远未完成.)
归档时间: |
|
查看次数: |
1312 次 |
最近记录: |