Read input from console in Ruby? - ruby

I want to write a simple A+B program in ruby, but I have no idea how to work with the console.

Are you talking about gets?
puts "Enter A"
a = gets.chomp
puts "Enter B"
b = gets.chomp
c = a.to_i + b.to_i
puts c
Something like that?
Update
Kernel.gets tries to read the params found in ARGV and only asks to console if not ARGV found. To force to read from console even if ARGV is not empty use STDIN.gets

you can also pass the parameters through the command line. Command line arguments are stores in the array ARGV. so ARGV[0] is the first number and ARGV[1] the second number
#!/usr/bin/ruby
first_number = ARGV[0].to_i
second_number = ARGV[1].to_i
puts first_number + second_number
and you call it like this
% ./plus.rb 5 6
==> 11

There are many ways to take input from the users. I personally like
using the method gets. When you use gets, it gets the string
that you typed, and that includes the ENTER key that you pressed
to end your input.
name = gets
"mukesh\n"
You can see this in irb; type this and you will see the \n, which is the “newline” character that the ENTER key produces:
Type name = gets you will see somethings like "mukesh\n"
You can get rid of pesky newline character using chomp method.
The chomp method gives you back the string, but without the terminating newline. Beautiful chomp method life saviour.
name = gets.chomp
"mukesh"
You can also use terminal to read the input. ARGV is a constant defined in the Object class. It is an instance of the Array class and has access to all the array methods. Since it’s an array, even though it’s a constant, its elements can be modified and cleared with no trouble. By default, Ruby captures all the command line arguments passed to a Ruby program (split by spaces) when the command-line binary is invoked and stores them as strings in the ARGV array.
When written inside your Ruby program, ARGV will take take a command line command that looks like this:
test.rb hi my name is mukesh
and create an array that looks like this:
["hi", "my", "name", "is", "mukesh"]
But, if I want to passed limited input then we can use something like this.
test.rb 12 23
and use those input like this in your program:
a = ARGV[0]
b = ARGV[1]

if you want to hold the arguments from Terminal, try the following code:
A = ARGV[0].to_i
B = ARGV[1].to_i
puts "#{A} + #{B} = #{A + B}"

If you want to make interactive console:
#!/usr/bin/env ruby
require "readline"
addends = []
while addend_string = Readline.readline("> ", true)
addends << addend_string.to_i
puts "#{addends.join(' + ')} = #{addends.sum}"
end
Usage (assuming you put above snippet into summator file in current directory):
chmod +x summator
./summator
> 1
1 = 1
> 2
1 + 2 = 3
Use Ctrl + D to exit

Related

Making a sorted array of user's input

I'm learning Ruby with 'Learn to Program' by Chris Pine. On chapter 10 I should write a program where the user types as many words as he like and when he's done, he can just press Enter on an empty line and exit.
I came up with this:
puts "Type whatever you want!"
index = 0
word = ''
array = []
while word != nil
word << gets.chomp
array[index] = word
index = index + 1
end
puts ''
puts array.sort
But that doesn't work. What did I miss? Is there another way I could define word without having to repeat it?
The word will not have nil value. It will be an empty string. So you need to check for that:
while word != ""
# or even better
while !word.empty?
Also, you are adding everything to your word. You probably want to assign to it instead:
word = gets.chomp
Per author's comment:
begin
# your code here
end while !word.empty?
# OR more readable
begin
# your code here
end until word.empty?
It seems like there's a simpler solution, if I'm reading the question correctly.
You could do something like this:
user_input = gets.chomp.split(" ").sort
ex)
input: bananas clementine zebra tree house plane mine
output: ["bananas", "clementine", "house", "mine", "plane", "tree", "zebra"]
Here's a simple loop that you could do just for kicks:
arr = []
arr << $_.strip until gets =~ /^\s*$/
puts arr.sort
$_ is a special variable that evaluates to the last input read from STDIN. So basically this reads "Call gets and check if the input is just spaces. If it is then break out of the loop, otherwise append the last input with whitespace removed value onto the array and continue looping."
Or even more fun, a one liner:
puts [].tap {|arr| arr << $_.strip until gets =~ /^\s*$/}.sort
Basically same thing as above except using tap to initialize the variable.
To answer your questions:
Is there another way I could define word without having to repeat it?
Use side effects of assignment. In ruby when you assign a variable the return value of that assignment is the assigned variable, as in:
irb(main):001:0> (variable = 2) == 2
=> true
The idea would be to put the assignment in the your conditional. If I were to write something like this in a comprehensible loop, as opposed to those above, I'd write something like this:
arr = []
while !(word = gets.strip).empty?
arr << word
end
puts arr.sort
Using loop might simplify the code:
a = []
loop do
input = gets.chomp
if input.empty?
break
else
a << input
end
end
a.sort!
puts a

