if statement after variable assignment - how common? - ruby

I was recently discussing the following Ruby syntax with a colleague:
value = if a == 0
"foo"
elsif a > 42
"bar"
else
"fizz"
end
I haven't seen this sort of logic much personally, but my colleague notes that it's actually a fairly common Rubyism. I tried googling the topic and found no articles, pages, or SO questions discussing it, making me believe it might be a very matter-of-fact technique. Another colleague, however, finds the syntax confusing and would instead write the above logic like this:
if a == 0
value = "foo"
elsif a > 42
value = "bar"
else
value = "fizz"
end
The disadvantage there being the repeated declarations of value = and the loss of an implicit else nil, if we wanted to use it. This also feels like it lines up with a lot of other syntactical sugar features found in Ruby.
My question is, how common is this technique in Ruby? Is there some sort of consensus on whether the community feels like this should be used or avoided?

value = if condition
x
else
y
end
is common. It lends itself to cleaning up this situation:
if condition
value = x
else
value = y
end
Have a look at this Ruby style guide. It's a popular guide in how Ruby code should be written.
https://github.com/bbatsov/ruby-style-guide#use-if-case-returns

The fact that if and case return values makes for some very tight, tidy, and yet still understandable code. It's a common pattern in Ruby when you're dealing with assignment through branching.
The way I tend to approach formatting these is to apply a level of indentation to make the assignment clear, but not overly "push" the code in too far:
value =
if a == 0
"foo"
elsif a > 42
"bar"
else
"fizz"
end
Or if you want a case:
value =
case
when a == 0
"foo"
when a > 42
"bar"
else
"fizz"
end
In many cases you'll see a method that has an if as the body to determine the result:
def value(a)
if a == 0
"foo"
elsif a > 42
"bar"
else
"fizz"
end
end
Then there's no quirky indentation necessary.

Related

Hackerrank New Year Chaos - Ruby - Print as return

I am trying to solve HackerRank's 'New year Chaos' challenge.
The script is supposed to print 'Too chaotic' in a certain case.
Regardless of wether or not my solution is correct, my current problem is that I can't seem to print it as it will then return nil. My solution is not accepted either it I replace it with a puts as it will include quotes.
bribes = 0
chaotic = false
q.each_with_index do |num, index|
if num - 1 - index > 2
chaotic = true
elsif index == 0
else
bribes += q.slice(0, index).count { |x| x > num }
end
end
return chaotic ? print('Too chaotic') : bribes
end
Hacker rank output
thanks!
Interesting problem to tackle. The main difference between print and puts is the included newline with puts. You want to use puts in this case. Here's a good discussion on the topic: https://www.rubyguides.com/2018/10/puts-vs-print/
I'm not sure what was happening with the quotes you mention, so I'd update your solution with puts, and tackle the quotes next.

How to create a custom while block

