Ex. 20 How does print_a_line know which line to print?


#1

Hello all,

I’m a little confused with exercise twenty and the print_a_line method:

def print_a_line(line_count, f)
puts “#{line_count}, #{f.gets.chomp}”
end

What I know is the method takes two arguments, line_count (which is an integer stored in current_line) and f (which is the opened file stored in current_file.) The method then outputs the integer passed to it from line_count, and then the line in the file that corresponds to that number.

What I don’t understand is how does this method know which line to print out? I don’t see anything in the code that says “Hey! Print out the first line of this file! Now, print out line two!”

Hopefully my question is clear enough. Thank you for your time!


#2

Hey there,
You have puts "#{line_count}, #{f.gets.chomp}, so ruby knows how to get the line number “line_count” from the given file, chomps it (removes the newline character at the end of the line) then “puts” prints it.


#3

Almost @io_io, when you call the function @strugglebunny you actually tell it the line number. Remember, there’s two parts to a function. The definition, which is what you have here, and where it’s called. If you only look at the definition then you only see half of the story. Go look at where it’s called when you something like:

print_a_line(line_count, f)

Now, the trick here is that then jumps up to where you wrote def print_a_line(line_count, f) and starts running the code there until it gets to the end. The end then causes it to go back down to right after where you wrote print_a_line() to call it, and moves on to the next one. The next piece is that you are passing line_count and f to ```def print_a_line`` so it is just using the one that you have outside.

The problem with my above description though is that it’s hard to understand from text. It’s something you have to see for yourself, so, put some of these right before you call it and right after:

puts ">>>> calling print_a_line:  line_count=#{line_count}"

Then at the top of line count and just before end to something like:

puts ">>>> entering print_a_line: line_count=#{line_count}"

If it helps you can also put in the line number in the source code for that spot so you can trace what’s going on. Now, what you should do then is run it, and then trace this output along with your code and keep track (maybe on paper) of what line_count is at each line of your code.

Try that, then let me know if you still can’t follow it and I can talk about using the ruby debugger.


#4

Hi all,

Thanks for your feedback! I really appreciate it!

@io_io As far as I know, line_count is just an argument containing an integer that Ruby isn’t doing anything with other than outputting the value with the “puts” method. I think my problem is my lack of understanding of how the “gets” method works. (Please see below…)

@zedshaw My issue with the code is a little different from your explanation. I can follow everything fine, I’m just confused as to how Ruby knows when to start and stop printing for each line. I think the problem is my ignorance of the “gets” method, and how it works. I still don’t completely understand it, but I think this explanation I found on Stack Overflow is closer to answering the problem I’m having with understanding what’s happening here.


#5

I know, @zedshaw. I misunderstood what @strugglebunny’s problem was.
Thought he knew what happened when the function was called but was confused about the def part itself.
Also , I am not good at explaining stuff exactly as I see it in my mind.
I opened the exercise, saw the whole of it, understood how it worked and only explained half of the problem, kinda.


#6

Ok, hope you see it now.


#7

Ah, that’s easy @strugglebunny:

It just read each character from the file until it hits a \n (new line), then stops and returns that.

Now, the next thing to realize is that all files on a computer are modeled after old tape drives on mainframes (even though they don’t need to be). Those are nearly the same as a reel-to-reel tape or a cassette tape from the 80’s. As the gets function reads the file it treats the file like a tape and moves the “read head” forward by 1 byte, then again and again until it reads a \n (newline), then stops and returns.

So now your file, f is stopped at the last point that gets read, kind of like it paused the reel-to-reel or cassette tape, then let your code continue. Now, then next time you call gets it basically presses play on the tape and starts reading it again, 1 byte at a time, until it hits the next \n and returns again.

Then when it does this enough it reaches the end of the file, which is like the end of the tape. This is called the EOF (end of file), so if you want to again read from the file you actually have to rewind it. In the old drives this was actually called a “seek” because you’d seek the read head backward or forward trying to find something (seek or look for). In ruby you can do this with f.rewind as the easiest, although there is a function called f.seek too but it’s more complicated.

Also, try doing:

puts f.pos

You can even see it work here:

https://ruby-doc.org/core-2.2.2/IO.html#method-i-pos

After each gets to see it move the read head forward, and you can look through the docs here https://ruby-doc.org/core-2.2.2/IO.html


#8

@zedshaw This explains exactly what I was missing! Thank you so much for your help!


#9

Great, yes a surprising amount of technology understanding comes from knowing the history. I lived most of this so it’s trivial for me, but for others it’s not which is why I sometimes go off on what seems like a tangent with the history.