What do you think would be the result of the next expression in Ruby?
a = 10 && b = 25
Try to calculate in the ming and only then use irb. So, if we take a look at the Ruby documentation about Operators Precedence then we will se that && operator has a higher priority than =. So you must think that Ruby will evaluate the expression in the next way:
a = ((10 && b) = 25)
But Ruby does a job in another way:
a = (10 && (b = 25))
# => 25
So, the priority of the = in b = 25 is higher, then &&. Can anybody explain why it is happend like so?
This has to do with the way Ruby identifies bareword identifiers
When it comes across an identifier like a, it has to resolve it by checking to see if it is a keyword, local variable or method in that order.
keyword - if a is a keyword then use it as a keyword
local variable - is there an equal sign to the right of a, then it must be a local variable. If not check if there is any local variable a defined. These points are very important, it is the reason why you can't call writer methods in instance methods without explicitly calling self.
Take for example this code
class Person
attr_accessor :name #creates both attr_reader name & attr_writer name=(text)
def print_name
name # => attr_reader method name is called and returns nil
name = 'Bola'#even though we have the name= method, it doesn't get called
#what happens is a local variable name is created instead
#this is as a result of how ruby interpreted the bareword identifier name
#not a keyword but has an equal sign so must be a local variable
name # this time local variable is used instead of method because it is resolved first
end
end
method if it's not resolved as a keyword or local variable then it assumes it's a method and tries to call it.
So this is how the code is evaluated
what is to the left of && 10, which it can make sense of
compare that 10 to what is on the right of && which is b but it has to evaluate what b is so it resolves it using the procedure I described above which results in local variable b = 25. Assignment operations always return the values on their right which is 25
compare 10 and 25 which returns 25
finally a = 25
With that being said && does have higher priority and the = signs are of the same precedence. Hope this explanation was clear.
Related
Given the method:
def foo(a,b=5,c=1)
return a+(b*c)
end
Running foo(1) should return 6. However, how would you go about doing something like this: foo(1,DEFAULT,2). I need to change the third value, but use the default second value.
How would you do this? (Note: I can't just change the order of the variables because they are arguments for a method from a gem)
You can't do it, in the terms posed. This sort of situation is exactly why named (keyword) parameters were introduced in Ruby 2. But your parameters with default values, according to the terms of the question, are not named.
Therefore, they are positional — that is why the optional parameters must come last — and the rule is, accordingly, that this method must be called with at least one argument (because a is not optional), and any further arguments will be used in the order supplied to fill in the corresponding parameters.
Thus, you can supply a, or a and b, or a and b and c. You cannot supply a and c alone, as you could easily do if these parameters were named.
Two obvious solutions spring to mind.
Call the method, supplying the default value of the second parameter as the second argument. Presumably you know what it is, so it's not much of a hardship:
foo(1,5,2)
Write a trampoline method that does the same thing, but where the parameters are named:
def foo(a,b=5,c=1)
return a+(b*c)
end
def bar(a,b:5,c:1)
return foo(a,b,c)
end
bar(1,c:2) # => 11
One solution is to default to nil and then default in the body of the code:
def foo(a,b=nil,c=nil)
b ||= 5
c ||= 1
a + (b * c)
end
Or you can do that as part of the computation:
def foo(a,b=nil,c=nil)
a + (b || 5) * (c || 1)
end
I want to do a compact error checking assignment in ruby.
class User
attr_accessor :x
end
user = User.new
user.x = 5
a = b || user.x
I want to figure out which of these is the first valid attribute and assign it, similarly to how javascript handles different API's, i.e.:
var AudioContext = window.AudioContext||window.webkitAudioContext;
audioContext = new AudioContext();
and figure out which was valid.
With ruby however, similar syntax gives errors when I reference an undefined variable. i.e.:
a = 10
b = 7
c = a || b
c # => 10
vs
a = 10
c = b || a # => Error: b is undefined
Is there a clean way to do this? Or at the very least, what is the best way to do this?
I'm working with a large code that I haven't created, and I am not permitted to change it.
UPDATE:
I think the real use case is kind of relevant to this question so i'll explain it.
I have a module which saves something to the DB every time a model in rails is updated, this update requires an id field, this id field is inside the model that includes my module, however not every model maintains the same naming convention for this id. The ternary operator equivalent of what I want to do is
id = defined?(self.id) ? self.id : defined?(self.game_id) ? self.game_id : defined?(self.app_id) ? self.app_id : nil
which is hard to read and write compared to the js equivalent
You can use defined? to test if a name refers to something recognizable in current scope (method, local variable, etc):
c = defined?(b) ? b : a # of course this assumes that 'a' is defined
Although it's pretty iffy that you're assigning from local variables that haven't been defined - how does that come up?
Or are you always testing for properties? In which case, respond_do? may be the better choice.
Edit
I was using the ternary operator as an example, you could always use an if/elsif/else block, of course.
Since you're only testing methods, respond_to? is more convenient than defined? because it takes symbols, rather than expressions, to which you can apply any logic you want:
def invoke_first *names
names.each do |name|
if respond_to? name
return send name
end
end
return nil # or, more likely, raise something
end
or, more concisely:
def invoke_first *names
send names.find(lambda {raise 'no method'}){|n| respond_to?(n)}
end
include in your model and use as:
invoke_first(:foo_id, :bar_id, :baz_id)
Okay so there is a much more concise way of doing this, but it has a side-effect of assigning something to the undefined var.
This breaks
a = 1
c = b || a # => b is undefined
This works
a = 1
c = b ||= a # => c == 1, b == 1
The above assigns b to c if b is valid, then falls back on a. a is only assigned to b (and c) if b is undefined/invalid.
Modifications made to b below are also made to a:
a = 'taco'
b = a
b << 's'
a #=> tacos
When I assign a variable x by pointing it at another variable y, x remains a shortcut to y; x and y will be identical. This seems confirmed by the dup method; by saying b = a.dup, b gets a's value at the moment of assignment rather than a shortcut.
But this doesn't happen with integers. When I assign b, it seems to be getting the value of a:
a = 4815
b = a
b /= 2
a #=> 4815
It seems like string variables pass by reference while integer variables pass by value. That doesn't seem right; I know something is wrong in my understanding, but I'm not sure what.
You need to understand the difference between variables and values. A variable points to a value. Multiple variables can point to the same value.
In Ruby, the only way to modify a variable is via assignment, either simple
foo = :something
or compound assignment
foo ω= :something # for some operator ω
# e.g.
foo += :something
foo <<= :something
foo ||= :something
If you modify a value, that modification will be visible no matter what variable you use to access that value.
Think about it this way: my mum calls me "son", my friends call me "Jörg", my close friends call me "jwm", my band colleagues call me "Jörgislaw", my girlfriend calls me "baby", but no matter what they call me, if I cut my hair, my hair will be gone, regardless of what name they use to refer to me. If, however, my girlfriend assigns a new value to the label "baby", then that does not affect me. (Well … bear with me, it's an analogy :-D )
In Ruby, << typically modifies the receiver, whereas / doesn't.
However, Ruby is always pass-by-value. But the value being passed is a pointer to a value, so that multiple variables can contain multiple copies of the same pointer to the same value.
Irrespective of what you are arguing, b /= 2 is a syntax sugar for b = b / 2. Thus, the new b is b / 2 (or a / 2), which has nothing to do with a.
That's correct, see Fixnum:
Fixnum objects have immediate value. This means that when they are assigned or passed as parameters, the actual object is passed, rather than a reference to that object.
Assignment does not alias Fixnum objects. There is effectively only one Fixnum object instance for any given integer value, so, for example, you cannot add a singleton method to a Fixnum. Any attempt to add a singleton method to a Fixnum object will raise a TypeError.
I'm getting familiar with ruby send method, but for some reason, I can't do something like this
a = 4
a.send(:+=, 1)
For some reason this doesn't work. Then I tried something like
a.send(:=, a.send(:+, 1))
But this doesn't work too. What is the proper way to fire plus equals through 'send'?
I think the basic option is only:
a = a.send(:+, 1)
That is because send is for messages to objects. Assignment modifies a variable, not an object.
It is possible to assign direct to variables with some meta-programming, but the code is convoluted, so far the best I can find is:
a = 1
var_name = :a
eval "#{var_name} = #{var_name}.send(:+, 1)"
puts a # 2
Or using instance variables:
#a = 2
var_name = :#a
instance_variable_set( var_name, instance_variable_get( var_name ).send(:+, 1) )
puts #a # 3
See the below :
p 4.respond_to?(:"+=") # false
p 4.respond_to?(:"=") # false
p 4.respond_to?(:"+") # true
a+=1 is syntactic sugar of a = a+1. But there is no direct method +=. = is an assignment operator,not the method as well. On the other hand Object#send takes method name as its argument. Thus your code will not work,the way you are looking for.
It is because Ruby doesn't have = method. In Ruby = don't work like in C/C++ but it rather assign new object reference to variable, not assign new value to variable.
You can't call a method on a, because a is not an object, it's a variable, and variables aren't objects in Ruby. You are calling a method on 4, but 4 is not the thing you want to modify, a is. It's just not possible.
Note: it is certainly possible to define a method named = or += and call it, but of course those methods will only exist on objects, not variables.
class Fixnum
define_method(:'+=') do |n| self + n end
end
a = 4
a.send(:'+=', 1)
# => 5
a
# => 4
This might miss the mark a bit, but I was trying to do this where a is actually a method dynamically called on an object. For example, with attributes like added_count and updated_count for Importer I wrote the following
class Importer
attr_accessor :added_count, :updated_count
def increment(method)
send("#{method}=", (send(method) + 1))
end
end
So I could use importer.increment(:added_count) or importer.increment(:updated_count)
Now this may seem silly if you only have these 2 different counters but in some cases we have a half dozen or more counters and different conditions on which attr to increment so it can be handy.
I'm processing a record-based text file: so I'm looking for a starting string which constitutes the start of a record: there is no end-of-record marker, so I use the start of the next record to delimit the last record.
So I have built a simple program to do this, but I see something which suprises me: it looks like Ruby is forgetting local variables exist - or have I found a programming error ? [although I don't think I have : if I define the variable 'message' before my loop I don't see the error].
Here's a simplified example with example input data and error message in comments:
flag=false
# message=nil # this is will prevent the issue.
while line=gets do
if line =~/hello/ then
if flag==true then
puts "#{message}"
end
message=StringIO.new(line);
puts message
flag=true
else
message << line
end
end
# Input File example:
# hello this is a record
# this is also part of the same record
# hello this is a new record
# this is still record 2
# hello this is record 3 etc etc
#
# Error when running: [nb, first iteration is fine]
# <StringIO:0x2e845ac>
# hello
# test.rb:5: undefined local variable or method `message' for main:Object (NameError)
#
From the Ruby Programming Language:
(source: google.com)
Blocks and Variable Scope
Blocks define a new variable scope: variables created within a block exist only within that block and are undefined outside of the block. Be cautious, however; the local variables in a method are available to any blocks within that method. So if a block assigns a value to a variable that is already defined outside of the block, this does not create a new block-local variable but instead assigns a new value to the already-existing variable. Sometimes, this is exactly the behavior we want:
total = 0
data.each {|x| total += x } # Sum the elements of the data array
puts total # Print out that sum
Sometimes, however, we do not want to alter variables in the enclosing scope, but we do so inadvertently. This problem is a particular concern for block parameters in Ruby 1.8. In Ruby 1.8, if a block parameter shares the name of an existing variable, then invocations of the block simply assign a value to that existing variable rather than creating a new block-local variable. The following code, for example, is problematic because it uses the same identifier i as the block parameter for two nested blocks:
1.upto(10) do |i| # 10 rows
1.upto(10) do |i| # Each has 10 columns
print "#{i} " # Print column number
end
print " ==> Row #{i}\n" # Try to print row number, but get column number
end
Ruby 1.9 is different: block parameters are always local to their block, and invocations of the block never assign values to existing variables. If Ruby 1.9 is invoked with the -w flag, it will warn you if a block parameter has the same name as an existing variable. This helps you avoid writing code that runs differently in 1.8 and 1.9.
Ruby 1.9 is different in another important way, too. Block syntax has been extended to allow you to declare block-local variables that are guaranteed to be local, even if a variable by the same name already exists in the enclosing scope. To do this, follow the list of block parameters with a semicolon and a comma-separated list of block local variables. Here is an example:
x = y = 0 # local variables
1.upto(4) do |x;y| # x and y are local to block
# x and y "shadow" the outer variables
y = x + 1 # Use y as a scratch variable
puts y*y # Prints 4, 9, 16, 25
end
[x,y] # => [0,0]: block does not alter these
In this code, x is a block parameter: it gets a value when the block is invoked with yield. y is a block-local variable. It does not receive any value from a yield invocation, but it has the value nil until the block actually assigns some other value to it. The point of declaring these block-local variables is to guarantee that you will not inadvertently clobber the value of some existing variable. (This might happen if a block is cut-and-pasted from one method to another, for example.) If you invoke Ruby 1.9 with the -w option, it will warn you if a block-local variable shadows an existing variable.
Blocks can have more than one parameter and more than one local variable, of course. Here is a block with two parameters and three local variables:
hash.each {|key,value; i,j,k| ... }
Contrary to some of the other answers, while loops don't actually create a new scope. The problem you're seeing is more subtle.
Prerequisite knowledge: a brief scoping demo
To help show the contrast, blocks passed to a method call DO create a new scope, such that a newly assigned local variable inside the block disappears after the block exits:
### block example - provided for contrast only ###
[0].each {|e| blockvar = e }
p blockvar # NameError: undefined local variable or method
But while loops (like your case) are different, because a variable defined in the loop will persist:
arr = [0]
while arr.any?
whilevar = arr.shift
end
p whilevar # prints 0
Summary of the "problem"
The reason you get the error in your case is because the line that uses message:
puts "#{message}"
appears before any code that assigns message.
It's the same reason this code raises an error if a wasn't defined beforehand:
# Note the single (not double) equal sign.
# At first glance it looks like this should print '1',
# because the 'a' is assigned before (time-wise) the puts.
puts a if a = 1
Not Scoping, but Parsing-visibility
The so-called "problem" - i.e. local-variable visibility within a single scope - is due to ruby's parser. Since we're only considering a single scope, scoping rules have nothing to do with the problem. At the parsing stage, the parser decides at which source locations a local variable is visible, and this visibility does not change during execution.
When determining if a local variable is defined (i.e. defined? returns true) at any point in the code, the parser checks the current scope to see if any code has assigned it before, even if that code has never run (the parser can't know anything about what has or hasn't run at the parsing stage). "Before" meaning: on a line above, or on the same line and to the left-hand side.
An exercise to determine if a local is defined (i.e. visible)
Note that the following only applies to local variables, not methods. (Determining whether a method is defined in a scope is more complex, because it involves searching included modules and ancestor classes.)
A concrete way to see the local variable behavior is to open your file in a text editor. Suppose also that by repeatedly pressing the left-arrow key, you can move your cursor backward through the entire file. Now suppose you're wondering whether a certain usage of message will raise the NameError. To do this, position your cursor at the place you're using message, then keep pressing left-arrow until you either:
reach the beginning of the current scope (you must understand ruby's scoping rules in order to know when this happens)
reach code that assigns message
If you've reached an assignment before reaching the scope boundary, that means your usage of message won't raise NameError. If you don't reach any assignment, the usage will raise NameError.
Other considerations
In the case a variable assignment appears in the code but isn't run, the variable is initialized to nil:
# a is not defined before this
if false
# never executed, but makes the binding defined/visible to the else case
a = 1
else
p a # prints nil
end
While loop test case
Here's a small test case to demonstrate the oddness of the above behavior when it happens in a while loop. The affected variable here is dest_arr.
arr = [0,1]
while n = arr.shift
p( n: n, dest_arr_defined: (defined? dest_arr) )
if n == 0
dest_arr = [n]
else
dest_arr << n
p( dest_arr: dest_arr )
end
end
which outputs:
{:n=>0, :dest_arr_defined=>nil}
{:n=>1, :dest_arr_defined=>nil}
{:dest_arr=>[0, 1]}
The salient points:
The first iteration is intuitive, dest_arr is initialized to [0].
But we need to pay close attention in the second iteration (when n is 1):
At the beginning, dest_arr is undefined!
But when the code reaches the else case, dest_arr is visible again, because the interpreter sees that it was assigned 2 lines above.
Notice also, that dest_arr is only hidden at the start of the loop; its value is never lost (as evidenced by the final contents being [0, 1]).
This also explains why assigning your local before the while loop fixes the problem. The assignment doesn't need to be executed; it only needs to appear in the source code.
Lambda example
f1 = ->{ f2 }
f2 = ->{ f1 }
p f2.call()
# The following fails because the body of f1 tries to access f2 before an assignment for f2 was seen by the parser.
p f1.call() # undefined local variable or method `f2'.
Fix this by putting an f2 assignment before f1's body. Remember, the assignment doesn't actually need to be executed!
f2 = nil # Could be replaced by: if false; f2 = nil; end
f1 = ->{ f2 }
f2 = ->{ f1 }
p f2.call()
p f1.call() # ok
Method-masking gotcha
Things get really hairy if you have a local variable with the same name as a method:
def dest_arr
:whoops
end
arr = [0,1]
while n = arr.shift
p( n: n, dest_arr: dest_arr )
if n == 0
dest_arr = [n]
else
dest_arr << n
p( dest_arr: dest_arr )
end
end
Outputs:
{:n=>0, :dest_arr=>:whoops}
{:n=>1, :dest_arr=>:whoops}
{:dest_arr=>[0, 1]}
A local variable assignment in a scope will "mask"/"shadow" a method call of the same name. (You can still call the method by using explicit parentheses or an explicit receiver.) So this is similar to the previous while loop test, except that instead of becoming undefined above the assignment code, the dest_arr method becomes "unmasked"/"unshadowed" so that the method is callable w/o parentheses. But any code after the assignment will see the local variable.
Some best-practices we can derive from all this
Don't name local variables the same as method names in the same scope
Don't put the initial assignment of a local variable inside the body of a while or for loop, or anything that causes execution to jump around within a scope (calling lambdas or Continuation#call can do this too). Put the assignment before the loop.
I think this is because message is defined inside the loop. At the end of the loop iteration "message" goes out of scope. Defining "message" outside of the loop stops the variable from going out of scope at the end of each loop iteration. So I think you have the right answer.
You could output the value of message at the beginning of each loop iteration to test whether my suggestion is correct.
Why do you think this is a bug? The interpreter is telling you that message may be undefined when that particular piece of code executes.
I'm not sure why you're surprised: on line 5 (assuming that the message = nil line isn't there), you potentially use a variable that the interpreter has never heard of before. The interpreter says "What's message? It's not a method I know, it's not a variable I know, it's not a keyword..." and then you get an error message.
Here's a simpler example to show you what I mean:
while line = gets do
if line =~ /./ then
puts message # How could this work?
message = line
end
end
Which gives:
telemachus ~ $ ruby test.rb < huh
test.rb:3:in `<main>': undefined local variable or method `message' for main:Object (NameError)
Also, if you want to prepare the way for message, I would initialize it as message = '', so that it's a string (rather than nil). Otherwise, if your first line doesn't match hello, you end up tring to add line to nil - which will get you this error:
telemachus ~ $ ruby test.rb < huh
test.rb:4:in `<main>': undefined method `<<' for nil:NilClass (NoMethodError)
you can simply do this :
message=''
while line=gets do
if line =~/hello/ then
# begin a new record
p message unless message == ''
message = String.new(line)
else
message << line
end
end
# hello this is a record
# this is also part of the same record
# hello this is a new record
# this is still record 2
# hello this is record 3 etc etc