Why / How is my variable getting set to nil
I have some code that reads a file into an array of lines, and then parses those lines to get at the structured data. The input file has various different data types that need to be handled differently, additionally there are major sections for various accounts (mobile numbers).
I loop through the lines looking for the account line, identify the account, and then I want to use that account until I encounter the next account line. The lines in between potentially represent various types of data belonging to that account. The problem is, after I find the account line and set a local variable (cur_num), the variable is set to nil when I want to use it. Why, how is this happening? I am learning Ruby so I want more than a fix - I want to understand why it works this way.
Here is my code:
count = 0
cur_num = ""
lines.each do |line|
unless (line.strip.eql?("")) # edited due to comment from normalocity
if (line.slice(0,15) == "Mobile Number:,")
cur_num = line.slice(15,12)
count = 1
puts "Current Number: #{cur_num}"
#puts "Object Type: #{cur_num.class}"
else
data = line.strip.split(',')
if (data.length > 8)
data.unshift(cur_num)
#if (count.modulo(10) == 0 || count == 1)
puts "[#{cur_num}] #{data.inspect}"
#pp data
#end
count += 1
end
end
end
end
A开发者_StackOverflow社区n overview of the input data structure would look like this:
Account 1
Data Section A
data line 1
data line 2
Data Section B
data line 1
data line 2
Account 2
Data Section A
data line 1
data line 2
Data Section B
data line 1
data line 2
end
adding code to duplicate lines array you should paste this above code sample if you are attempting to duplicate. I'm putting it here at the end to try to make my question more readable:
lines = []
lines.push("ATT Wireless Bill")
lines.push("")
lines.push("Mobile Number:,770-555-1212")
lines.push("item,date,time,number called,rate period,plan type,minutes,airtime charge,ld charge,total charge")
lines.push("")
lines.push("1,2011-01-02,6:56AM,404-555-1212,NW,UNW,4,0.00,0.00,0.00")
lines.push("")
lines.push("2,2011-01-03,6:56AM,404-555-1212,NW,UNW,4,0.00,0.00,0.00")
lines.push("")
lines.push("1,2011-01-03,7:56AM,404-555-1213,DT,UM2M,5,0.00,0.00,0.00")
lines.push("")
lines.push("Mobile Number:,770-555-1213")
lines.push("item,date,time,number called,rate period,plan type,minutes,airtime charge,ld charge,total charge")
lines.push("")
lines.push("1,2011-01-02,6:56AM,404-555-1212,NW,UNW,4,0.00,0.00,0.00")
lines.push("")
lines.push("2,2011-01-03,6:56AM,404-555-1212,NW,UNW,4,0.00,0.00,0.00")
lines.push("")
lines.push("1,2011-01-03,7:56AM,404-555-1213,DT,UM2M,5,0.00,0.00,0.00")
lines.push("")
Answer to the answer Why / How is my variable getting set to nil
Without cur_num = ""
you don't initialize cur_num outside the loop (lines.each
).
So cur_num
is initialized in each loop.
I would expect a undefined local variable or method
error, but it seems cur_num
is created in each loop, even, if the if-branch is not executed. So you have cur_num
, but without a value (or better: it is nil).
I added a short example to show, that a variable is created, even if the code is not executed:
if false
a = 1
else
p a #works fine, a was created, but it is nil
p b #undefined local variable or method `b'
end
Addendum II:
The following code shows, that a variable inside a loop is 'loop internal'. The 2nd (and following) cycle(s) starts without the variable a.
#~ a = 0 # uncomment to compare
5.times{
if defined? a
puts "a is defined as #{a.inspect}"
else
puts "a is not defined"
a = 1 #define it now
end
}
Related questions: The scope is confusing
I redesigned your code a bit. When I parse texts like yours, I prefer to use a case
-statement with regular expressions:
count = 0
cur_num = ""
DATA.each_line do |line|
case line
when /\A\s\Z*/ #skip empty lines
when /Mobile Number:,(.{12})/
cur_num = $1
count = 1
puts "Current Number: #{cur_num}"
#puts "Object Type: #{cur_num.class}"
else
data = line.strip.split(',')
if data.length > 8
data.unshift(cur_num)
#if (count.modulo(10) == 0 || count == 1)
puts "[#{cur_num}] #{data.inspect}"
#pp data
#end
count += 1
end #(data.length > 8)
end #case line
end
__END__
ATT Wireless Bill
Mobile Number:,770-555-1212
item,date,time,number called,rate period,plan type,minutes,airtime charge,ld charge,total charge
1,2011-01-02,6:56AM,404-555-1212,NW,UNW,4,0.00,0.00,0.00
2,2011-01-03,6:56AM,404-555-1212,NW,UNW,4,0.00,0.00,0.00
1,2011-01-03,7:56AM,404-555-1213,DT,UM2M,5,0.00,0.00,0.00
Mobile Number:,770-555-1213
item,date,time,number called,rate period,plan type,minutes,airtime charge,ld charge,total charge
1,2011-01-02,6:56AM,404-555-1212,NW,UNW,4,0.00,0.00,0.00
2,2011-01-03,6:56AM,404-555-1212,NW,UNW,4,0.00,0.00,0.00
1,2011-01-03,7:56AM,404-555-1213,DT,UM2M,5,0.00,0.00,0.00
After staring at this for over an hour, comparing two nearly identical versions, one that worked, one that didn't, I am able to make it fail or succeed by commenting out a single line. here is the exact code I am running: [Edit note, altered the code to prevent vertical scroll bar in code sample]
#!/usr/local/bin/ruby
my_input ="ATT Wireless Bill\n\nMobile Number:,770-555-1212\n\nitem,date,time,number called,rate period,plan type,minutes,airtime charge,ld charge,total charge\n\n1,2011-01-02,6:56AM,404-555-1212,NW,UNW,4,0.00,0.00,0.00\n\n"
my_input << "2,2011-01-03,6:56AM,404-555-1212,NW,UNW,4,0.00,0.00,0.00\n\n1,2011-01-03,7:56AM,404-555-1213,DT,UM2M,5,0.00,0.00,0.00\n\nMobile Number:,770-555-1213\n\nitem,date,time,number called,rate period,plan type,minutes,airtime charge,ld charge,total charge\n\n"
my_input << "1,2011-01-02,6:56AM,404-555-1212,NW,UNW,4,0.00,0.00,0.00\n\n2,2011-01-03,6:56AM,404-555-1212,NW,UNW,4,0.00,0.00,0.00\n\n1,2011-01-03,7:56AM,404-555-1213,DT,UM2M,5,0.00,0.00,0.00\n\n"
lines = my_input.split("\n")
count = 0
cur_num = "" # Line 7 - Comment out this line to see failure
lines.each do |line|
unless (line.strip.eql?(""))
if (line.slice(0,15) == "Mobile Number:,")
cur_num = line.slice(15,12)
count = 1
puts "Current Number: #{cur_num}"
#puts "Object Type: #{cur_num.class}"
else
data = line.strip.split(',')
if (data.length > 8)
data.unshift(cur_num)
#if (count.modulo(10) == 0 || count == 1)
puts "[#{cur_num}] #{data.inspect}"
#pp data
#end
count += 1
end
end
end
end
If I comment out line 7, then cur_val is nil on lines 18 and 20.
Can someone please explain why?
精彩评论