Multiline conditionals in Ruby and other mysteries -- why won't this compile? - ruby

I'm learning Ruby, but I'm having trouble predicting what Ruby will behave under certain circumstances. For example, the following causes a syntax error:
[2, 3, 4].any? do |x|
(x > 1
and x < 4)
end
but this compiles fine:
[2, 3, 4].any? do |x|
(x > 1 and
x < 4)
end
(the difference is in the placement of the and)
Why does the former fail to compile, while the latter succeeds, and how would I have known that? i.e.: since the above seems totally unintuitive, where is the formal grammar for Ruby, like https://docs.python.org/3/reference/grammar.html , or a guide like this https://docs.python.org/3/reference/lexical_analysis.html so I don't have to just guess at Ruby's behavior and figure it out by trial & error?

You have to end the line with an operator if you want the statement to be continued on the next line.
I will try to see if I can re-find the doc on this.
This is documented in the following book , chapter 2.
The Ruby Programming Language
By David Flanagan, Yukihiro Matsumoto
To paraphrase :
With having semicolons as explicit end of statement indicators, the ruby interpreter must figure out when a statement ends. It does this by figuring out if a statement is complete or not, if not it continues parsing on the next line. So
total = x +
y
is the same as total = x + Y since total = x + is not a complete statement
but
total = x
+y
is the same as total = x followed by the statement +y, which is just y
so the same applies for a conditional , if you end with a && for example the interpreter will continue parsing on the next line
x = a &&
b
is the same as x = a && b
but
x = a
&& b
is assigns a to x and generates a syntax error for the next line