I know that in ruby we can use a while loop, but I want to know if I can create a custom one so I can make something like this:
custom_while i < 5 do
puts i
i += 1
end
I currently have this code:
def custom_while(condition)
loop do
break if not condition
yield
end
end
i = 0
custom_while i < 5 do
puts i
i += 1
end
However, when condition is evaluated, it always get true (because it considers the first evaluation of i < 5 = true only.
Any help will be appreciated!
Note: This is for educational purposes only.
You almost had it. So, your problem is that the condition is only evaluated once? Well, what construct do we know that we can evaluate as often as we want? That's right: functions! So, let's make condition a function (or a Proc in Ruby lingo):
def custom_while(condition)
loop do
break unless condition.()
yield
end
end
i = 0
custom_while -> { i < 5 } do
puts i
i += 1
end
# 0
# 1
# 2
# 3
# 4
This is unfortunately not as nice looking as in other languages. Ruby's syntax and semantics are aggressively optimized for methods that take exactly 1 "function" as an argument. Ruby has a special syntactically and semantically light-weight construct for that, namely blocks. As soon as you have more than one, though, you're out of luck.
Compare this with languages that have proper block literals, like Smalltalk, for example. In Smalltalk, you could write a method while:do:, and call it like this:
i := 0.
while: [i < 5] do: [Transcript write: i. i := i + 1].
In fact, in Smalltalk, the syntax for blocks is so lightweight that there are no control structures at all in the language. if/then/else is simply an instance method of Boolean, for example:
i % 2 == 0 ifTrue: [Transcript write: "even"] ifFalse: [Transcript write: "odd"].
And while is actually an instance method of Block, so in reality, your example would look like this:
i := 0.
[i < 5] whileTrue: [Transcript write: i. i := i + 1]
Note: I make no guarantees for the Smalltalk code, I didn't test it.
The problem is, the condition is being evaluated before it's passed in, so it will never change.
Make the condition a function that you evaluate inside the loop, or use a macro to make it cleaner.

program using while with array values

I am trying to write a small program that goes through an array's values outputting each individual value. When it reaches 15 it stops and outputs "too big".
Why is my logic below wrong, it makes sense to me..
x = [10,11,12,13,14,15,16,17,18]
def counting
for x[y]
while y < 15
puts y
else
puts "too big"
end
puts counting
I'm just learning sorry if this is a really simple solution.
That's nothing at all like Ruby syntax. You want a .each and a simple if statement:
x.each do |y|
if y < 15
puts y
else
puts "too big"
break
end
end
It appears though you are trying to use Ruby like you would a c-style programming language. It's possible and viable, albeit not recommended.
Code Blocks
Ruby has structure known as code blocks. Code blocks are sort of like anonymous functions.
You can read more about code blocks here.
x = [10,11,12,13,14,15,16,17,18]
# This is a code block.
x.each do |y| # The y between the '|'s is the parameter caught by the code block
if y < 15
puts y
else
puts "Too big."
break # Break out of the loop
end
end
If you want a one liner:
x.each {|y| y < 15 ? puts(y) : puts("too big") || break }
If you insist using while, it can be done as following:
i = 0
while i do
x[i] < 15 ? puts(x[i]) : puts("too big") || break
i+=1
end
I think the cleanest way to do this using while would be:
def counting(x)
i = 0
while x[i] < 15
puts x[i]
i += 1
end
puts 'too big'
end
counting([10,11,12,13,14,15,16,17,18])
Why is my logic below wrong, it makes sense to me..
Given that you program isn't even syntactically legal, it's impossible to tell what its semantics would be if it were syntactically legal, let alone why those semantics were wrong. However, here's an idiomatic Ruby solution (you will almost never use nor see while in any Ruby code, and you will certainly never see for):
puts x.take_while {|n| n < 15 }
puts 'too big'
I prefer writing in point-free style, however in this case the only way to do that is to make use of the symmetry of the condition and invert the order of operands, which will lead to awkward logic:
x.take_while(&15.method(:>))
Ao, in this case I would rather avoid point-free style, because it no longer matches the original specification literally. Instead of "take all numbers less than 15", we now have "take all numbers such that 15 is greater than those numbers", which reads awkward.

Ruby: enumerator can't be coerced to Fixnum; struggling with Project Euler #5

The challenge is to find the smallest integer foo for which:
foo % 1..20 == 0
My current attempt is brute force
until foo % 1.upto(20) == 0 do
foo++
end
This outputs the error unexpected keyword end. But if I don't put the end keyword into irb the code never runs, because the block isn't closed.
I made an empty test case to see where my error lays
until foo % 1.upto(20) == 0 do
end
This throws a new error: enumerator can't be coerced to a fixnum. I imagine this means you can't directly perform modulus upon a range and expect a neat boolean result for the whole range. But I don't know where to go from here.
My first attempts forewent brute force in favor of an attempt at something more efficient/elegant/to-the-point and were as follows:
foo = 1
1.upto(20) {|bar| foo *= bar unless foo % i == 0}
gave the wrong answer. I don't understand why, but I'm also interested in why
foo = 1
20.downto(1) {|bar| foo *= bar unless foo % i == 0}
outputs a different answer.
EDIT: I would have used for loops (I got my feet wet with programming in ActionScript) but they do not work how I expect in ruby.
Your first solution is wrong because 1.upto(20) is an enumerator, that is, essentially an iterator over the values 1 to 20, and it cannot be used as a number for modulo or comparison to another number, since it itself isn't a number.
You really need two "loops" here:
foo = 1
foo += 1 until (1..20).all? { |i| foo % i == 0 }
the first loop is the until, and then the all? is another loop of sorts, in that it ensures that the block ({ |i| foo % i == 0 }) is true for each element in the range it is called on ((1..20)). Note that I'm using the one-line "backwards" syntax (which also works for if, unless, while, …)—the above is equivalent to:
foo = 1
until (1..20).all? { |i| foo % i == 0 } do
foo += 1
end
# foo => 232792560
Also, this is incredibly inefficient, Project Euler often involves a bit more math than programming, and a non-brute-force solution will likely involve more math but be far faster.
Try this:
until 1.upto(20).reject{|i| foo % i == 0 }.empty? do
foo += 1
end
I know it's not directly the OP question, but this is way easier to achieve with just:
puts (1..20).reduce(:lcm)
It's so simple that it seems like isn't fair to solve it this way, but that's precisely why Ruby is my language of choice for Project Euler.
See also this question
If this were me, I'd define a function to test the condition:
def mod_test(num)
test = (1..20).map {|i| num % i == 0}
test.all? # all are true
end
and then a loop to try different values:
foo = 20
until mod_test(foo) do
foo += 20
end
(Thanks to Dylan for the += 20 speedup.)
I'm sure that there's a clever way to use the knowledge of foo % 10 == 0 to also imply that foo % 5 == 0 and foo % 2 == 0, and perform only tests on prime numbers between 1 and 20, and probably even use that information to construct the number directly -- but my code ran quickly enough.

Idiomatic ruby for temporary variables within a method

Within a method, I am using i and j as temporary variables while calculating other variables. What is an idiomatic way of getting rid of i and j once they are no longer needed? Should I use blocks for this purpose?
i = positions.first
while nucleotide_at_position(i-1) == nucleotide_at_position(i)
raise "Assumption violated" if i == 1
i -= 1
end
first_nucleotide_position = i
j = positions.last
while nucleotide_at_position(j+1) == nucleotide_at_position(j)
raise "Assumption violated" if j == sequence.length
j += 1
end
last_nucleotide_position = j
Background: I'd like to get rid of i and j once they are no longer needed so that they aren't used by any other code in the method. Gives my code less opportunity to be wrong. I don't know the name of the concept - is it "encapsulation"? The closest concepts I can think of are (warning: links to TV Tropes - do not visit while working) Chekhov'sGun or YouHaveOutlivedYourUsefulness.
Another alternative would be to put the code into their own methods, but that may detract from readability.
What makes you think splitting the code up into multiple methods will hurt the readability? In my experience, splitting even small or medium sized pieces of code into multiple methods can greatly improve readability.
Ruby (like JS) doesn't create a new scope for each block by default (as C++, etc. do). However, in Ruby 1.9, you can try:
last_nucleotide_position = nil
proc { |;i, j|
i = positions.first
while nucleotide_at_position(i-1) == nucleotide_at_position(i)
raise "Assumption violated" if i == 1
i -= 1
end
first_nucleotide_position = i
j = positions.last
while nucleotide_at_position(j+1) == nucleotide_at_position(j)
raise "Assumption violated" if j == sequence.length
j += 1
end
last_nucleotide_position = j
}.call()
See How to make block local variables the default in ruby 1.9?. Any variables that you want to be used outside the block should be defined before-hand (like last_nucleotide_position).
FM is right that a separate method may be more readable.
I think the term you are looking for is variable scope -- in other words, you are looking for ways to confine the scope of i and j. But you don't need to worry about that. The problem at hand calls for creating separate methods -- regardless of scope considerations.
This will improve readability, because it will allow the reader to grok the code starting at the high level and then boring in deeper only as needed. It will also improve testability because your small methods will do exactly one thing.
def calc_first_nucleotide_position(po)
i = po.first
while nucleotide_at_position(i-1) == nucleotide_at_position(i)
raise "Assumption violated" if i == 1
i -= 1
end
i
end
# etc...
first_nucleotide_position = calc_first_nucleotide_position(positions)
last_nucleotide_position = calc_last_nucleotide_position(positions)
# etc...
You are looking for the Ruby equivalent of Lisp's let special operator. Ruby does not support it out of the box but you can hack it in very easily, and the resulting syntax is like this:
x = 10
scope { |x|
x = 30
}
puts x #=> 10
see: http://banisterfiend.wordpress.com/2010/01/07/controlling-object-scope-in-ruby-1-9/
If all you want is to keep new variables from spilling out into the rest of your program, you can wrap your code in a block using 1.times. Any new variables you create inside the block will be destroyed when you close the block. Just keep in mind that any changes you make to pre-existing variables will remain once the block closes.
y = 20
1.times do
# put your code in here
i = 1
puts x = y # => 20, because y is available from outside the block
y = 'new value' # We can change the value of y but our changes will
# propagate to outside the block since y was defined before we opened
# the block.
end
defined? i # => nil, i is lost when you close the block
defined? x # => nil, x is also local to the block
puts y # => 'new value'

Resources