Is instantiating an array in condition a bad programming habit? - ruby

I often see this kind of condition :
if ([CONSTANT_1, CONSTANT_2, CONSTANT_3].contains? var && a_boolean) #...
instead of :
if ((var == CONSTANT_1 || var == CONSTANT_2 || var == CONSTANT_3) var && a_boolean) #...
I understant that the first condition is more elegant, but does creating an array only to check a condition is bad or does the resource used are negligible enough to be ignored ?

The former pattern does exert more memory pressure and CPU overhead since it has to allocate that array each time this code is run.
That said, this isn't necessarily a problem unless it's a particularly hot code path. If this is a method that is called frequently deep in a library somewhere, I'd probably elect for the second version to avoid the extra allocations, but for business code with a relatively low call rate, I think the readability of the first is acceptable.

The second version (like most complicated conditions) is error-prone. It's too easy to forget whether you need parentheses or how boolean logic works. The pattern of "is the variable one of these values and if so is this other thing true?" can be especially tricky, so it certainly makes sense to simplify.
However, I don't think the Array class includes contains?, does it? In my tests, I used include?:
CONSTANT_1 = 1
CONSTANT_2 = 2
CONSTANT_3 = 3.14
a_boolean = true
var = 3.14
puts "true" if [CONSTANT_1, CONSTANT_2, CONSTANT_3].include?(var) && a_boolean
Notice the parenthesis around var are necessary so that include? isn't searching for var && a_boolean instead of just var.
The only real problem I see with this idiom is that it would be a bit nicer to have the var come first. In SQL you might write something like:
var in (CONSTANT_1, CONSTANT_2, CONSTANT_3) and a_boolean
That seems just a little more natural to read.
If you are really worried about efficiency and you do this test many times, it might be better to set up a hash:
constant_test = {}
[CONSTANT_1, CONSTANT_2, CONSTANT_3].each do |k|
constant_test[k] = true
end
puts "true" if constant_test[var] && a_boolean
This has the advantage of being a hash lookup instead of a loop each time you test. (See also: this answer.)

Ideally, you wouldn't see either one of those. Instead, you'd use something like this as would be more idiomatic and more testable by breaking the conditional into small pieces of functionality.
I realize this example isn't quite the same if you're using, but it's close enough to show what I mean.
class Thing
def initialize(var)
#var = var
end
def a_method
return 'yes' if has_constaints? && boolean?
'no'
end
private
def has_constaints?
#var == CONSTANT_1 || #var == CONSTANT_2 || #var == CONSTANT_3
end
def boolean?
#var && a_boolean
end
end

Related

Loop method until it returns falsey

I was trying to make my bubble sort shorter and I came up with this
class Array
def bubble_sort!(&block)
block = Proc.new { |a, b| a <=> b } unless block_given?
sorted = each_index.each_cons(2).none? do |i, next_i|
if block.call(self[i], self[next_i]) == 1
self[i], self[next_i] = self[next_i], self[i]
end
end until sorted
self
end
def bubble_sort(&prc)
self.dup.bubble_sort!(&prc)
end
end
I don't particularly like the thing with sorted = --sort code-- until sorted.
I just want to run the each_index.each_cons(s).none? code until it returns true. It's a weird situation that I use until, but the condition is a code I want to run. Any way, my try seems awkward, and ruby usually has a nice concise way of putting things. Is there a better way to do this?
This is just my opinion
have you ever read the ruby source code of each and map to understand what they do?
No, because they have a clear task expressed from the method name and if you test them, they will take an object, some parameters and then return a value to you.
For example if I want to test the String method split()
s = "a new string"
s.split("new")
=> ["a ", " string"]
Do you know if .split() takes a block?
It is one of the core ruby methods, but to call it I don't pass a block 90% of the times, I can understand what it does from the name .split() and from the return value
Focus on the objects you are using, the task the methods should accomplish and their return values.
I read your code and I can not refactor it, I hardly can understand what the code does.
I decided to write down some points, with possibility to follow up:
1) do not use the proc for now, first get the Object Oriented code clean.
2) split bubble_sort! into several methods, each one with a clear task
def ordered_inverted! (bubble_sort!), def invert_values, maybe perform a invert_values until sorted, check if existing methods already perform this sorting functionality
3) write specs for those methods, tdd will push you to keep methods simple and easy to test
4) If those methods do not belong to the Array class, include them in the appropriate class, sometimes overly complicated methods are just performing simple String operations.
5) Reading books about refactoring may actually help more then trying to force the usage of proc and functional programming when not necessary.
After looking into it further I'm fairly sure the best solution is
loop do
break if condition
end
Either that or the way I have it in the question, but I think the loop do version is clearer.
Edit:
Ha, a couple weeks later after I settled for the loop do solution, I stumbled into a better one. You can just use a while or until loop with an empty block like this:
while condition; end
until condition; end
So the bubble sort example in the question can be written like this
class Array
def bubble_sort!(&block)
block = Proc.new { |a, b| a <=> b } unless block_given?
until (each_index.each_cons(2).none? do |i, next_i|
if block.call(self[i], self[next_i]) == 1
self[i], self[next_i] = self[next_i], self[i]
end
end); end
self
end
def bubble_sort(&prc)
self.dup.bubble_sort!(&prc)
end
end

