Ruby if vs end of the line if behave differently? - ruby

Why doesn't this code work?
b if b = true
Error: undefined local variable or method `b'
But this does:
if b = true
b
end
Shouldn't they be the same?

This is a very good question. It has to do with the scoping of variables in Ruby.
Here is a post by Matz on the Ruby bug tracker about this:
local variable scope determined up to down, left to right. So a local variable first assigned in the condition of if modifier is not effective in the left side if body. It's a spec.

In the first version as soon as k is hit, the parser pukes because it hasn't been seen yet.
In the second version, k is part of an assignment expression, and is parsed differently.

I don't know the reason but the problem that the interpreter tries to lookup the variable k before evaluating the condition.
If you write it like this, there won't be any error and works as you expected:
k = nil
h = {k: 1}
v = k if k = h.delete(:k)

you have put only one '='
Try with '=='
Then you will get error
In second example, you are assigning 'true' to b.

Because the Ruby interpreter creates a local variable when it sees an assignment
In the second case, it hasn't yet seen the assignment, so the variable doesn't exist when the expression is parsed.
To be more precise, a method is first parsed into an internal representation, and then, perhaps, the code will eventually be called and actually executed.
Local variables are created in that parsing pass. It's a matter of declaration, it just means that the interpreter becomes aware of them. They won't be created in the sense of being given space or a value until the surrounding method is called by someone.

Related

Warning : previous definition of Variable was here - Ruby

Every single time I load my program, even for the fist time, it says
file.rb:9: warning: already initialized constant W_mum
file.rb:6: warning: previous definition of W_mum was here.
a little help here?
W_mum = gets.to_i
elsif (W_mum = 1)
Ruby uses two different "storage bins" for data: variables and constants. In your source code, you can identify them y their first letter: constants always have a capital letter at the start of their name, variables a lower-case letter.
In your case, you thus have a constant named W_mum. Now, when you first set a value to a constant and then later set a different value to it, Ruby will show a warning (as such: you can set new values to constants, but you should not).
Now, as for why Ruby warns here: in your elsif, you are actually assigning the constant the value 1. This might be a bug though. Instead of an assignment with =, you likely intended to use a comparison here, using the == operator.

"NoMethodError: undefined method '-#' for ["some-text"]:Array" when inside while loop [duplicate]

The pre/post increment/decrement operator (++ and --) are pretty standard programing language syntax (for procedural and object-oriented languages, at least).
Why doesn't Ruby support them? I understand you could accomplish the same thing with += and -=, but it just seems oddly arbitrary to exclude something like that, especially since it's so concise and conventional.
Example:
i = 0 #=> 0
i += 1 #=> 1
i #=> 1
i++ #=> expect 2, but as far as I can tell,
#=> irb ignores the second + and waits for a second number to add to i
I understand Fixnum is immutable, but if += can just instanciate a new Fixnum and set it, why not do the same for ++?
Is consistency in assignments containing the = character the only reason for this, or am I missing something?
Here is how Matz(Yukihiro Matsumoto) explains it in an old thread:
Hi,
In message "[ruby-talk:02706] X++?"
on 00/05/10, Aleksi Niemelä <aleksi.niemela#cinnober.com> writes:
|I got an idea from http://www.pragprog.com:8080/rubyfaq/rubyfaq-5.html#ss5.3
|and thought to try. I didn't manage to make "auto(in|de)crement" working so
|could somebody help here? Does this contain some errors or is the idea
|wrong?
(1) ++ and -- are NOT reserved operator in Ruby.
(2) C's increment/decrement operators are in fact hidden assignment.
They affect variables, not objects. You cannot accomplish
assignment via method. Ruby uses +=/-= operator instead.
(3) self cannot be a target of assignment. In addition, altering
the value of integer 1 might cause severe confusion throughout
the program.
matz.
One reason is that up to now every assignment operator (i.e. an operator which changes a variable) has a = in it. If you add ++ and --, that's no longer the case.
Another reason is that the behavior of ++ and -- often confuse people. Case in point: The return value of i++ in your example would actually be 1, not 2 (the new value of i would be 2, however).
It's not conventional in OO languages. In fact, there is no ++ in Smalltalk, the language that coined the term "object-oriented programming" (and the language Ruby is most strongly influenced by). What you mean is that it's conventional in C and languages closely imitating C. Ruby does have a somewhat C-like syntax, but it isn't slavish in adhering to C traditions.
As for why it isn't in Ruby: Matz didn't want it. That's really the ultimate reason.
The reason no such thing exists in Smalltalk is because it's part of the language's overriding philosophy that assigning a variable is fundamentally a different kind of thing than sending a message to an object — it's on a different level. This thinking probably influenced Matz in designing Ruby.
It wouldn't be impossible to include it in Ruby — you could easily write a preprocessor that transforms all ++ into +=1. but evidently Matz didn't like the idea of an operator that did a "hidden assignment." It also seems a little strange to have an operator with a hidden integer operand inside of it. No other operator in the language works that way.
I think there's another reason: ++ in Ruby wouldn't be remotely useful as in C and its direct successors.
The reason being, the for keyword: while it's essential in C, it's mostly superfluous in Ruby. Most of the iteration in Ruby is done through Enumerable methods, such as each and map when iterating through some data structure, and Fixnum#times method, when you need to loop an exact number of times.
Actually, as far as I have seen, most of the time +=1 is used by people freshly migrated to Ruby from C-style languages.
In short, it's really questionable if methods ++ and -- would be used at all.
You can define a .+ self-increment operator:
class Variable
def initialize value = nil
#value = value
end
attr_accessor :value
def method_missing *args, &blk
#value.send(*args, &blk)
end
def to_s
#value.to_s
end
# pre-increment ".+" when x not present
def +(x = nil)
x ? #value + x : #value += 1
end
def -(x = nil)
x ? #value - x : #value -= 1
end
end
i = Variable.new 5
puts i #=> 5
# normal use of +
puts i + 4 #=> 9
puts i #=> 5
# incrementing
puts i.+ #=> 6
puts i #=> 6
More information on "class Variable" is available in "Class Variable to increment Fixnum objects".
I think Matz' reasoning for not liking them is that it actually replaces the variable with a new one.
ex:
a = SomeClass.new
def a.go
'hello'
end
# at this point, you can call a.go
# but if you did an a++
# that really means a = a + 1
# so you can no longer call a.go
# as you have lost your original
Now if somebody could convince him that it should just call #succ! or what not, that would make more sense, and avoid the problem. You can suggest it on ruby core.
And in the words of David Black from his book "The Well-Grounded Rubyist":
Some objects in Ruby are stored in variables as immediate values. These include
integers, symbols (which look like :this), and the special objects true, false, and
nil. When you assign one of these values to a variable (x = 1), the variable holds
the value itself, rather than a reference to it.
In practical terms, this doesn’t matter (and it will often be left as implied, rather than
spelled out repeatedly, in discussions of references and related topics in this book).
Ruby handles the dereferencing of object references automatically; you don’t have to
do any extra work to send a message to an object that contains, say, a reference to
a string, as opposed to an object that contains an immediate integer value.
But the immediate-value representation rule has a couple of interesting ramifications,
especially when it comes to integers. For one thing, any object that’s represented
as an immediate value is always exactly the same object, no matter how many
variables it’s assigned to. There’s only one object 100, only one object false, and
so on.
The immediate, unique nature of integer-bound variables is behind Ruby’s lack of
pre- and post-increment operators—which is to say, you can’t do this in Ruby:
x = 1
x++ # No such operator
The reason is that due to the immediate presence of 1 in x, x++ would be like 1++,
which means you’d be changing the number 1 to the number 2—and that makes
no sense.
Some objects in Ruby are stored in variables as immediate values. These include integers, symbols (which look like :this), and the special objects true, false, and nil. When you assign one of these values to a variable (x = 1), the variable holds the value itself, rather than a reference to it.
Any object that’s represented as an immediate value is always exactly the same object, no matter how many variables it’s assigned to. There’s only one object 100, only one object false, and so on.
The immediate, unique nature of integer-bound variables is behind Ruby’s lack of pre-and post-increment operators—which is to say, you can’t do this in Ruby:
x=1
x++ # No such operator
The reason is that due to the immediate presence of 1 in x, x++ would be like 1++, which means you’d be changing the number 1 to the number 2—and that makes no sense.
Couldn't this be achieved by adding a new method to the fixnum or Integer class?
$ ruby -e 'numb=1;puts numb.next'
returns 2
"Destructive" methods seem to be appended with ! to warn possible users, so adding a new method called next! would pretty much do what was requested ie.
$ ruby -e 'numb=1; numb.next!; puts numb'
returns 2 (since numb has been incremented)
Of course, the next! method would have to check that the object was an integer variable and not a real number, but this should be available.

Ruby object variables and why they always equal the same name?

In the books I'm reading, I always see
def initialize (side_length)
#side_length = side_length
end
If the object variable always equals the variable with the same name why not write the language to just equal it without having to type it out?
For example:
def initialize (side_length)
#side_length # this could just equal side_length without stating it
end
That way we don't have to type it over with over.
In the example given:
def initialize(side_length)
#side_length = side_length
end
The #side_length = side_length is just an assignment, passing the available argument to an instance variable, in this case it happens to be the same name as the argument.
However those two values don't have to have same names - it's usually named that way for readability/convention reasons. That same code could just as easily be:
def initialize(side_length)
#muffin = side_length
end
The above code is perfectly fine, it just wouldn't read as well (and might slightly confuse someone giving it a first glance). Here's another example of the argument not being assigned to an instance variable of the same name.
It would be possible to write the language in a way which assumes that the argument variable should automatically be assigned to an instance variable of the same name, but that would mean more logic for handling arguments, and that same assumption may result in more restrictions for the developer, for example, someone who may actually want to assign side_length to #muffin for whatever reason.
Here's a SO question similar to this one - the accepted answer provides an interesting solution.
Hope this helps!
It is because "the object variable [does not] always [equal] the variable withthe [sic] same name".

Why can I refer to a variable outside of an if/unless/case statement that never ran?

Why does the following code not throw an error?
if false
x = 0
end
x #=> nil
Whereas the following does throw an error:
y # NameError: undefined local variable or method `y' for main:Object
The same thing happens with unless & case statements.
It's because of how the Ruby parser works. Variables are defined by the parser, which walks through the code line-by-line, regardless of whether it will actually be executed.
Once the parser sees x =, it defines the local variable x (with value nil) henceforth in the current scope. Since if/unless/case/for/while do not create a new scope, x is defined and available outside the code block. And since the inner block is never evaluated as the conditional is false, x is not assigned to (and is thus nil).
Here's a similar example:
defined?(x) and x = 0
x #=> nil
Note that this is a rather high-level overview of what happens, and isn't necessarily exactly how the parser works.
This has to do with a quirk of Ruby's scoping rules.
In ruby, an undecorated variable x appearing by itself could either be a local variable or a method call -- the grammar can't tell which. It's up to the parser to figure it out as it resolves local variable references. The rule is simple: if an assignment to a variable of the same name has been seen already in the local scope, then the reference is a local variable, and the reference is bound to that local variable. Otherwise, it's a method call, and it will be looked up as such at runtime.
Local variable references in Ruby are optimized into array lookups (each local variable is assigned a 'slot', and bound local variable references generated by the parser are converted into slot references). The array is initialized with all nil:
/* initialize local variables */
for (i=0; i < local_size; i++) {
*sp++ = Qnil;
}
Thus, if you refer to a local variable that hasn't been assigned, through a bound local reference (which can only happen if there was a skipped assignment above the reference in the same local scope), you get nil.
I thought your question was interesting so I tried to look it up and found this:
I don't understand ruby local scope
The correct answer seems to be put Jorg.
Lets look at what happens when you try to access a variable that isn't initialized:
NameError: undefined local variable or method `UNDECLAREDVAR' for main:Object
The exception states that it is unavailable to evaluate whether a variable or method. The reason it doesn't throw the same exception is because uninitialized local variables are set to nil. So puts x is a okay because the interpretor knows that x is variable but uninitialized and not a method.

not able to do ruby string comparision

I have been trying to do ruby string comparision which doesnt seem to work
max == "value"
if user.name == max
I also tried using the eql method but nothing seems to work
max.eql(user.name)
This is not working althought the values are same.
What could be the reason?
This is because of white spaces. Try doing
if user.name.strip == max
strip will remove all the white spaces
Ruby use the same semantics as C when it comes to assignment versus comparison.
x = y
will assign x the value of y Even if this is done inside an if expression.
The second attempt to use eql (which really should be eql?) will fail, as x.eql?(y) returns true if the x and y are the same object. It is not sufficient that they have the same value.
Note, that in a language like Ruby, many variables can be bound to the same object. If you update the object destructively, this will be reflected on all variables bound to the same object. On the other hand, it will not affect variables bound the another object, even if that object happened to have an equal value as the first object.
Update: The poster changed the question after this answer was posted.

Resources