Why Does Conditional Statement and Assigning Value in Ruby Fails If the "If" Statement Is at End of Clause

Why does conditional statement and assigning value in ruby fails if the if statement is at end of clause?

It fails because of the way the parser works.

From the parser's point of view the variable tmp2 exists from the point in the code at which it is first assigned until the point where it goes out of scope. For this it does not matter, when (or if) the assignment is actually executed, just when the parser sees the assignment (i.e. it depends on the assignments position in the code).

Edit: To expand on that bit:

The decision whether a name is a local variable or a method call is made by the parser. The parser makes that decision solely based on whether or not it has already seen an assignment to that variable. So when the parser sees tmp2 before seeing tmp2 = ..., it decides that here tmp2 refers to a method. When that part of the code is actually executed, it tries to call the method tmp2 which does not exist so you get the error.

Is assignment in a conditional clause good ruby style?

It is GOOD style to use assignments in conditionals. If you do so, wrap the condition in parentheses.

# bad (+ a warning)
if v = array.grep(/foo/)
do_something(v)
# some code
end

# good (MRI would still complain, but RuboCop won't)
if (v = array.grep(/foo/))
do_something(v)
# some code
end

# good
v = array.grep(/foo/)
if v
do_something(v)
# some code
end

See the community style guide for more information

Ruby syntax oddity

It's due to the parsing of lines, vs execution time. In the first version, value is parsed and set and then the puts evaluated. In the second line, when the parser gets to the variable puts value, it has not yet been defined. In other words, it can't run the line to set the variable, until it has first parsed the line.

What is Returned in Ruby if the Last Statement Evaluated is an If Statement

If your if statement doesn't result in any code being run, it returns nil, Otherwise, it returns the value of the code that was run. Irb is a good tool to experiment with such stuff.

irb(main):001:0> i = if false then end
=> nil
irb(main):002:0> i = if true then end
=> nil
irb(main):007:0> i = if false then "a" end
=> nil
irb(main):008:0> i = if false then "a" else "b" end
=> "b"

Why does Ruby include? behave differently when nested within an if/end conditional?

Here's what's happening in each of these examples:

First Example

This example outputs 1. Your string includes a j or J. regardless of the previous line. The my_string.include? check is being ignored as it's not used in a comparison anywhere, so the second line is just a regular puts.

Second Example

The second example is a little more interesting. ("j" or "J") is syntax in Ruby which will output the first of the provided arguments which evaluates to true. "j" evaluates to true because it's not nil or false, so it becomes the argument of the second include? method. include? is case-sensitive, so it will return false – the string Jack does not include a lowercase j.

You can try this out by running irb and entering something like 1 or 2 or false and 1; you'll see pretty quickly that the first true argument is returned (or false if no arguments are true).

There's no good way to make this work as-is, other than updating the include? check to use something like set intersections. An easier solution may be to downcase the input before checking characters.

Avdi Grimm posted a good video on using and and or in Ruby.

Third Example

The third example is calling include? twice on the string, and returning true when it hits the second call, hence the if statement being evaluated.

Update

papirtiger's answer got me thinking, so I did a bit of digging with Ripper using the following script:

require 'ripper'
require 'pp'

expression = <<-FOO
if true
puts 'Hello'
end
FOO

pp Ripper.sexp(expression)

Here's the result:

[:program,
[[:if,
[:var_ref, [:@kw, "true", [1, 3]]],
[[:command,
[:@ident, "puts", [2, 2]],
[:args_add_block,
[[:string_literal,
[:string_content, [:@tstring_content, "Hello", [2, 8]]]]],
false]]],
nil]]]

After updating the expression to the following:

expression = <<-FOO
if
true
puts 'Hello'
end
FOO

This was the new output:

[:program,
[[:if,
[:var_ref, [:@kw, "true", [2, 2]]],
[[:command,
[:@ident, "puts", [3, 2]],
[:args_add_block,
[[:string_literal,
[:string_content, [:@tstring_content, "Hello", [3, 8]]]]],
false]]],
nil]]]

It looks as though Ruby does indeed ignore any whitespace and evaluate the next expression. I don't have enough expertise to dig much deeper, but after trying a few more examples (such as throwing a dozen newlines in after an if statement), I'm convinced.

Case conditional in ruby doesn't enter in when

There are multiple problems here:

case args
when args = "order1"

Firstly, args is an Array - so it cannot possibly be equal to a String. I'm not sure what you intended to happen here, so can't say exactly how to fix it.

Secondly, = is an assignment operator, whereas == performs an equality check.

And lastly, this is a case statement, not an if statement, so you shouldn't actually be performing an equality check here... Either of these would have made sense syntactically:

case args
when "order1"
# ...
end

# OR:

case
when args == "order1"
# ...
end

Also, note that your question description is a bit confusing. You said:

the when loop

but this is not a loop. You could call it a "clause", or a "statement", but it's certainly not a "loop".

One line if statement not working

Remove if from if @item.rigged ? "Yes" : "No"

Ternary operator has form condition ? if_true : if_false

Multiple Conditional Statement Execution in Ruby

You can simply add the values and later filter them out. Will save you from all those checks:

:customer => {
:first_name => @transaction.nome,
:last_name => @transaction.last_name,
:country_name => @transaction.nation.try(:name)
}.reject{ |_, v| v.nil? }

Ruby: Variables defined in If/else statement are accessible outside of if/else?

Variables are local to a function, class or module defintion, a proc, a block.

In ruby if is an expression and the branches don't have their own scope.

Also note that whenever the parser sees a variable assignment, it will create a variable in the scope, even if that code path isn't executed:

def test
if false
a = 1
end
puts a
end

test
# ok, it's nil.

It's bit similar to JavaScript, though it doesn't hoist the variable to the top of the scope:

def test
puts a
a = 1
end

test
# NameError: undefined local variable or method `a' for ...

So even if what you were saying were true, it still wouldn't be nil.



Related Topics



Leave a reply



Submit