Using gets() gives "No such file or directory" error when I pass arguments to my script

Hi I am making a simple ruby script practiced where I make a form using gets.chomp and arguments, the problem is that when gets.chomp use the script returns me an error when I apply the argument test.
The code:
#!usr/bin/ruby
def formulario(quien)
while (1)
print "[+] Word : "
word = gets.chomp
print quien + " -> " + word
end
end
quien = ARGV[0]
formulario(quien)
The error:
[+] Word : C:/Users/test/test.rb:8:in `gets': No such file or directory # rb_sysopen - test (Errno::E
NOENT)
from C:/Users/test/test.rb:8:in `gets'
from C:/Users/test/test.rb:8:in `formulario'
from C:/Users/test/test.rb:17:in `<main>'
Can anyone help?
It looks like you want to the user to type some input by reading a line from STDIN, the best way to do this is by calling STDIN.gets and not gets. So your line becomes:
word = STDIN.gets.chomp
This is documented as IO.gets. STDIN is an instance of IO.
Right now, you're executing Kernel.gets, which does something different (emphasis mine):
Returns (and assigns to $_) the next line from the list of files in ARGV (or $*), or from standard input if no files are present on the command line.
This appears to behave like STDIN.gets if ARGV is empty, but is not the same thing, hence the confusion.
If your program handle empty argument nor non-empty argument. You can use this module (especially if you already use default gets everywhere)
# A module that help to use method gets no matter what the file argument is
module InputHelper
# Handle input method based on its file argument
def gets
if ARGV.nil?
Kernel.gets
else
STDIN.gets
end
end
end
and then you could include it on your class
require 'input_helper'
class YourClass
...
include InputHelper
...
end
I ran into this issue today in Ruby 3.1.2. I can also confirm that STDIN.gets avoids this problem. An alternative workaround is to set ARGV to an empty array prior to capturing input via gets. You can simply set
ARGV = []
gets.chomp # works fine here
or store them elsewhere if you must get input before you've dealt with them all
cli_args = ARGV.dup
ARGV.clear
gets.chomp # works fine here

Trying to convert a number to binary form

I wrote a simple program which works:
a = 4.to_s(2)
puts a.reverse
I want to be able to change it based on input from the user in the terminal. This is what I wrote:
puts 'Please enter a number you would like to see expressed in binary form'
i = gets.chomp
b = i.to_s(2)
puts b
This is the error that I keep getting:
`to_s': wrong number of arguments(1 for 0) (ArgumentError)
You're starting with a string, so you need to convert it:
i.to_i.to_s(2)
The #to_s method on a string doesn't take any arguments.
You do not need the chomp method, #to_i will take care of it.
Write it as:
puts 'Please enter a number you would like to see expressed in binary form'
i = gets.to_i
b = i.to_s(2)
puts b
You are calling to_s on an string, not on integer as you are thinking, because Kernel#gets always gives you a String object.
First, convert it to Fixnum, then call Fixnum#to_s on the Fixnum instance, which takes an argument, but String#to_s doesn't accept arguments, which was why you got a complaint from Ruby.
It seems that you want to use Fixnum#to_s but in your program gets.chomp returns a string, so you actually call String#to_s
You can could do:
i = gets.chomp.to_i

Program to take input from command line into an array and find the biggest among them

I am new to Ruby and just can't figure out how you take input for an array from a user and display it.If anyone could clear that I can add my logic to find the biggest number.
#!/usr/bin/ruby
puts "Enter the size of the array"
n = gets.chomp.to_i
puts "enter the array elements"
variable1=Array.new(n)
for i in (0..n)
variable1[i]=gets.chomp.to_i
end
for i in (0..n)
puts variable1
end
How about capturing the array in one line?
#!/usr/bin/ruby
puts "Enter a list of numbers"
list = gets # Input something like "1 2 3 4" or "3, 5, 6, 1"
max = list.split.map(&:to_i).max
puts "The largest number is: #{max}"
You are doing it ok. But try this little change
#!/usr/bin/ruby
puts "Enter the size of the array"
n = (gets.chomp.to_i - 1)
puts "enter the array elements"
variable1=Array.new(n)
for i in (0..n)
variable1[i]=gets.chomp.to_i
end
puts variable1
or for undefined number of values here is one way
#!/usr/bin/ruby
puts "enter the array elements (type 'done' to get out)"
input = gets.chomp
arr = []
while input != 'done'
arr << input.to_i
input = gets.chomp
end
puts arr
I believe that this is a little bit more elegant solution.
puts "Please enter numbers separated by spaces:"
s = gets
a = s.split(" ")
#Displays array
puts a
#Displays max element
puts a.max
First you collect the series of numbers from the user, then you use a split method on the string, which converts it to the array. If you want to use some other separator, like "," than you can write s.split(","). After that you can use your logic to find the biggest number or you could just use max method.
Some feedback:
chomp.to_i is a bit redundant, since the latter will also remove newlines.
for x in y is not commonly seen in idiomatic Ruby code. It basically behaves like each with slightly different scoping rules and probably should have been removed from the language a while ago.
Ruby arrays are dynamic, so no need to preinitialize them. Something like (1..n).map { gets.to_i } would also produce the array you need.
Displaying it can then be done like this: array.each { |n| puts n }
Alternatively you can use the strip approach outlined before, take the numbers as command line arguments in ARGV or pipe into your program using ARGF.

private method `chomp' called for nil:NilClass (NoMethodError)

