Error when chaining with chomp - ruby

I wrote a small program as the following
print "Your string = "
input = gets.chomp.downcase!
if input.include? "s"
puts "Go yourself!"
end
But I got the error
undefined method `include?' for nil:NilClass (NoMethodError)
if I delete the exclamation mark (!) after downcase, the program runs properly.
I don't understand the reason.

String#downcase! will give you nil, if the string is already in down-cased. So use String#downcase, it is safe. I am sure, you passed from the command line to the method gets, a string which is already down-cased. Replace the line input = gets.chomp.downcase! with input = gets.chomp.downcase. Now you are safe.
String#downcase
Returns a copy of str with all uppercase letters replaced with their lowercase counterparts. If the receiver string object is already, downcased, then the receiver will be returned.
String#downcase!
Downcases the contents of str, returning nil if no changes were made.
One example to demonstrate this -
>> s = "abc"
>> p s.downcase
"abc"
>> p s.downcase!
nil
Now nil is an instance of the class NilClass, which has no instance method called #include?. So you got no method error. This is obvious.
>> nil.respond_to?(:downcase)
false
>> nil.respond_to?(:downcase!)
false
>> s.respond_to?(:downcase!)
true
>> s.respond_to?(:downcase)
true

Do not use downcase! as it can return nil if no changes have been made to the string.
Therefore, the correct code will be:
print "Your string = "
input = gets.chomp.downcase
if input.include? "s"
puts "Go yourself!"
end

Related

Calling multiple methods on a variable

So I have the following code:
print "input please: "
user_input = gets.chomp.downcase!
if user_input.include? "s"
user_input.gsub!(/s/, "th")
else
puts "There is no S in your input"
end
which throws me an error regarding the include method
when I run this it works:
print "input please "
user_input = gets.chomp
user_input.downcase!
if user_input.include? "s"
user_input.gsub!(/s/, "th")
else
puts "There is no S in your input"
end
is it not possible to call multiple methods on a variable?
It is permissible (and recommended!) to chain methods in Ruby as you have done with gets.chomp.downcase!. However, using downcase! as opposed to the form downcase is causing an unexpected behavior in your code. According to the docs, the downcase! form
Downcases the contents of str, returning nil if no changes were made.
So if your input does not contain any upper case letters, downcase! returns nil and that causes an error down the line when you call .include? on it. Try it out with input like this string:
input please: No letter in here!
# prints
There is no S in your input
But the same called without the upper N errors:
input please: no letter in here!
Traceback (most recent call last):
test.rb:4:in `<main>': undefined method `include?' for nil:NilClass (NoMethodError)
If you supply input that does contain upper case characters, you'll get no error with your original code. The fix for this is to use the non-! form:
# Don't use the ! form of downcase
user_input = gets.chomp.downcase
Because the downcase! form is intended to modify a variable rather than modify a transient string such as the string returned by .chomp.
I suspect you intended to puts user_input there also, following your .gsub! call. Your string replacement s to th does work correctly if you add that in.

Why is the .to_s method needed in Crystal when converting user input to an integer?

I'm getting started with Crystal, and I've run into something I don't understand. I've written a simple program to demonstrate, which takes a number from the console and adds one.
Ruby
# Add one program.
puts "Enter a number."
number = gets
number = number.to_i
puts "You entered #{number}. #{number} + 1 = #{number + 1}"
Crystal
# Add one program.
puts "Enter a number."
number = gets
number = number.to_s.to_i # Why is to_s needed?
puts "You entered #{number}. #{number} + 1 = #{number + 1}"
As you can see, the programs are nearly identical, however, in crystal I must take the input from the console and convert it to a string before it can be converted into an integer.
What I want to know is:
What is being returned by gets in crystal?
Is there another way to do this without chaining methods?
This may seem like a basic question, but it's still early days for crystal, and documentation is sparse.
Error
Error in example.cr:6: undefined method 'to_i' for Nil (compile-time type is (String | Nil)) (did you mean 'to_s'?)
number = number.to_i # Why is to_s needed?
^~~~
================================================================================
Nil trace:
example.cr:4
number = gets
^~~~~~
example.cr:4
number = gets
^~~~
/usr/share/crystal/src/kernel.cr:105
def gets(*args, **options)
/usr/share/crystal/src/kernel.cr:105
def gets(*args, **options)
^
/usr/share/crystal/src/kernel.cr:106
STDIN.gets(*args, **options)
^~~~
/usr/share/crystal/src/io.cr:574
def gets(chomp = true) : String?
/usr/share/crystal/src/io.cr:574
def gets(chomp = true) : String?
^
/usr/share/crystal/src/io.cr:574
def gets(chomp = true) : String?
/usr/share/crystal/src/io.cr:574
def gets(chomp = true) : String?
^~~~
/usr/share/crystal/src/io.cr:575
gets '\n', chomp: chomp
^~~~
/usr/share/crystal/src/io.cr:604
def gets(delimiter : Char, chomp = false) : String?
^~~~
/usr/share/crystal/src/io.cr:605
gets delimiter, Int32::MAX, chomp: chomp
^~~~
/usr/share/crystal/src/io.cr:618
def gets(delimiter : Char, limit : Int, chomp = false) : String?
^~~~
/usr/share/crystal/src/io.cr:619
raise ArgumentError.new "Negative limit" if limit < 0
^
/usr/share/crystal/src/io.cr:632
if ascii && !decoder && (peek = self.peek)
^
/usr/share/crystal/src/io.cr:633
if peek.empty?
^
/usr/share/crystal/src/io.cr:634
nil
^
In most cases gets will return a String, but it is possible that it returns nil too.
This isn't an issue in Ruby, because in your example you will ever have nil returned at runtime and even if you had, there is NilClass#to_i in Ruby which always returns 0.
But the Crystal compiler checks object types upfront and therefore makes sure that your code can handle all possible return types. Unfortunately, in Crystal, there is no to_i method on Nil yet and therefore you get the compiler error:
undefined method 'to_i' for Nil (compile-time type is (String | Nil))
When running either program, try giving Ctrl+D (EOF) as input. Is the program behaving as you expected?
Crystal is protecting you against these kinds of programming mistakes by making you handle all the possible types a method may return. A more correct version of the program could for example be:
print "Enter a number: "
number = gets.try &.to_i?
if number
puts "You entered #{number}. #{number} + 1 = #{number + 1}"
else
puts "Please enter a valid number"
end
Note: String#to_i? returns nil when the conversion fails, rather than raising an exception as String#to_i does.

