In this example, the last english in the method definition seems to be unnecessary to me. I took it out and the code worked just as fine. Why does the book include it? Is there some sort of convention I am unaware of?
class Integer
def to_eng
if self == 5
english = 'five'
else
english = 'fifty-eight'
end
english
end
end
# I'd better test on a couple of numbers...
puts 5.to_eng
puts 58.to_eng
In ruby, the result of the last statement is always returned from the function. In this case the result is the value of the 'english' variable. However if we remove the last line of the method it still behaves the same because both lines returns the value that is set on 'english' variable.
english = 'five' #=> 'five'
english = 'fifty-eight' #=> 'fifty-eight'
Also this method could be written in more compact and elegant way using a ternary operator.
class Integer
def to_eng
self == 5 ? 'five' : 'fifty-eight'
end
end
The best reason for me is debuggability. In the showcased code, you can insert a debug statement just before the return, and know the result:
class Integer
def to_eng
if self == 5
english = 'five'
else
english = 'fifty-eight'
end
debugger
# or: puts "ENGLISH IS #{english}"
# or: binding.pry
# or whatever else you want
english
end
end
You can't do that if you leave off the last english, because the return value would change.
It ensures that the value in local variable 'english' is returned. The last executed statement in a method is what's returned.
In this case, it's not needed, but if (for example) the method was rewritten as...
def to_eng
english = 'fifty-eight'
english = 'five' if self == 5
end
... you would find that nothing gets returned for any number other than 5 because the last statement... not being executed... would just return nil.
Related
I write the method capitalized(word) to be funneled into the .each function.
i use bang! to make the function work correctly but i don't understand why its working correctly.
The part that is really throwing me off is the !capitalized word line if statement. I don't understand the logic and how the method returns the answers that it does.
!false returns false in the method... why? And !true it returns true.
Whats the best way to understand this concept.
I have tried taking out the bang and putting it back in to see its effect.
And it just confuses me. Thank you for your help.
# A name is valid is if satisfies all of the following:
# - contains at least a first name and last name, separated by spaces
# - each part of the name should be capitalized
#
# Hint: use str.upcase or str.downcase
# "a".upcase # => "A"
def is_valid_name(str)
name = str.split(' ')
if name.length < 2
return false
end
name.each do |word|
if !capitalized(word)
return false
end
end
return true
end
def capitalized(word)
if word[0] == word[0].upcase && word[1..-1]== word[1..-1].downcase
return true
else
return false
end
end
puts is_valid_name("Kush Patel") # => true
puts is_valid_name("Daniel") # => false
puts is_valid_name("Robert Downey Jr") # => true
puts is_valid_name("ROBERT DOWNEY JR") # => false
The purpose of the part of the code you struggling with, is to check whether all of the elements of the name are capitalised. There are two ways you can approach that problem:
Find any element that is not capitalised.
If such element exists, that means name is not valid, so method is_valid_name should return false. This is the approach used in you code:
def is_valid_name(str)
#...
name.each do |word| #1
if !capitalized(word) #2
return false #3
end
end
return true #4
end
Iterate over every part of the name
Check if word is capitalized. We want to break only when we find invalid (not capitalized) word. That occurs when capitalized(word) returns false: so condition if !capitalized(word) is actually if !false, which is equal to if true.
We entered, that means our word is not valid, so we return false from is_valid_name method.
Check if all elements are capitalized
This is more straightforward solution. We just want to check, if method capitalized(word) returns true for all of the elements of the name.
To achieve that, we can use method all?, which returns true if condition in the block returns true for every element; otherwise false. So we can replace all the code above with single line:
name.all?{|word| capitalized(word) }
Final implementation of the validation method can look like that:
def is_valid_name(str)
name = str.split(' ')
name.length < 2 && name.all?{|word| capitalized(word)}
end
Hope it helps you!
!false returns false in the method... why?
I'm assuming you mean this one:
if !capitalized(word)
return false
end
"If capitalize returns false, why do we return false", this question? Because if a word is not capitalized, the name is invalid. That's the given business logic.
I am learning methods in Ruby and thought that the best way to learn them was to create a method that already exists. However, there are two problems that I am running in to:
I do not know what the capitalize method looks like
My solution (it does more than the original method does) seems like it can be refactored into something more elegant.
This is what I have come up with:
# method that capitalizes a word
def new_capitalize(string)
if string[0].downcase == "m" && string[1].downcase == "c"
puts "#{string[0].upcase}#{string[1].downcase}#{string[2].upcase}#{string[3..-1].downcase}"
else
puts "#{string[0].upcase}#{string[1..-1].downcase}"
end
end
name1 = "ryan"
name2 = "jane"
new_capitalize(name1) # prints "Ryan"
new_capitalize(name2) # prints "Jane"
str = "mCnealy"
puts str.capitalize
# prints "Mcnealy"
new_capitalize(str)
# prints "McNealy"
It seems as if the first part of my if statement could be made much more efficient. It does not need to be even close to my solution as long as it prints the second capital if the name begins with "mc"
Also, if someone could point me to where the built in capitalize method's code could be found that would be great too!
Thank you in advance!
Alright, how about:
module NameRules
refine String do
def capitalize
if self[0..1].downcase == 'mc'
"Mc#{self[2..-1].capitalize}"
else
super
end
end
end
end
Then to use it:
class ThingWithNames
using NameRules
def self.test(string)
string.capitalize
end
end
ThingWithNames.test('mclemon') # => "McLemon"
ThingWithNames.test('lemon') # => "Lemon"
If we were starting from scratch and not using the C implemented code:
module NameRules
refine String do
def capitalize
if self[0..1].downcase == 'mc'
"Mc#{self[2..-1].capitalize}"
else
new_string = self.downcase
new_string[0] = new_string[0].upcase
new_string
end
end
end
end
Reference materials:
String#capitalize source
A really good presentation on refinements
First, in my opinion, doing anything other than capitalizing the first letter of the string should be a different method or an optional arg you pass. Second, if you are trying to mimic the core lib behavior than you could monkey-patch String.
class String
def capitalize
self[0].upcase << self[1..-1].downcase
end
end
The closest to an official ruby implementation is probably Rubinius
https://github.com/rubinius/rubinius/blob/377d5c958bc8239514fb98701b75859c6b51b9d4/core/string.rb#L332
I need to write a ruby method that reverses a string only if it has less than four characters.
# Write a method that reverses
# a string ONLY if it's shorter than
# 4 letters.
# Otherwise, the string is
# returned as-is.
# (Hint: strings have
# a built-in .length method!)
# conditional_reverse("yo")
# => "oy"
# conditional_reverse("hello")
# => "hello"
Here is the code I came up with.
def conditional_reverse(string)
good = string.length
if good < 4
puts string.reverse
else
puts string
end
puts conditional_reverse("cat")
end
When I run it in repl I get the following response
:conditional_reverse
I have no idea what i'm doing wrong.
just put puts conditional_reverse("cat") out side our def
def conditional_reverse(string)
good = string.length
if good < 4
puts string.reverse
else
puts string
end
end
conditional_reverse("cat")
You are callind your method in its definition. Avoid it if you are not writing a recursive method.
def conditional_reverse(s)
s.length < 4 ? s.reverse : s
end
The answer provided by #Ursus is perfect, but in case you want to go with your way the change you have to do is this;
def conditional_reverse(string)
good = string.length
if good < 4
puts string.reverse
else
puts string
end
end
puts conditional_reverse("cat")
What the others said, plus...
You get that response from irb because in recent versions of Ruby, a method definition returns the method name as a symbol.
Also, your problem specifies that the string should be reversed, not that the string should be output reversed; so you should remove the puts calls and just manipulate the string.
For the benefit of your readers, I recommend being specific with your names. good = string.length could be changed to needs_reversing = string.length < 4, for example.
I am trying to write fast and concise code. I'd appreciate your thoughts on which is the best way to write the following code and why:
Option #1
def get_title
title = check_in_place_one
if title.empty?
title = check_in_place_two
if title.empty?
title = check_in_place_three
end
end
return title
end
Option #2
def get_title
title = check_in_place_one
title = check_in_place_two unless !title.empty?
title = check_in_place_three unless !title.empty?
return title
end
I think Option #1 is better since if the title is found by check_in_place_one, we test title.empty? once and then skip the rest of the code in the method and return. But, it looks too long. Option #2 appears better, but processes title.empty? one extra time, and unnecessary time before returning. Also, am I missing a third option?
From performance, there is no difference between the two versions of your code (besides very minor difference that may come from parsing, which should be ignorable). The control structures are the same.
From readability, if you can get away with nesting, doing so is better. Your second option is better.
It is usually better to get rid of any case that does not need further processing. That is done by return.
def get_title
title = check_in_place_one
return title unless title.empty?
title = check_in_place_two
return title unless title.empty?
title = check_in_place_three
return title
end
The last title = and return in the code above are redundant, but I put them there for consistency, which improves readability.
You can further compact the code using tap like this:
def get_title
check_in_place_one.tap{|s| return s unless s.empty?}
check_in_place_two.tap{|s| return s unless s.empty?}
check_in_place_three
end
tap is a pretty much fast method, and unlike instance_eval, its performance penalty is usually ignorable.
The following approach could be used for any number of sequential tests. Moreover, it is completely general. The return condition could be changed, arguments could easily be assigned to the test methods, etc.
tests = %w[check_in_place_one check_in_place_two check_in_place_three]
def do_tests(tests)
title = nil # Define title outside block
tests.each do |t|
title = send(t)
break unless title.empty?
end
title
end
Let's try it:
def check_in_place_one
puts "check 1"
[]
end
def check_in_place_two
puts "check 2"
''
end
def check_in_place_three
puts "check 3"
[3]
end
do_tests(tests) #=> [3]
check 1
check 2
check 3
#=> [3]
Now change one of the tests:
def check_in_place_two
puts "check 2"
'cat'
end
do_tests(tests) #=> 'cat'
check 1
check 2
#=> "cat"
If there were more tests, it might be convenient to put them in a module which would be included into a class. Mixed-in methods behave the same as those that you define for the class. For example, they have access to instance variables. I will demonstrate that with the definition of the first test method. We probably want to make the test methods private. We could do it like this:
module TestMethods
private
def check_in_place_one
puts "#pet => #{#pet}"
puts "check 1"
[]
end
def check_in_place_two
puts "check 2"
''
end
def check_in_place_three
puts "check 3"
[3]
end
end
class MyClass
##tests = TestMethods.private_instance_methods(false)
puts "##tests = #{##tests}"
def initialize
#pet = 'dog'
end
def do_tests
title = nil # Define title outside block
##tests.each do |t|
title = send(t)
break unless title.empty?
end
title
end
include TestMethods
end
The following is displayed when the code is parsed:
##tests = [:check_in_place_one, :check_in_place_two, :check_in_place_three]
Now we perform the tests:
MyClass.new.do_tests #=> [3]
#pet => dog
check 1
check 2
check 3
Confirm the test methods are private:
MyClass.new.check_in_place_one
#=> private method 'check_in_place_one' called for...'
The advantage of using a module is that you can add, delete, rearrange and rename the test methods without making any changes to the class.
Well, here's a few other alternatives.
Option 1: Return first non-empty check.
def get_title
return check_in_place_one unless check_in_place_one.empty?
return check_in_place_two unless check_in_place_two.empty?
return check_in_place_three
end
Option 2: Helper method with short-circuit evaluation.
def get_title
check_place("one") || check_place("two") || check_place("three")
end
private
def check_place(place)
result = send("check_in_place_#{place}")
result.empty? ? nil : result
end
Option 3: Check all places then find the first that's non-empty.
def get_title
[
check_in_place_one,
check_in_place_two,
check_in_place_three,
].find{|x| !x.empty? }
end
Option 2 looks good although you did a 360 degree turn with the unless !title.empty?. You can shorten that to if title.empty? since unless is equivalent to an if ! so doing an unless ! takes you back to just if.
If you're only ever going to have 3 places to look in then option 2 is the best. It's short, concise, and easy to read (easier once you fix the aforementioned whoopsie). If you might add on to the places you look for a title in you can get a bit more dynamic:
def get_title(num_places = 4)
title, cur_place = nil, 0
title = check_in_place(cur_place += 1) while title.nil? && cur_place < num_places
end
def check_in_place(place_num)
# some code to check in the place # given by place_num
end
The tricky line is that one with the while in it. What's happening is that the while will check the expression title.nil? && cur_place < num_places and return true because the title is still nil and 0 is less than 4.
Then we'll call the check_in_place method and pass in a value of 1 because the cur_place += 1 expression will increment its value to 1 and then return it, giving it to the method (assuming we want to start checking in place 1, of course).
This will repeat until either check_in_place returns a non nil value, or we run out of places to check.
Now the get_title method is shorter and will automatically support checking in num_places places given that your check_in_place method can also look in more places.
One more thing, you might like to give https://codereview.stackexchange.com/ a look, this question seems like it'd be a good fit for it.
I don't think there's any reason to get too clever:
def get_title
check_in_place_one || check_in_place_two || check_in_place_three
end
Edit: if the check_in_place_X methods are indeed returning an empty string on failure it would be better (and more idiomatic) to have them instead return nil. Not only does it allow for truthy comparisons like the above code, return "" results in the construction of a new and unnecessary String object.
I'm attempting to add conversion methods to the Numeric class but when I run the following lines of code I get a SystemStackError
puts 5.dollars.in(:euros) # => 6.5
puts 1.dollar.in(:yen)
Here is my Numeric class
class Numeric
##conversion_hash = {:dollar => {:yen => 0.013, :euros => 1.292, :rupees => 0.019}}
def method_missing(method_id)
name = method_id.to_s
if name =~ /^dollar|yen|euros|rupee|$/
self.send(name + 's')
else
super # pass the buck to superclass
end
end
def dollars()
puts "Called Dollars method"
#current_currency = :dollar
return self
end
def in(key)
if ##conversion_hash.has_key?(#current_currency)
puts "Current currency: #{#current_currency}"
conversion_rate = ##conversion_hash[#current_currency]
puts "Current conversion rate: #{conversion_rate}"
if conversion_rate.has_key?(key)
puts "we have that key"
puts"What am I? #{self}"
rate = conversion_rate[key]
puts "Rate to multiply by #{rate}"
return self.to_int * conversion_rate[key]
end
end
end
end
Any help is greatly appreciated.
You're getting infinite recursion in your method_missing because your regex isn't quite right. Try changing the line:
if name =~ /^dollar|yen|euros|rupee|$/
to:
if name =~ /^dollar|yen|euros|rupee$/
That extra | is causing anything to match the regex, so any other method is recursing with a continually extending suffix of s.
In this case it looks like puts seems to be trying to call to_ary when it's trying to determine the type its argument. I'm not exactly sure why it's not just using respond_to? though - it's deep in the C internals so I don't really know what's happening.
Your solution overcomplicated.
- You don't need to modify method_missing. Armando version works fine.
- You should simple dollar definition to hash plus
- find the way to call method_missing again from method in(this is your homework).
Working solution have only 1 line of code + 2 lines def surronding.