I do this a lot in javascript
some_var || some_var = function(){ return "blah"}();
I'm wondering what the equivalent in ruby might be so I can do
some_var ||= # sequence of operations
edit
the Proc.开发者_C百科new.call
has been brought to my attention, but I also just came across this in someone's code:
a ||= begin
# do some stuff
# return some stuff
end
Is this functionally equivalent to using a Proc.new.call
??
edit2 People seem to be confused as to what I'm trying to achieve. Imagine this in javascript:
function someExpensiveFunction(){
# do some really expensive stuff
return "some expensive calculations"
}
a || a = someExpensiveFunction();
Obviously sets a
once... calls expensive function once... In this case I don't care about scoping, I just need my return value to be a calculated sequence of events rather than a single value.
I'm pretty sure my example above a ||= begin; ... end;
is equivalent...
Per your comment:
don't care about scoping... just want a clean syntax for setting a variable using ||= that involves multiple lines of code
I'm not sure I understand why you feel you have t use ||= and a lambda. You could, for example, use
if(some_var.nil?)
# do some stuff
some_var = result_of_doing_some_stuf
end
Or, as you put it in your example:
a ||= begin
# do some stuff
# return some stuff
end
It isn't clear to me why you must be using a proc or lambda.
But if you are bent on using ||= and lambdas, you could do:
calculate = lambda { 1 + 1 }
some_var ||= calculate.call
s = Proc.new { 5 + 5 }.call
I wasn't sure what part you are asking about at first. The logical ||
does effect your expected operations. In Ruby, the equivalent is:
somevar = "blah" unless somevar;
If somevar is nil value or a false value change to "blah". If somevar is not nil or true, the line is not executed. The opposite action would be:
somevar = "blah" if somevar;
That would assign "blah" if somevar is true or not nil.
Ruby has language features similar to the self executing function, and many javascript libraries have been inspired by those features.
Check out this page for more information:
http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_containers.html
The section on "Blocks and Iterators" would be of interest here. Also "Blocks for Transactions" and "Blocks can be closures".
Essentially the Ruby block and the ruby lambda are the closest things to the Javascript's self executing function.
You can do it with lambda blocks
some_var ||= lambda { "something" }.call
or
some_var ||= Proc.new { "something" }.call
In JS self executing functions are usually used to avoid polluting scopes, so you can use local variables internally but don't expose them. Unfortunately, if using blocks in Ruby prior to 1.9, this is not the case, where blocks don't have their own scope.
# Ruby 1.8.7
x = "some initial value"
some_var ||= Proc.new { x = 10; x + 2 }.call #=> 12
x #=> 10
so if that's the case, there is probably a better solution for what you're trying to do. Ruby ain't Javascript.}
EDIT: Sorry, I forgot about variable definition in scopes vs. variable assignment. Updated snippet to reflect that
You can do:
def run
@a ||= begin
x = 1 + 1
puts "Calculated x"
x
end
end
puts "run = #{run}" # => Calculated x, run = 2
puts "run = #{run}" # => run = 2
Or use a lambda if you want to pass a variable
def run
@a ||= lambda do |i|
x = i + i
puts "Calculated x"
x
end.call(1)
end
puts "run = #{run}" # => Calculated x, run = 2
puts "run = #{run}" # => run = 2
But it's less magical and more readable to use a method if you're gonna call the lambda immediately
def calc(i)
x = i + i
puts "Calculated x"
x
end
def run
@a ||= calc(1)
end
puts "run = #{run}" # => Calculated x, run = 2
puts "run = #{run}" # => run = 2
The difference between a block (begin ... end) a proc and a lambda are the way the handle return. return is not allowed in the block. Return in a Proc return from the where it's been defined and return in Lambda do what is expected
So
def f()
a = Proc.new do
return 5 # exit from f
end.call
# never called
a+10
end
returns 5 (and not 15)
def g()
a = lambda do
return 5
end.call
a+10
end
returns 15 (as expected)
and
def f()
a = begin
return 5
end
a+10
end
doesn't compile.
If you are not using return in your block, then you can use begin... do (I like it by the way).
精彩评论