include works with if but not else

I doing a Ruby botcamp. I'm supposed to write code that replaces all user input of 's' with 'th' so it reads like Daffy Duck is speaking. If I enter an s it will be replaced with th. That works! But If I don't enter an 's' it's supposed to print that none were included in my elsif statemnt. Instead I'm getting the error 'undefined method `include?' for nil:NilClass'. Other than that error, the interpretor is telling me the code is good.
print "Input a string: "
user_input=gets.chomp.downcase!
if user_input.include?"s"
user_input.gsub!(/s/, "th")
puts "Your string is #{user_input}!"
elsif
puts "There are no s's in your string!"
end
Any ideas on what I need to change?
You need to be careful with built-in ruby methods that end with an exclamation point (!). A lot of them will return nil if no changes were made:
'test'.downcase! # => nil
'Test'.downcase! # => "test"
Since you are assigning the result to a variable, there's no need to use the exclamation point method, since those modify in-place, you can just use the normal downcase method.
'test'.downcase # => "test"
You also later on have an elsif with no condition, that should probably just be an else. It's actually executing the first line of the "body" of the elsif as the conditional:
if false
puts "a"
elsif
puts "b" # recall that `puts` returns `nil`
puts "c"
else
puts "d"
end
This results in
b
d
being output

Ruby puts command in modules returning nil

i am working though LearnTocodethehardway.com http://ruby.learncodethehardway.org/book/ex25.html
On ex25. In the example there is a module that has a bunch of methods that return or print values. The Print_last_word method when supplied with an array of strings just puts nil. it does this even in his example output. My question would then be why?
To be precise, it doesn't puts nil - it puts the last word and returns nil. Here's the example output:
>> Ex25.print_last_word(words)
wait. # <- this is the output
=> nil # <- this is the return value
puts always returns nil.
UPDATE
There seems to be a bug in print_first_word:
module Ex25
def Ex25.print_first_word(words)
word = words.pop(0)
puts word
end
end
Ex25.print_first_word(["foo", "bar", "baz"])
#=> nil
This is because ["foo", "bar", "baz"].pop(0) returns an empty array and puts [] just returns nil without printing anything.
A working implementation (in the exercise's style) could look like this:
module Ex25
def Ex25.print_first_word(words)
word = words.shift
puts word
end
end
To make it more easy to understand:
"puts" never is used for its return value. It is used for its side effect if you wanted a method that would return a word, you would then do something with that word.
words = ["apple", "cucumber", "apple"]
def give_me_first_word(array_of_words)
array_of_words.first
end
variable_to_be_used_later = give_me_first_word(words)
This function would return "apple"(and in this case the variable would be assigned "apple"), but it would puts nothing to the screen. This value would then be used in another program or function.
If you puts a value, you would then not need to return it to any other program as it's already served its purpose. It's actually intuitive because if puts also returned the value, it would be doing two things. The counterpart to "puts" would be "return" that simply returns the value and does not display it to the screen.

Why does Kernel#p duplicate my text on standard output?

Look at this code :
def hello
p "Hey!"
end
p hello
the output will be:
"Hey!"
"Hey!"
=> "Hey!"
And so here is my conclusion: puts itself returns the text which is going to be sent in output in Ruby code, else it wouldn't print "Hey!" again. What is happening while printing the string? If puts doesn't send it to standard output directly, who is responsible for it and how?
All Methods Return a Value
In Ruby, almost everything returns a value, even if that value is nil. However, in your case the issue is that Kernel#p and Kernel#puts differ in the values they return.
def hello
# Print string literal, then return
# the printed object.
p "Hey!"
end
# Print the return value of main#hello.
p hello
As a result, the string gets printed once inside the method, and then the method's return value is passed to Kernel#p and printed again. This is by design.
Use Kernel#puts to Avoid Duplicated Output
def hello
# Print string; return nil.
puts "Hey!"
end
# Calls main#hello, but prints nil (blank line).
puts hello
This will result in the string literal being printed inside the method, and then a blank line printed since the return value from the method is nil.
Hey!
=> nil
The Right Way
If you want to avoid the blank line, avoid sending to standard output more than once. For example:
def hello
'Hey!'
end
p hello
If the p method returns the string it's given, then hello would return that as well, which means that the secondary p call would repeat it.
This is probably why puts returns nil by default.

Resources