Your best friend: CLICK ME
If you would like to format your conditionals like your first example(I don't know why you would) you can use the \format. Try this
[2, 3, 4].any? do |x|
(x > 1 \
and x < 4)
end
Basically just add the slash after each Boolean except your last one!
However standard Ruby convention is to be as short, descriptive, and concise as possible. A similar conditional could be written as so
As johnson said it should be
[2, 3, 4].any? { |x| x > 1 && x < 4 }
Happy Coding!

Related

Check if numeral within bounds in one statement

Is there a way to check if a number x is greater than a number a and less than a number b, without specifying x twice?
I can do this:
x = 4
a = 1
b = 10
x > a && x < b
...but is there a way to do something like this:
# not valid ruby, is there another way?
a < x < b
This does not answer the question exactly, please read the Edit part of the answer.
Tested this to work for x being an Integer and a Float.
(a..b).include? x
Edit:
As #spickermann pointed out in comments the original question excludes the beginning and the end of the interval.
To exclude the end is easy, this is what ... is for. Therefore
(a...b).include? x # is the same as `a <= x && x < b`
To exclude the start of the interval is not that easy. One could use next_float:
(a.to_f.next_float...b).include? x
However even the (a...b) is something I would not use because it is not so common literal and in my opinion decreases readability of the code. With a.to_f.next_float we are making some really awkward code that may work for Integers and Floats but I would be afraid of other Numeric data types.
Unless someone brings completely different approach I would stick with x > a && x < b

What is wrong with my if statement 'if x + y = z'

I am trying to evaluate if a selected item meats a requirement. I am trying to evaluate the result of nominal + x against required.
I am using the following code:
if #weight.nominal + x = required
weights << #weight.id
end
However, it adds nominal + x together. Why is that, and how do I do what I want?
As #Pavan said in the comment, your conditional might be wrong. It should be == instead of =. = is the assignment.
if #weight.nominal + x == required ... end
Here what happens "behind the scene" with your original code:
required is assigned to x.
Add x to #weight.nominal (#weight.nominal + x)
Evaluate the result from step 2, which is always true.
Execute the code within if block, which is weights << #weight.id
Eventually, your x's value is lost, it will take required value instead and your conditional is useless since it always true.

Using for-loop to add numbers together

If I randomly put in two numbers (first number is smaller), how do I use a for-loop to add all the numbers between and itself?
ex:
first number: 3
second number: 5
the computer should give an answer of '12'.
How do I do that using a for-loop?
In Ruby we seldom use a for loop because it leaves litter behind. Instead, you can very simply do what you want using inject:
(3..5).inject(:+) # => 12
This is using some of the deeper Ruby magic (:+), which is a symbol for the + method and is passed into inject. How it works is a different question and is something you'll need to learn later.
Don't insist on doing something in a language using a particular construct you learned in another language. That will often force non-idiomatic code and will keep you from learning how to do it as other programmers in that language would do it. That creates maintenance issues and makes you less desirable in the workplace.
Simple for loop across the range you defined:
puts "Enter first number: "
first = gets.to_i
puts "Enter second number: "
second = gets.to_i
total = 0
for i in (first..second) do
total += i
end
puts total
Note that if you don't enter a valid number, it will converted to 0. Also this assumes the second number is larger than the first.
In Rails, or in plain-vanilla Ruby with ActiveSupport, you can do something even simpler than a for loop, or than what other people wrote.
(first_num..second_num).sum
This is shorthand for sum in Ruby:
sum = 0
(first_num..second_num).each { |num| sum += num }
first, second = [3,5]
for x in (0..0) do
p (first + second)*(second - first + 1) / 2
end
I know you said for loop, but why not use what Ruby gives you?
> a = 3
> b = 5
> a.upto(b).inject(0) {|m,o| m += o}
=> 12
If you insist on a for loop...
> m = 0
=> 0
> for i in 3..5
* m += i
* end
=> 3..5
> m
=> 12
Since Ruby 2.4 you directly call sum on an Enumerable.
For Example [1, 2, 3].sum #=> 6
In Ruby it's very rare to see a for loop. In this instance a more idiomatic method would be upto:
x = 3
y = 5
total = 0
x.upto(y) do |n|
total += n
end
puts total
# => 12
Another method would be to use reduce:
total = x.upto(y).reduce do |sum, n|
sum += n
end
...which can be shortened to this:
total = x.upto(y).reduce(&:+)

Why parentheses change returning value?

I'm learning ruby and I got stuck with probable simple problem. There is the code:
str = 'abc'
a = 1
b = 2
a = str.reverse if str.size > 3
b = (str.reverse if str.size > 3)
p a
p b
Output:
1
nil
Can somebody tell me what these parentheses change in return value ? Is it kind of "block" of code ?
They are two different statements.
The first one is a conditional assignment:
a = str.reverse if str.size > 3
The if applies to the whole line. If str.size is not greater than 3, then absolutely nothing happens; a is not touched at all. You could also write it this way:
if str.size > 3 then
a = str.reverse
end
Being able to stick the if on the end just lets you do it in one line instead of a block.
The second one is an assignment of a conditional value.
b = (str.reverse if str.size > 3)
The value of b will always be changed in this case, no matter what; the value of str.size just determines what it is changed to. You could also use the block-form of if here:
b = if str.size > 3 then
str.reverse
end
The important difference is that the assignment to b happens outside the if, so it's not conditional; it always happens.
Parentheses don't create blocks, but they do determine precedence. Whenever you have a statement that could be interpreted multiple ways depending on the order in which things happen, what actually happens is determined by precedence rules. For instance, standard arithmetic rules tell us that this is true:
3 + 2 × 4 = 11
The answer isn't 20 because multiplication has precedence over addition. Parentheses let you change that; the above is equivalent to
3 + (2 × 4) = 11
and if you want the answer to be 20, then you could write this instead:
(3 + 2) × 4 = 20
Same thing goes for Ruby. Without parentheses, the first line is equivalent to this parenthesized version:
(a = str.reverse) if str.size > 3
which makes it clear that the assignment is what is guarded by the condition, not just the value being assigned.
Parentheses will change priority during operation.
Code between parenthesis will be evaluated as boolean due to its status as conditional expression.
Like #Mark Reed said, there are differents statements. And it is up to your intention to use the correct one. Hope it helped. ;)

Syntax Error in Ruby, Unexpected Pipe Character in a Do

I'll try to be concise this time around! I'm still working Project Euler, this time back to #2. My real issue here is I'm terrible with Ruby. When I run the following code
x = 1
y = 2
sum = 2
while x >= 4_000_000 do |x|
sum += y if y % 2 == 0
z = x + y
x = x ^ y # xor magic
y = x ^ y # xor magic
x = x ^ y # xor magic
y = z
end
p sum
My interpreter kicks out the following output:
/Users/Andy/Documents/Programming/Ruby/ProjectEuler/P2.rb:4: syntax error, unexpected '|'
while x >= 4_000_000 do |x|
                         ^
I'm reading why's (Poignant) Guide to Ruby, and I'm pretty sure I have the pipe syntax correct for the Do. Could someone point out what I'm doing wrong here? I've tried messing around in a lot of different ways and am coming up short handed
while (x >= 4_000_000)
foo
end
You don't even have to pass in x, because it's accessible in the scope of the enclosing block.
while does not take a block. Remove the do |x| part.
while is not a method that takes a block, it is a ruby looping statement. It considers the part between the while and do (or newline) to be the logical test and the part between the do (or newline) and end keyword to be the loop body.
while x < 10 do x += 1; puts x; end
while x < 10
x += 1
puts x
end
Contrast this with something like the Array's each method which takes in a block. Here the each method calls your block for each element of the array (passed into the block as x)
[1,2,3].each do |x|
puts x
end
You accidentally combined the two, asking the while loop to call your code block with the loop counter to be passed in as x. That is not how while works... hence the parsing exception.
What an interesting question! It inspired me to take a shot at the problem, too. Here's my solution.
First, some preparatory work:
class Enumerator
def lazy_select
Enumerator.new do |y|
each do |el|
y.yield(el) if yield el
end
end
end
alias_method :lazy_find_all, :lazy_select
end
module Enumerable
def sum
reduce(:+)
end
end
module Math
ROOT5 = Math.sqrt(5)
PHI = 0.5 + ROOT5/2
def self.fibonacci(n)
Integer(0.5 + PHI**n/ROOT5)
end
end
class Integer
def fibonacci
Math.fibonacci(self)
end
end
Now an Enumerator which generates an infinite sequence of Fibonacci Numbers:
fibs = Enumerator.new do |y|
n = -1
loop do
y.yield (n += 1).fibonacci
end
end
And the nice thing is that we can now directly express the original problem statement in code:
Find the sum of all the even-valued terms in the sequence which do not exceed four million.
puts fibs.lazy_find_all(&:even?).take_while {|n| n <= 4_000_000 }.sum
I think that this is a much more Rubyish way to solve the problem. You write in your question that you are terrible with Ruby. But that's not actually the problem. The real problem is that you are good with C! In other words, the real problem is that you simply aren't writing Ruby, you are writing C with Ruby syntax.
Two good examples are:
y % 2 == 0
and
x = x ^ y
y = x ^ y
x = x ^ y
The Ruby way to write these would be
y.even?
and
x, y = y, x

Resources