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
Related
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.
I am trying to read the file name and process each line. How do I pass the file name to the the function?
puts "file name?? "
_file = get.chomps
def printFile(_file)
do |f|
f.each_line do |line|
print_me = "Line 1 " + line
return print_me
end
end
end
I am planning to pass print_me to another function like:
def thisWillPrint(print_me)
new_print = print_me + " DONE! "
end
I can see a few problems in your code. First you are using a block inside the definition of your printFile function which is a syntax error, next you use the variable f in that block which was never given a value, on top of that you try to do a loop on it and never open a file descriptor. Finally you must call the printFile function somewhere so that ruby knows it has to run it.
The first thing your printFile function should do is get a file descriptor to the file the user gives you as a string in the _file variable, this way you actually have a stream you can read lines from not just the string object. So I recommend you change the variable from _file to fileName, and leave file for the stream. You do this by using Ruby's own File class and calling its open method. As you can see from the documentation open can be called in a few different ways, but let's use a block like you were trying to do.
puts 'give me a path to a file'
fileName = gets.chomp
def printFile(fileName)
counter = 0
File.open(fileName) do |file|
while line = file.gets
print_me = "line " + counter.to_s + " "+line
thisWillPrint(print_me)
end
end
end
def thisWillPrint(print_me)
puts print_me + " DONE! "
end
printFile(fileName)
You also have to call the printFile function at the end so that ruby actually runs something.
Note that by returning inside the loop, you will exit it as well. With the following, you will get the contents of the file.
def printfile(filename)
print_me = ""
File.open(filename, "r") do |f|
f.each {|line| print_me << line }
end
print_me
end
For large files, the return variable will also be very large.
To read a line from standard input you can use the gets method. The gets method captures a newline \n by default. You have to use the chomp method to get rid of the newline.
So to get the file's name from standard input you can do the following:
print "File's name? "
_file = gets.chomp
Inside the printFile method you can do the following:
def printFile(_file)
print_me = ""
File.foreach(_file) do |line|
# process each line however you'd like inside this block
# for example:
print_me += line
end
return print_me # explicit return not required
end
Note that you do not have to explicity 'return' something if it's the last expression in the method. The last expression could have just been print_me instead.
You can pass what this method returns to another method like thisWillPrint like this:
def thisWillPrint(print_me)
new_print = print_me + "Done!"
end
output = printFile(_file)
thisWillPrint(output)
I'm almost a Ruby-nOOb (have just the knowledge of Ruby to write some basic .erb template or Puppet custom-facts). Looks like my requirements fairly simple but can't get my head around it.
Trying to write a .erb template, where it reads a file (with space delimited lines) to an array and then handle each array element according to the requirements. This is what I got so far:
fname = "webURI.txt"
def myArray()
#if defined? $fname
if File.exist?($fname) and File.file?($fname)
IO.readlines($fname)
end
end
myArray.each_index do |i|
myLine = myArray[i].split(' ')
puts myLine[0] +"\t=> "+ myLine.last
end
Which works just fine, except (for obvious reason) for the line that is commented out or blank lines. I also want to make sure that when spitted (by space) up, the line shouldn't have more than two fields in it; a file like this:
# This is a COMMENT
#
# Puppet dashboard
puppet controller-all-local.example.co.uk:80
# Nagios monitoring
nagios controller-all-local.example.co.uk::80/nagios
tac talend-tac-local.example.co.uk:8080/org.talend.admin
mng console talend-mca-local.example.co.uk:8080/amc # Line with three fields
So, basically these two things I'd like to achieve:
Read the lines into array, stripping off everything after the first #
Split each element and print a message if the number id more than two
Any help would be greatly appreciated. Cheers!!
Update 25/02
Thanks guy for your help!!
The blankthing doesn't work for at all; throwing in this error; but I kinda failed to understand why:
undefined method `blank?' for "\n":String (NoMethodError)
The array: myArray, which I get is actually something like this (using p instead of puts:
["\n", "puppet controller-all-local.example.co.uk:80\n", "\n", "\n", "nagios controller-all-local.example.co.uk::80/nagios\n", ..... \n"]
Hence, I had to do this to get around this prob:
$fname = "webURI.txt"
def myArray()
if File.exist?($fname) and File.file?($fname)
IO.readlines($fname).map { |arr| arr.gsub(/#.*/,'') }
end
end
# remove blank lines
SSS = myArray.reject { |ln| ln.start_with?("\n") }
SSS.each_index do |i|
myLine = SSS[i].split(' ')
if myLine.length > 2
puts "Too many arguments!!!"
elsif myLine.length == 1
puts "page"+ i.to_s + "\t=> " + myLine[0]
else
puts myLine[0] +"\t=> "+ myLine.last
end
end
You are most welcome to improve the code. cheers!!
goodArray = myArray.reject do |line|
line.start_with?('#') || line.split(' ').length > 2
end
This would reject whatever that either starts with # or the split returns an array of more than two elements returning you an array of only good items.
Edit:
For your inline commenting you can then do
goodArray.map do |line|
line.gsub(/#.*/, '')
end
I would like to use eval() in Ruby 1.9 to test little pieces of ruby code in an interactive way. A long time ago (around Ruby 1.4) I found a neat script on the internet providing this functionality. Here is simplified and reduced version:
line = ''
$stdout.sync = true
print "ruby> "
while true
input = gets
if input
line = input
else
break if line == ''
end
begin
print eval(line).inspect, "\n"
rescue ScriptError, StandardError
$! = 'exception raised' unless $!
print "ERROR: ", $!, "\n"
end
break if not input
line = ''
print "ruby> "
end
I was able to do something like:
ruby> str = "a:b:c"
"a:b:c"
ruby> str.split /:/
["a", "b", "c"]
ruby>
This script works fine up to Ruby 1.8, however not anymore in 1.9 due to the changed semantics of eval(). Now I'm not able to make local variables like str anymore. Instead I get the following obvious message:
ERROR: undefined local variable or method `str' for main:Object
Is there a way to fix or bypass this behaviour of eval()? I've read something about bindings but I'm not sure how to do that here.
Of course there is irb but in that tool I can't use the pound sign like in "abc#{var}def". If I try then irb comments out the whole line.
Working code:
$stdout.sync = true
while true
print "ruby> "
input = gets
break unless input
begin
p eval(input, TOPLEVEL_BINDING)
rescue ScriptError, StandardError
puts "ERROR: #{$! || "exception raised"}"
end
end
I changed many thing in the code to make it clean, but the point is the scope of the eval. It was being executed inside the begin block. All local variables defined by the eval were being created inside the begin block and were being destroyed when the begin ends, after each iteration. The constant TOPLEVEL_BINDING returns the scope of the top level (outside everything). It makes the eval execute the code in a place that will not be destroyed (until the program ends). You can also get the scope of any place using the method binding and send it as the second argument of eval.
def get_a_binding
x = 42
return binding
end
x = 50
scope = get_a_binding
eval("print x", scope) #=> prints 42
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