New to Ruby - While loop issues in IRB
i++
is not valid ruby. You need to do i += 1
.
Edit:
See Mladen's comment as to what the parser is seeing.
Why isn't my variable getting defined, and ending this loop?
Whether or not a variable is defined depends upon not only the scope, but also on its location within the ruby script. That is, a variable is defined only if it has been defined previously within the parse, not the execution.
Here's an example:
begin
puts "Is foo defined? #{defined?(foo).inspect}" # foo is never defined here
foo ||= 1
puts "but now foo is #{foo}" # foo is always defined here
foo += 1
end while foo <= 3
Output:
Is foo defined? nil
but now foo is 1
Is foo defined? nil
but now foo is 2
Is foo defined? nil
but now foo is 3
Because foo
has not been defined previously within the script on the first line of the loop, it is undefined at that point, and remains undefined, even if it is assigned to and the same line is returned to at a later point during execution.
This is why foo
in the while
condition of the question is always undefined:
while defined?(foo).nil? # foo is always undefined
foo = 1
end
and will loop forever. In contrast, this loop only executes once:
begin
foo = 1
end while defined?(foo).nil? # foo is defined
because foo
is assigned to previously in the parse.
Edit:
Only loops that require a block seem to isolate its local variables from living outside of it. E.g. loop
, upto
, each
, inject
, map
, times
, etc. These all require use of the keyword do
and end
, or curly braces, which delimit the block. In contrast, while
, until
, and for
do not, and so variables defined within them continue to live outside of them. This is demonstrated here:
while true
foo_while = 1
break
end
puts "foo_while: #{defined?(foo_while).inspect}"
until false
foo_until = 1
break
end
puts "foo_until: #{defined?(foo_until).inspect}"
for i in 0..2
foo_for = 1
break
end
puts "foo_for: #{defined?(foo_for).inspect}"
loop do
foo_loop = 1
break
end
puts "foo_loop: #{defined?(foo_loop).inspect}"
1.upto(2) do |i|
foo_upto = 1
break
end
puts "foo_upto: #{defined?(foo_upto).inspect}"
[1,2,3].each do |i|
foo_each = 1
break
end
puts "foo_each: #{defined?(foo_each).inspect}"
[1,2,3].inject do |i,j|
foo_inject = 1
break
end
puts "foo_inject: #{defined?(foo_inject).inspect}"
[1,2,3].map do |i|
foo_map = 1
break
end
puts "foo_map: #{defined?(foo_map).inspect}"
3.times do
foo_times = 1
break
end
puts "foo_times: #{defined?(foo_times).inspect}"
Output:
foo_while: "local-variable"
foo_until: "local-variable"
foo_for: "local-variable"
foo_loop: nil
foo_upto: nil
foo_each: nil
foo_inject: nil
foo_map: nil
foo_times: nil
Confusion with IRB output in Ruby when object#initialize is overloaded
What exactly are you trying to do? You just redefined Object.new
, so there is no surprise you make everything go haywire.
You can basically get the same effect by just:
>> def Object.new
>> end
>> [press enter]
KABOOM
The reason "hi"
is printed is that someone just called Object.new
, probably the irb
REPL loop, and it expected an object, but instead it gets gobledygook.
You can also try this:
def Object.new *args
p args
end
And you will see funny stuff. However you won't be able to quit irb or do anything useful with it after that. Again: you just broke Object
.
To make some sense of it you should read this:
In Ruby, what's the relationship between 'new' and 'initialize'? How to return nil while initializing?
And then you can try this:
class Object
class << self
alias :old_new :new
end
end
Now you can do:
def Object.new *args
p args
old_new *args
end
This won't break new
because you are still calling the old version of it. However you will now be printing out stuff every time someone calls new
.
Ruby Loop failing to break when using gets
Just use the String#chomp
method which will return the string without the new line separator:
gets_var.chomp == "stop"
https://ruby-doc.org/core-2.2.0/String.html#method-i-chomp
You can check it out manually in irb
:
irb(main):011:0> gets_var = gets
test
=> test
irb(main):012:0> pp gets_var
"test\n"
=> test
There's a new line (\n
) character at the end of the string. It appears always when you're using gets
.
Could gets be a loop condition?
The problem that in you sample while
loop will break when line
is null
, but gets.chomp
will return empty string when empty line is given.
Simplest solution to use loop
with explicit break
lines = []
loop do
line = gets.chomp
break if line.empty?
lines << line
end
If you would use ActiveSupport
library(included with Rails) you can do one liner with presence
method
lines << line while line = gets.chomp.presence
Related Topics
Removing or Overriding an Activerecord Validation Added by a Superclass or Mixin
Creating a Setter Method That Takes Extra Arguments in Ruby
Rvm Ruby with Tk Installation (Osx)
How to Disable Wait Time for Watir
Rails Cannot Load Such File -- MySQL2/Mysql2 (Loaderror)
Call Ruby Script from Powershell
Your Ruby Version Is 2.1.0, But Your Gemfile Specified 2.0.0
Ruby on Rails Routing Matching Username
Rbenv Install Ruby Build Failed
How to Find Current Abstract Route in Rails Middware
What Does "Temps.Each(&:Valid)" Mean in Ruby
How to Run Code After Each Line in Ruby
Differencebetween Ruby's 'Open-Uri' and 'Net:Http' Gems