Running method on two variables at once

I am wondering what the proper way is to refactor this code for efficiency besides running it twice.
class Hamming
def compute (a, b)
a.to_a.split("")
b.to_a.split("")
end
end
Is there something similar to assigning two variables at once like
a, b = 1, 2?
First off, your code is invalid. #to_a returns an array; #split is not defined on arrays.
Secondly, if your code was valid (say, a.to_s.split(""); b.to_s.split(""), it would not actually do much, because your code would just return the value of the last executed statement (b.to_s.split("")). Both #to_s and #split are non-destructive, which means they will not change a or b - the only effect you get from this function is what it returns, and you do not return the result of a.to_s.split("") in any way: it is forgotten.
If you meant something like this:
class Hamming
def compute(a, b)
[
a.to_s.split(""),
b.to_s.split("")
]
end
end
this is fairly readable. However, if you had more complex operation than just .to_s.split(""), it would be better to isolate it into its own function:
class Hamming
def compute(a, b)
[
list_chars(a),
list_chars(b)
]
end
private def list_chars(str)
str.to_s.split("")
end
end
You could simplify it even more using map, but it really only becomes necessary when you have multiple elements, as the two-element case is perfectly legible as-is. However, here goes:
class Hamming
def compute(a, b)
[a, b].map { |x| list_chars(x) }
end
private def list_chars(str)
str.to_s.split("")
end
end
Also, you might want to see the method #each_char, giving you an iterator, which is more readable, and often the more correct choice, than .split("").
EDIT: After thinking about it a bit, it seems like you're starting a method to evaluate a Hamming distance between two strings; and that you do not intend to have that function simply return the character of the two strings. In that case, I'd just write this:
def compute(a, b)
a_chars = a.to_s.each_char
b_chars = b.to_s.each_char
# ...
end
or possibly this, if you absolutely need to have characters themselves, and not an iterator:
def compute(a, b)
a_chars = a.to_s.each_char.to_a
b_chars = b.to_s.each_char.to_a
# ...
end
The solution I believe you are looking for would look like this:
def compute(a, b)
a_chars, b_chars = *[a, b].map { |x| x.to_s.each_char.to_a }
# ...
end
but I'd consider that less readable than the non-DRY one; if you really want to DRY it up, extract the listification into its own function as described above, and just do
a_chars = list_chars(a)
b_chars = list_chars(b)
which is actually the best of both worlds, even if it is a bit of an overkill in this case: it is DRY-ly maintainable and self-documentingly legible, for a bit of tradeoff in verbosity.
Since the code doesn't make sense, I think what you're asking is how do you avoid repeating yourself.
Simple, write another method and call that. Here's an example of wanting to find out which phrase is longer, but you want to ignore lots of whitespace. So foo bar isn't longer than 12345678.
def longer_phrase(phraseA, phraseB)
normalizedA = normalize(phraseA)
normalizedB = normalize(phraseB)
return normalizedA.length > normalizedB.length ? phraseA : phraseB
end
def normalize(phrase)
normalized = phrase.gsub(/\s+/, ' ');
normalized.strip!
return normalized
end
puts longer_phrase("foo bar ", "12345678")
Needing to normalize all your data before doing work on it comes up a lot. This avoids repeating yourself. It makes your code easier to understand, since we know what the point of all that work is, to normalize the string. And it gives you a normalization function to use elsewhere so you're normalizing your data the same way.

Optimizing basic method memoization with early return

When implementing basic memoization in Ruby, is there a pattern or simple way to return the memoized instance var if the value predicates on a more complex evaluation before hand?
Say the assignment of something requires an intense calculation, is Ruby smart enough to return the instance variable if it's present, or will something always be assigned within the scope of that method before setting #some_value?
def some_value
#some_value if #some_value.present? # possible?
something = something_else.try(:method_name) || another_something.method_name # prevent this from evaluating after execution
#some_value ||= MyClass.new(property: something.property)
end
What would a better memoization pattern be to implement what I have?
Based on how your code is currently written, the "intense calculation" will always occur. Ruby uses implicit return unless you explicitly use the keyword return, so, even if #some_value is present, the code will still execute to the last line.
def some_value
return #some_value if #some_value.present? # possible?
something = something_else.try(:method_name) || another_something.method_name # prevent this from evaluating after execution
#some_value ||= MyClass.new(property: something.property)
end
So, if you want to return #some_value if it is present, and not run any code afterwards, you will want to use explicit return. See above.
Now, Ruby will check if #some_value is present, and if that is true, the value is returned, otherwise, it will continue with the calculation.

Ruby for loop a trap?

In a discussion of Ruby loops, Niklas B. recently talked about for loop 'not introducing a new scope', as compared to each loop. I'd like to see some examples of how does one feel this.
O.K., I expand the question: Where else in Ruby do we see what apears do/end block delimiters, but there is actually no scope inside? Anything else apart from for ... do ... end?
O.K., One more expansion of the question, is there a way to write for loop with curly braces { block } ?
Let's illustrate the point by an example:
results = []
(1..3).each do |i|
results << lambda { i }
end
p results.map(&:call) # => [1,2,3]
Cool, this is what was expected. Now check the following:
results = []
for i in 1..3
results << lambda { i }
end
p results.map(&:call) # => [3,3,3]
Huh, what's going on? Believe me, these kinds of bugs are nasty to track down. Python or JS developers will know what I mean :)
That alone is a reason for me to avoid these loops like the plague, although there are more good arguments in favor of this position. As Ben pointed out correctly, using the proper method from Enumerable almost always leads to better code than using plain old, imperative for loops or the fancier Enumerable#each. For instance, the above example could also be concisely written as
lambdas = 1.upto(3).map { |i| lambda { i } }
p lambdas.map(&:call)
I expand the question: Where else in Ruby do we see what apears do/end block delimiters, but there is actually no scope inside? Anything else apart from for ... do ... end?
Every single one of the looping constructs can be used that way:
while true do
#...
end
until false do
# ...
end
On the other hand, we can write every one of these without the do (which is obviously preferrable):
for i in 1..3
end
while true
end
until false
end
One more expansion of the question, is there a way to write for loop with curly braces { block }
No, there is not. Also note that the term "block" has a special meaning in Ruby.
First, I'll explain why you wouldn't want to use for, and then explain why you might.
The main reason you wouldn't want to use for is that it's un-idiomatic. If you use each, you can easily replace that each with a map or a find or an each_with_index without a major change of your code. But there's no for_map or for_find or for_with_index.
Another reason is that if you create a variable within a block within each, and it hasn't been created before-hand, it'll only stay in existance for as long as that loop exists. Getting rid of variables once you have no use for them is a good thing.
Now I'll mention why you might want to use for. each creates a closure for each loop, and if you repeat that loop too many times, that loop can cause performance problems. In https://stackoverflow.com/a/10325493/38765 , I posted that using a while loop rather than a block made it slower.
RUN_COUNT = 10_000_000
FIRST_STRING = "Woooooha"
SECOND_STRING = "Woooooha"
def times_double_equal_sign
RUN_COUNT.times do |i|
FIRST_STRING == SECOND_STRING
end
end
def loop_double_equal_sign
i = 0
while i < RUN_COUNT
FIRST_STRING == SECOND_STRING
i += 1
end
end
times_double_equal_sign consistently took 2.4 seconds, while loop_double_equal_sign was consistently 0.2 to 0.3 seconds faster.
In https://stackoverflow.com/a/6475413/38765 , I found that executing an empty loop took 1.9 seconds, whereas executing an empty block took 5.7 seconds.
Know why you wouldn't want to use for, know why you would want to use for, and only use the latter when you need to. Unless you feel nostalgic for other languages. :)
Well, even blocks are not perfect in Ruby prior to 1.9. They don't always introduce new scope:
i = 0
results = []
(1..3).each do |i|
results << lambda { i }
end
i = 5
p results.map(&:call) # => [5,5,5]

Ruby case statement with multiple variables using an Array

I'd like to compare multiple variables for a case statement, and am currently thinking overriding the case equals operator (===) for Array is the best way to do it. Is this the best way?
Here is an example use case:
def deposit_apr deposit,apr
# deposit: can be nil or 2 length Array of [nil or Float, String]
# apr: can be nil or Float
case [deposit,apr]
when [[Float,String],Float]
puts "#{deposit[0]} #{deposit[1]}, #{apr*100.0}% APR"
when [[nil,String],Float]
puts "#{apr*100.0}% APR on deposits greater than 100 #{deposit[1]}"
when [[Float,String],nil]
puts "#{deposit[0]} #{deposit[1]}"
else
puts 'N/A'
end
end
The only problem is the Array case equals operator doesn't apply the case equal to the elements of the Array.
ruby-1.9.2-p0 > deposit_apr([656.00,'rupees'],0.065)
N/A
It will if I override, but am not sure what I'd be breaking if I did:
class Array
def ===(other)
result = true
self.zip(other) {|bp,ap| result &&= bp === ap}
result
end
end
Now, it all works:
ruby-1.9.2-p0 > deposit_apr([656.00,'rupees'],0.065)
656.0 rupees, 6.5% APR
Am I missing something?
I found this question because I was looking to run a case statement on multiple variables, but, going through the following, came to the conclusion that needing to compare multiple variables might suggest that a different approach is needed. (I went back to my own code with this conclusion, and found that even a Hash is helping me write code that is easier to understand.)
Gems today use "no monkey patching" as a selling point. Overriding an operator is probably not the right approach. Monkey patching is great for experimentation, but it's too easy for things to go awry.
Also, there's a lot of type-checking. In a language that is designed for Duck Typing, this clearly indicates the need for a different approach. For example, what happens if I pass in integer values instead of floats? We'd get an 'N/A', even though that's not likely what we're looking for.
You'll notice that the example given in the question is difficult to read. We should be able to find a way to represent this logic more clearly to the reader (and to the writer, when they revisit the code again in a few months and have to puzzle out what's going on).
And finally, since there are multiple numbers with associated logic, it seems like there's at least one value object-type class (Deposit) that wants to be written.
For cleanliness, I'm going to assume that a nil APR can be considered a 0.0% APR.
class Deposit
def initialize(amount, unit='USD', options={})
#amount = amount.to_f # `nil` => 0.0
#unit = unit.to_s # Example assumes unit is always present
#apr = options.fetch(:apr, 0.0).to_f # `apr: nil` => 0.0
end
end
Once we have our Deposit object, we can implement the print logic without needing case statements at all.
class Deposit
# ... lines omitted
def to_s
string = "#{#amount} #{#unit}"
string << ", #{#apr * 100.0}% APR" if #apr > 0.0
string
end
end
d = Deposit.new(656.00, 'rupees', apr: 0.065)
d.to_s
# => "656.0 rupees, 6.5% APR"
e = Deposit.new(100, 'USD', apr: nil)
e.to_s
# => "100.0 USD"
f = Deposit.new(100, 'USD')
f.to_s
# => "100.0 USD"
Conclusion: If you're comparing multiple variables in a case statement, use that as a smell to suggest a deeper design issue. Multiple-variable cases might indicate that there's an object that wants to be created.
If you are worried about breaking something by changing Array behavior, and certainly that's a reasonable worry, then just put your revised operator in a subclass of Array.
it's definitely not the best way. even more - you should not redefine methods of standart classes as core functionality may depend on it - have fun debugging then.
defensive style is nice(with lot of type checks and whatnot) but it usually hurts performance and readability.
if you know that you will not pass anything else than bunch of floats and strings to that method - why do you need all those checks for?
IMO use exception catching and fix the source of problem, don't try to fix the problem somewhere in the middle

Resources