Thursday 21 November 2013

Retaining a Value

Ok that's fine but now I want to print out a line number against those output lines.  Therefore the line number is an additional argument to the looper function:  set it to 1 on the first call from the main function, and then add one to it each time it is re-called from inside the looper.  Basic.  Like this:


main([Filename]) ->
    {ok, Stream} = file:open(Filename, [read]),
    listout_loop(Stream, 1),
    file:close(Stream).

listout_loop(Stream, LineNumber) ->
    case io:get_line(Stream, "") of
        eof ->
            ok;
        Line ->
            io:format("~b ~s", [LineNumber, Line]),
            listout_loop(Stream, LineNumber+1)
    end.

Speaking in rodent terms, then, in a procedural language you might keep the line number in a variable that you increment each time through the loop, squirrel-like, burying your treasure in the ground for easy access later: whereas in a functional language you keep your variable quantity balanced on your call parameters, hamster-like, running with your essentials stuffed in your cheeks.

So, just for the exercise, how would I return a result from the process?  Say I wanted to see the number of lines?  Well, when I hit eof I can return the LineNumber value - actually no, I will want LineNumber-1 because if I got eof then there was no line number LineNumber.  Then write this out at the end, no reason to except to prove I can do it.

Note incidentally that we don't close the file in the loop function when it gets the eof flag:  i.e. we don't say

    eof ->
        file:close(Stream).

By the Toybox Rule (If You Get It Out You Put It Away) it's the function that opens the file that should close it.


main([Filename]) ->
    {ok, Stream} = file:open(Filename, [read]),
    LineCount = listout_loop(Stream, 1),
    file:close(Stream),
    io:format("(~b lines)", [LineCount]).

listout_loop(Stream, LineNumber) ->
    case io:get_line(Stream, "") of
        eof ->
            LineNumber-1;
        Line ->
            io:format("~4.10.0B ~s", [LineNumber, Line]),
            listout_loop(Stream, LineNumber+1)
    end.

The mysterious format definition "~4.10.0B" requests a field four digits long in base 10 with 0 as the fill-up character showing the value of some Binary ie integer quantity.

So it looks like this:

C:\Users\polly\Erlang>escript listout.erl hello.erl
0001
0002 main([]) ->
0003     io:format("Hello World");
0004 main([Arg]) ->
0005     io:format("Hello ~s", [Arg]).
(5 lines)
C:\Users\polly\Erlang>

I admit I'm getting to like the idea that starting-lower-case identifiers are just atoms that mean themselves and can go enywhere with impunity.  Something has always told me that Nothing Of Importance Should Depend On The Case Of An Identifier but maybe the advantages justify breaking the rule.  These atoms do what symbols do in Lisp but in Lisp I have sometimes been caught out by forgetting whether I have quoted something or by quoting a quote.  Anyway.

Exercise: make the size of the line number field a parameter that can be passed in on the command line:-)

No comments:

Post a Comment