ruby push method is alternating - ruby

cool_words = []
while true
cool_words.push gets
break if gets.chomp == ''
end
puts cool_words
It is only pushing the first entry then the third and then the fifth. I think it is the way I have it breaking out of the loop because without the break method it doesn't happen.
I need it to break out of the loop when I hit enter on an empty line.
Thanks in advance!

You are calling gets twice in the loop. The first time it is being pushed into the array. The second time it is comparing against an empty string for loop breaking. But each time it is asking for a new line.
You only want to call gets one time per loop. So you can save it in a variable, and then use that variable multiple times later in the code.
cool_words = []
while true
line = gets
cool_words.push line
break if line.chomp == ''
end
puts cool_words
UPDATE: #MicahelKohl in the comments points out that you can accomplish the above task more elegantly like this:
cool_words = []
until (line = gets).to_s.chomp.empty?
cool_words << line
end
puts cool_words

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

How to determine whether input is empty or enter is pressed

I have a task to puts an infinite number of word, each in one line to array, and when enter is pressed on an empty line, puts these words in reverse order. How can I define when enter is pressed or empty line is input?
Code is here:
word = []
puts "Enter word"
add = 0
until add == ????
word.push gets.chomp
add = word.last
end
puts word.reverse
Here's a possible solution, with comments. I didn't see any useful role being played by your add variable, so I ignored it. I also believe in prompting the user regularly so they know the program is still engaged with them, so I moved the user-prompt inside the loop.
word = [] # Start with an empty array
# Use loop when the terminating condition isn't known at the beginning
# or end of the repetition, but rather it's determined in the middle
loop do
print 'Enter word: ' # I like to prompt the user each time.
response = gets.chomp # Read the response and clean it up.
break if response.empty? # No response? Time to bail out of the loop!
word << response # Still in the loop? Append the response to the array.
end
puts word.reverse # Now that we're out of the loop, reverse and print
You may or may not prefer to use strip rather than chomp. Strip would halt if the user input a line of whitespace.
Here, this is a modified version of your code and it works as requested.
word = []
puts "Enter word"
add = 0
while add != -1
ans = gets.chomp
word.push ans
if ans == ""
puts word.reverse
exit
end
add += 1
end
puts word.reverse
This is another version, using (as you did originally) the until loop.
word = []
puts "Enter word"
add = 0
until add == Float::INFINITY
ans = gets.chomp
word.push ans
if ans == ""
puts word.reverse
exit
end
add += 1
end
puts word.reverse

How to stop outer block from inner block

I try to implement search function which looks for occurrence for particular keyword, but if --max options is provided it will print only some particular number of lines.
def search_in_file(path_to_file, keyword)
seen = false
File::open(path_to_file) do |f|
f.each_with_index do |line, i|
if line.include? keyword
# print path to file before only if there occurence of keyword in a file
unless seen
puts path_to_file.to_s.blue
seen = true
end
# print colored line
puts "#{i+1}:".bold.gray + "#{line}".sub(keyword, keyword.bg_red)
break if i == #opt[:max] # PROBLEM WITH THIS!!!
end
end
end
puts "" if seen
end
I try to use break statement, but when it's within if ... end block I can't break out from outer each_with_index block.
If I move break outside if ... end it works, but it's not what I want.
How I can deal with this?
Thanks in advance.
I'm not sure how to implement it in your code as I'm still learning Ruby, but you can try catch and throw to solve this.
def search_in_file(path_to_file, keyword)
seen = false
catch :limit_reached do
#put your code to look in file here...
throw :limit_reached if i == #opt[:max] #this will break and take you to the end of catch block
Something like this already exist here

Ruby, Within a While Loop, Convert String to Method Call

This is lives within a method "play" that is called once. After you enter the while loop, you stay there until you exit the process. Right now, I'm trying to use the case statement to turn user-defined strings into the variable that is passed at the end to call the next method, all within the while loop.
def play
next_action = #start # comes from an initialize function earlier in script
while true
case next_action
when beginning
next_action = beginning
when "instruct"
next_action = instructions
when "display"
next_action = display_users
else
puts "Unknown command."
next_action = display_users
end
puts "\n----------"
next_action = method(next_action).call
end
end
First problem: the case statement fails to recognize any choice but the last.
Second problem: this leads to the loop ending, jumping to the last method called, and then exiting the process.
Any help or advice is appreciated.
See if changing
next_action = #start
to:
next_action = #start.chomp
gets you any further.
you should use a state-machine instead.
See: http://railscasts.com/episodes/392-a-tour-of-state-machines

How can I get this condition met for a loop that changes a string to end?

I'm comparing a word against another string, which is changing by looping through the alphabet and inserting each letter at every position of the word.
#position_counter = 0
EDIT: Here is the code that letter_loop is running through.
#array = ["amethod", "variable", "block"]
def word_list_loop
#match_counter = 0
#array.each do |word|
letter_loop(word)
end
puts #match_counter
end
CLOSE EDIT
def letter_loop(word)
("a".."z").each do |letter|
word_plus_letter = #word.dup
word_plus_letter.insert(#position_counter, letter)
#match_counter+=1 if word.match(/\A#{word_plus_letter}\z/)
end
#position_counter+=1
letter_loop(word) unless #position_counter == (#word.length + 1)
end
The word I'm using for the argument is "method". But when I run this, I am getting a index 7 out of string (IndexError). Its looping through the alphabet for each position correctly, but it doesn't seem to get caught with the unless #position_counter == (#word.length + 1) to end.
I've tried a few other ways, with an if statement, etc, but I'm not able to get the method to complete itself.
How many times are you running letter_loop? Are you sure the error happens in the first run? From what I see, if you call it a second time without resetting #position_counter to zero, it will begin with #word.length + 1 producing the exact error you see. Other than that, I couldn't find any problems with your code (ran just fine here on the first run).
Update: since you're using a recursive solution, and position_counter does not represent the state of your program (just the state of your method call), I'd suggest not declaring it as #position_counter but as an optional parameter to your method:
def letter_loop(word, position_counter=0)
("a".."z").each do |letter|
word_plus_letter = #word.dup
word_plus_letter.insert(position_counter, letter)
#match_counter+=1 if word.match(/\A#{word_plus_letter}\z/)
end
position_counter+=1
letter_loop(word, position_counter) unless position_counter == (#word.length + 1)
end
If you can't/don't want to do this, just reset it before/after each use, like I suggested earlier, and it will work fine:
#array.each do |word|
#position_counter = 0
letter_loop(word)
end
(though I wouldn't recommend this second approach, since if you forget to reset it somewhere else your method will fail again)
I think the problem is that you are calling letter_loop from within #array.each, but you don't reset #position_counter to zero on each iteration of the #array.each loop.
If that doesn't fix your problem, add something like this as the first line of letter_loop:
puts "letter_loop word=#{word}, position=#{#position_counter}, matches=#{#match_counter}"
Then run the program and examine the output leading up to the IndexError.

Resources