I've found three ways to write the same condition in Ruby:
#1
if 1==1
puts "true"
end
#2
puts "true" if 1==1
#3
if 1==1 then puts "true" end
Why can't I do this?
#4
if 1==1 puts "true"
I don't understand:
Why then and end are needed in #3, and,
Why I need to change the order to get #2 to work.
Statement #4 seems like the most natural way to write this. I don't understand why it's not possible.
The "if x then y end" syntax is meant for multiline conditionals while the "y if x" form is meant for concise single-line conditional statements. The then is necessary in the first case to tell Ruby that the condition is over (since Ruby doesn't require parens like C), and the end is necessary to tell Ruby that the whole if block is over (since it can be multiple lines).
You can replace the then with a semicolon, because an end-of-line also signals the end of the condition. You can't get rid of the end with a multiline if. Either use the second form or the ternary operator if you want a concise one-liner.
For example, suppose
x = true
the following will evaluate true, and return y
x ? y :
=> y
likewise, this will evaluate false and return nothing
!x ? y :
=>
add a term after the ':' for the else case
!x ? y : z
=> z
The thing is that both ways actually are a natural way to think:
if this is true then do something
do something if this is true
See? Ruby tries to get close to English syntax this way. The end is just necessary to end the block, while in the second version, the block is already closed with the if.
To actually answer your question, I think there is no chance to get the then and end removed. Remember Pascal / Delphi? You have a then there as well. It's typical only for C-style languages to not have it.
What about using a colon instead of then like this? http://www.java2s.com/Code/Ruby/Statement/layoutanifstatementisbyreplacingthethenwithacolon.htm
There are various ways you could short circuit if you wanted to do so.
The conditional statement is just part of the Ruby syntax to make it more English like.
Related
irb(main):186:0* begin puts "abc" end while false
abc
=> nil
irb(main):187:0> puts "abc" while false
=> nil
So when you use a block with the while modifier, the block is executed once (which seems like a do-while loop in many other languages). But if you use a single statement with the while modifier then it becomes more like just a while loop, where the condition is checked first. This seeks kind of surprising so why does the behavior exist?
This seems to be just a quirk of the language. ... while condition acts a a statement modifier except when it is after a begin ... end, where it behaves like a do ... while loop from other languages.
Yukihiro Matsumoto (Matz, the creator of Ruby) has said he regrets this, and would like to remove this behaviour in the future if possible.
There’s a bit more info in this blog post from New Relic, where I found the link to the mail list post: https://blog.newrelic.com/2014/11/13/weird-ruby-begin-end/
I think your confusion lies in the fact that begin/end is not considered a block. To showcase your example with a block take a look at this:
[3] pry(main)> loop { puts "hi" } while false
=> nil
Do while ensures at least one execution, but the second one is not a do while, is one of many sintax sugar of ruby. Its equivalent to
while false
puts 'abc'
end
Just remember the
puts 'abc' if false
Both are behaviouring correctly.
Hi all new to ruby is this if statement valid syntax?
if (verify_login_id = Login.where(params[:email_address], "active" =>1).select('id')# => [#<Login id: 767>]
verify_admin_id = Admin.where("login_id" => verify_login_id.first.id).exists? #=> true)
puts "trueee"
else
raise(ActiveRecord::RecordNotFound.new("Authorization is required to access this endpoint!"))
end
Although setting variable values within an if statement is syntactically valid, it is very problematic.
It can be mistakenly read as a comparison rather than assignment, and in many cases it is a result of someone trying to make a comparison and confusing the equals predicate == with the assignment operator =. That's why a lot of IDEs today mark such code as a warning and a probable error.
In your code it also seems quite unneeded... Break it into two more readable lines:
verified_login = Login.where(params[:email_address], "active" =>1).select('id').first # => [#<Login id: 767>]
if verified_login && Admin.where("login_id" => verified_login.id).exists? #=> true
puts "trueee"
else
raise(ActiveRecord::RecordNotFound.new("Authorization is required to access this endpoint!"))
end
Some more observations - your assignment to verify_login_id is not an id, but a Login object, and your assignment to verify_admin_id is not an id either - but a boolean (and you are not using it anyway). This might seem besides the point - but it adds up to an unreadable and an unmaintainable code.
No it is not a valid code. In the second line, it contains an invalid #-comment that has removed a single ) from the code. (exact snip is #=>true) and should be ) #=>true)
I've removed the comments from the code, added the missing parenthesis and the parser seems to accept it. I couldn't run it of course. So, try this one, it might work:
if (verify_login_id = Login.where(params[:email_address], "active" =>1).select('id')
verify_admin_id = Admin.where("login_id" => verify_login_id.first.id).exists?)
puts "trueee"
else
raise(ActiveRecord::RecordNotFound.new("Authorization is required to access this endpoint!"))
end
Regarding multiline condition in the if - yes, it is possible. Sometimes directly, sometimes with a small trick. Try this:
if (1+2 >=
3)
puts "as"
end
It works (at least on Ruby 1.9.3). However, this will not:
if (1+2
>=
3)
puts "as"
end
This is because of some internal specific of how Ruby interpreter/compiler is designed. In the last example, to make it work, you need to inform Ruby that the line has not ended. The following example works:
if (1+2 \
>=
3)
puts "as"
end
Note the \ added at the end of problematic line
I'm not a native English speaker. I know it sounds stupid nonetheless. I write in Python & C. But I can't quite understand how unless works.
Is it healthy to think of it from a logical standpoint? Eg, consider the if keyword: if condition. If condition is true, the code runs. What's the logical explanation for unless?
Is there any other way to think of it?
unless x is equivalent to if !x
Ruby unless modifier:
Syntax: code unless conditional
Executes code if conditional is false.
Example:
$var = 1
print "1 -- Value is set\n" if $var
print "2 -- Value is set\n" unless $var
$var = false
print "3 -- Value is set\n" unless $var
This will produce following result:
1 -- Value is set
3 -- Value is set
The easiest way is: unless is an opposite of if. It works in the same places (I think) as if:
foo = nil
puts "This will not show" if foo
puts "But this will" unless foo
if foo then
puts "This will not show"
end
unless foo
puts "But this will"
end
It looks nicer in the code if you write unless foo instead of if !foo - but that is only my opinion. In the longer form, you can even use else, but please, do not.
Here is a set of rules to follow to make the code more readable.
The way i think of it is to say:
"The block of code is going to be executed, unless the condition is true"
unless <condition>
<code>
end
hungry = true
unless hungry
puts "I'm writing Ruby programs!"
else
puts "Time to eat!"
end
This is the easy way to understand
Is there a way to achieve the following in one line? :
if foo
some_method this_var, other_var1, other_var2
else
some_method that_var, other_var1, other_var2
end
some_method((foo ? this_var : that_var), other_var1, other_var2)
Dan showed you how to do it in one line, but IMHO reducing line count at all costs is not great for readability. I'd do the following, which is shorter than what you have now and IMHO more readable than the one-liner:
var = foo ? this_var : that_var
some_method(var, other_var1, other_var2)
I'm not a big fan of the conditional operator. It is necessary in C, because in C, if is a statement, not an expression, whereas the conditional operator is an expression. In Ruby, however, everything is an expression, so, naturally, if is an expression, too:
some_method(if foo then this_var else that_var end, other_var1, other_var2)
However, as Michael Kohl points out, names can help clarify the intent of code, so maybe it is better to give a name to the argument:
meaningful_name = if foo then this_var else that_var end
some_method(meaningful_name, other_var1, other_var2)
Does anybody why I can write this:
ruby-1.8.7-p302 > a = %w( a b c)
=> ["a", "b", "c"]
ruby-1.8.7-p302 > while (i = a.shift) do; puts i ; end
a
b
c
=> nil
Which looks like passing a block to while.
And not:
while(i = a.shift) { puts i; }
Is it because the "do" of the while syntax is just syntaxic sugar and as nothing to do with the "do" of a block?
Is it because the do of the while syntax is just syntaxic sugar and as nothing to do with the do of a block?
More or less, yes. It's not syntactic sugar, it's simply a built-in language construct, like def or class, as #meagar already wrote.
It has nothing to do with the do of a block, except that keywords are expensive and so reusing keywords makes sense. (By "expensive" I mean that they limit the programmer in his expressiveness.)
In a while loop, there are two ways to separate the block from the condition:
the do keyword and
an expression separator.
There are, in turn, two different expression separators in Ruby:
the semicolon ; and
a newline
So, all three of the following are valid:
while i = a.shift do puts i end # do
while i = a.shift; puts i end # semicolon
while i = a.shift
puts i end # newline
[Obviously, that last one wouldn't be written that way, you would put the end on a new line, dedented to match the while. I just wanted to demonstrate what is the minimum needed to separate the parts of the while loop.]
By the way: it is highly un-idiomatic to put the condition in parentheses. There's also a lot of superfluous semicolons in your code. And the variable name i is usually reserved for an index, not an element. (I normally use el for generic elements, but I much prefer more semantic names.)
It is also highly un-idiomatic to iterate a collection manually. Your code would be much better written as
a.each(&method(:puts)).clear
Not only is it much easier to understand what this does (print all elements of the array and delete all items from it), it is also much easier to write (there is no way to get the termination condition wrong, or screw up any assignments). It also happens to be more efficient: your version is Θ(n2), this one is Θ(n).
And actually, that's not really how you would write it, either, because Kernel#puts already implements that behavior, anyway. So, what you would really write is this
puts a
a.clear
or maybe this
a.tap(&method(:puts)).clear
[Note: this very last one is not 100% equivalent. It prints a newline for an empty array, all the other ones print nothing.]
Simple. Clear. Concise. Expressive. Fast.
Compare that to:
while (i = a.shift) do; puts i ; end
I actually had to run that multiple times to be 100% clear what it does.
while doesn't take a block, it's a language construct. The do is optional:
while (i = a.shift)
puts i
end