use of ! in Ruby - ruby

I am new to ruby! And i am trying to learn the use of "!" .
I am aware that ! is included to so that the user's string is modified in-place; otherwise, Ruby will create a copy of user_input and modify that instead.
But in the following case for both the programs i am getting the same output.Why?
print "Please Enter your Input"
user_input = gets.chomp
user_input.downcase!
print "Please Enter your Input"
user_input = gets.chomp
user_input.downcase

In Ruby, bangs (!) are used to inform the programmer that the method they are calling is destructive. It's Ruby's way of saying "Hey! This method is going to change the object it is called on!". A number of safe methods in the String, Array,Enumerable`, etc classes have destructive counterparts.
Example:
my_str = "Hello, World!"
my_str.downcase # => "hello, world!"
my_str # => "Hello, World!"
my_str = "Goodbye, World!"
my_str.downcase! # => "goodbye, world!"
my_str #> "goodbye, world!"
As you can see, while both methods return the string's lower case variant, downcase! actually changes my_str permanently.
It's a very convenient aspect of Ruby that I wish more languages offered.
I think it's also worth mentioning that, because destructive methods work in-place, they are generally faster and more memory efficient than their safe counterparts who have to return new objects. Therefore, my_string.downcase! should be preferred to my_string = my_string.downcase whenever possible.

print "Please Enter your Input"
user_input = gets.chomp
user_input.downcase!
user_input value is what the user entered, in lowercase
print "Please Enter your Input"
user_input = gets.chomp
user_input.downcase
user_input value is what the user entered
The difference resides in the value of user_input, not in what gets printed.

Both methods behave the same, but the returned objects are different.
downcasereturns a modified copy of user_input. In other words, user_input stays the same.
downcase! returns user_input modified. Note that this can be more memory efficient, since you don't generate a copy of user_input.
In both cases, they return a downcase version of user_input. That's why you have the same output.
To learn more about bang methods in Ruby, see this blog post.
hth

Related

Can I recall the "case" in case?

I want to recall the case until user writes a or b. I do not want to use "case"
particularly.
I just want to get input from user but not geting something else. If he writes something else, he should need to write until he writes a or b.
str = gets.chomp.to_s
case str
when "a"
print "nice a"
when "b"
puts "nice b"
else
puts "please do it again"
end
class person
attr_accessor :name , :surname #and other attributes
end
#There will be a method here and it will run when the program is opened.
#The method will create the first object as soon as the program is opened.
#The new object that the user will enter will actually be the 2nd object.
puts "What do you want to do?
add
list
out"
process = gets.chomp.to_s
case process
when "add"
#in here user will add new objects of my class
when "list"
#in here user will show my objects
when "out"
puts "Have a nice day"
else
puts "please do it again"
end
In fact, if you look at it, many actions will be taken as a result of the user entering the correct input. what I want to tell is more detailed in this example. According to the input of the user, there will be actions such as calling methods, adding objects, etc.
I wrote most of the code on my computer. But still I couldn't solve my first problem.
Use Kernel#loop
There are a lot of ways to solve this problem, but let's start with a simple Kernel#loop wrapper around your existing code, as that's probably the easiest path forward for you.
loop do
str = gets.chomp.to_s
case str
when "a"
print "nice a"
when "b"
puts "nice b"
else
puts "please do it again"
# restart your loop when not "a" or "b"
next
end
# exit the loop if else clause wasn't triggered
break
end
Use until Control Expression
The loop construct above is pretty straightforward, but it requires you to think about where you need next and break statements for flow control. My own instinct would be to simply call a block until it's truthy. For example, the core logic could be shortened to:
str = nil; until str =~ /a|b/i do str = gets.chomp end; p str
This is a lot shorter, but it's not particularly user-friendly. To leverage this approach while making the solution more communicative and error-resistant, I'd refactor the original code this way:
# enable single-character input from console
require 'io/console'
# make sure you don't already have a value,
# especially in a REPL like irb
str = nil
until str =~ /a|b/ do
printf "\nLetter (a, b): "
str = STDIN.getch.downcase
end
puts "\nYou entered: #{str}"
While not much shorter than your original code, it handles more edge cases and avoids branching. It also seems less cluttered to me, but that's more a question of style. This approach and its semantic intent also seem more readable to me, but your mileage may legitimately vary.
See Also
IO::Console
Control Expressions
"I just want to do something until something else happens" is when you use some sort of while loop.
You can do this:
while true
str = gets.chomp
break unless str == 'a' || str == 'b'
puts "please do it again"
end
You can also use loop do:
loop do
str = gets.chomp
break unless ['a', 'b'].include?(str)
puts "please do it again"
end
puts "Nice #{str}."
Rubyists tend to prefer loop do over while true. They do pretty much the same thing.
One more thing. There's a simpler way to write out arrays of strings:
loop do
str = gets.chomp
break unless %w(a b).include?(str)
puts "please do it again"
end
puts "Nice #{str}."
It doesn't look a whole lot simpler, but if you have, say, 10 strings, it's definitely quicker to type in when you don't have to use all those quotation marks.
As your intuition was telling you, you don't need to use the case statement at all. Like trying to kill a flea with a sledgehammer. The most concise way to do your check is to check whether the input character is included in an array of the desired characters.

Different Messages For Different User Inputs

How do I put a message (string) for a specific answer (user input) and another message for another answer? For e.g.
puts "Did You Like My Program?"
feedback = gets
if feedback = "Yes"
puts "We're Glad!"
elsif feedback = "No"
puts "We Will Try To Improve!"
end
What should I change, add, or modify?
Your problem is that, when you compare, you have to use ==, not =.
When you input on command line, you always use Enter. It produces \n at the end of the string. So you need to remove it with chomp.
Also, to filter user input, I suggest this variant:
feedback = nil
until %w[y n].include?(feedback)
puts 'Did You Like My Program? Y/N'
feedback = gets.chomp.downcase
end
if feedback == 'y'
puts "We're Glad!"
else
puts "We Will Try To Improve!"
end
Brief explanation:
The code uses Array#include? and String#downcase.
%w[y n] is equal to ["y", "n"].
The until-loop executes the code while the condition is false.

I'm trying to design a simple Ruby calculator and I'm getting an error

So I've been messing around with Ruby for the first time after finishing the codecademy course up to "Object Oriented Programming, Part I" and I decided to start making a calculator. For some reason though, I get this error:
calc.rb:13:in `addition': undefined local variable or method `user_input' for main:Object (NameError)
from calc.rb:21:in `<main>'
I'm confused why it doesn't see my "user_input" array. Is it out of the scope of the method? Did I initialize it wrong?
Here's the code so you can see for yourself, it's obviously nothing sophisticated and it's not finished. I'm just trying to test for addition right now.
#!/usr/bin/env ruby
user_input = Array.new
puts "Would you like to [a]dd, [s]ubtract, [m]ultiply, or [d]ivide? "
type_of_math = gets.chomp
def addition
operator = :+
puts "Please enter the numbers you want to add (enter \"=\" to stop adding numbers): "
until gets.chomp == "="
user_input << gets.chomp.to_i
end
sum = user_input.inject(operator)
return sum
end
case type_of_math
when "a"
addition
when "s"
puts "Test for subtraction"
when "m"
puts "Test for multiplication"
when "d"
puts "Test for division"
else
puts "Wrong"
end
Consider this untested variation on your code. It's more idiomatic:
def addition
user_input = []
puts 'Please enter the numbers you want to add (enter "=" to stop adding numbers): '
loop do
input = gets.chomp
break if input == '='
user_input << input
end
user_input.map(&:to_i).inject(:+)
end
Notice that it puts user_input into the method. It also uses the normal [] direct assignment of an empty array to initialize it. Rather than chomp.to_i each value as it's entered it waits to do that until after the loop exits.
Instead of while loops, consider using loop do. They tend to be more easily seen when scanning code.
Also notice there's no return at the end of the method. Ruby automatically returns the last value seen.

Write a new file with defined function on Ruby

In Learn Ruby The Hard Way book on exercise 19, it says one should take the provided function:
def cheese_and_crackers(cheese_count, boxes_of_crackers)
puts "You have #{cheese_count} cheeses!"
puts "You have #{boxes_of_crackers} boxes of crackers!"
puts "Man that's enough for a party!"
puts "Get a blanket.\n"
end
and explore different approaches with it. I tried to explore the function's arguments and write a text with it:
file = ARGV.first
puts "Let's make a test?"
puts "Does the output file exist? #{File.exist?(arquivo)} "
puts "Ready, hit RETURN to continue, CTRL-C to abort."
$stdin.gets
def success(price, recipe)
puts """Text goes on like this:
In order to become a coder, You must dedicate yourself.\n
For that, you must pay a price, such as #{price}\n
Becoming a coder also requires #{recipe}\n"""
end
puts "What is the price to pay in order to become a coder?"
price = $stdin.gets
puts "What are the fundamental components in order to become a coder?"
recipe = $stdin.gets
coder = success(price, recipe)
motivational = File.open(file, 'w')
motivational.write(coder)
puts "Read this every day."
But I can't seem to make it write the function into a new test.txt file. The test.txt file comes out empty.
When calling puts, you're writing to stdout, but the return value will be empty.
Change this:
def success(price, recipe)
return """Text goes on like this:
In order to become a coder, You must dedicate yourself.\n
For that, you must pay a price, such as #{price}\n
Becoming a coder also requires #{recipe}\n"""
end
Then also:
motivational.close()
EDIT: Here's an extended explanation: when calling puts, you're just writing the string to stdout, which is the default output for programs. Methods (functions) can have a return value. For your success method, you need to return the string, so that you can write it in your file. If you call puts instead of return, the success method will not return any value, therefore leaving the file empty.
As for the close() call, it is advisable to close the file stream before your script ends.
Take a look at these references for more information:
https://www.ruby-lang.org/en/documentation/quickstart/2/
http://ruby-doc.org/core-2.2.3/IO.html

Getting the last line printed in Ruby

Is there any way to get the output of the last string sent to output? For instance:
puts "Hello"
puts _+" World"
Would return
Hello
Hello World
The assignment I'm working on involves minimizing code as much as possible. The above example is not the assignment, but if such a variable exists it would certainly help.
Thanks
** EDIT **
#gnibbler has the closest answer to what I'm looking for. This has nothing to do with spacing. I need to reuse the data output on the previous line, not append to it. Another example would be:
puts "foobar" // foobar
puts _.reverse // raboof
Yeah its possible. You need to override the Kernel::puts method likes this
module Kernel
alias_method :old_puts, :puts
def puts arg
old_puts arg
$_=arg # $_ is a global variable, holds the last printed item
end
end
and use it like
>> puts "sample"
=> "sample"
>> _
=> "sample"
>> _.reverse
=> "elpmas"
_ will always holds the last printed value
it means
>> puts "hello"
=> "hello"
>> puts _ + " word"
=> "hello word"
>> _
=> "hello word"
Is there a reason that you want to do this?maybe there is other solutions. If you really wants to do, you can do like this:
module Kernel
alias_method :puts_with_save, :puts
def puts_with_save arg_
puts arg_
$LastPuts = arg_
end
end
puts_with_save "hello"
puts_with_save $LastPuts + " shanison"
This should work as you expected. But I introduce a global $LastPuts to your program.
There is no special variable for that
apart from patching puts (which will conflict with your requirement to minimise the code), you could use a pattern like this
puts w="Hello"
puts w=(w+" World")
puts w=(w.reverse)
etc.
The only thing I can think of is that if you knew you were using puts you could patch it with your own version to give it a "memory" to suit your objective. However, it sounds like in your case you may as well use a separate mechanism rather than overriding the default behavior of puts.

Resources