I am attempting to learn Ruby by converting a Java program to Ruby, but I've been coming up with an error surrounding this block of code:
def create
#user_input = String.new()
# #word_arr = Array.new
print "Enter the text to be converted to pig latin, EOF to quit: "
while gets do
STDOUT.flush
#user_input = gets.chomp
#word_arr = #user_input.string.split(' ')
#word_arr.each { |x| puts x.engToLatin() + ' '}
print "EOF to Quit"
#user_input = ""
end
end
I've been getting this error:
EnglishToPigLatin.rb:14:in `create': private method `chomp' called for nil:NilClass (NoMethodError)
from EnglishToPigLatin.rb:60
This is the area around line 60:
#if __FILE__ == $0
mg = EnglishToPigLatin.new
mg.create
#end
Essentially what I am trying to do is while there is still input, get that input, split it up into individual words, and run each word through a Pig Latin conversion method.
It looks like you're trying to get input inside of your loop.
Try
loop do
user_input = gets.chomp!
word_arr = user_input.to_s.split(' ')
word_arr.each { |x| puts x.engToLatin() + ' '}
puts "EOF to Quit"
end
Otherwise you're trying to get the next line of input when there isn't one. Additionally, do isn't necessary for a while statement.
You also don't need to reset #user_input to ''.
And since this is all in a block, you don't need to use instance variables, unless the methods you call need them.
Also your conditional is always true. gets will block until it gets a line of input. You can use loop for an infinite loop that ends on an interrupt.
Also, you needn't flush STDOUT if you use a puts for the last line there instead of a print.
The whole thing could be a script or a method in a module. An instance doesn't even need to be made. And if you do, instead of using two lines with your mg.create, you should define an initialize method. This is used as a constructor then, and whatever you set when you create an instance should be put there.
It can all be done like this:
loop do
puts gets.chomp.split(' ').map{ |x| x.engToLatin() }.join(' ')
puts "EOF to Quit"
end
Mario's answer is right. But I have the following notes.
You can still use the while construction as below.
+' ' implies that you don't want line breaks after each word. I changed that part. map and join is common in similar cases. print does not add a line break while puts does.
I am not sure what you are trying to do with STDOUT.flush. If you wanted to scroll to the top of the screen before each output, use system('clear').
You have a method entToLatin, and it should work, but it is a ruby convention to use underscore, like eng_to_latin for methods (although there are a few exceptions).
So a more rubyish way would be:
def create
print "Enter the text to be converted to pig latin, EOF to quit: "
while input = gets.strip and input != 'EOF'
system('clear')
puts input.split(/\s+/).map{|x| x.engToLatin}.join(' ')
puts "EOP to Quit"
end
end
And if you are using ruby 1.9.2, you can shorten map so that:
def create
print "Enter the text to be converted to pig latin, EOF to quit: "
while input = gets.strip and input != 'EOF'
system('clear')
puts input.split(/\s+/).map(:engToLatin).join(' ')
puts "EOP to Quit"
end
end

Resources