Search code examples
erlang

List replication challenge with Erlang


I'm trying to study Erlang resolving Hackerrank problems. There is a problem called List Replication. I finished with a solution like this:

-module(solution).
-export([main/0]).

process_input(Repeats)->
    Line = io:get_line(""),
    case string:len(Line) of
        1 -> ok;
        _ -> output(Line, Repeats), process_input(Repeats)
    end.

output(_, 0)-> ok;
output(Line, Repeats)->
    io:format(Line),
    output(Line, Repeats - 1).

main()->
  {ok, [Repeats]} = io:fread("", "~d"),
  process_input(Repeats).

But this solution has a problem: I expect the last line is empty (in fact the last line is a number without \n). Any thoughts?


Solution

  • I think it's easier to handle eof from io:get_line/1 in case of a missing final newline, as well as handling a blank line to indicate end of input:

    -module(solution).
    -export([start/0]).
    
    process_input(Repeat, Acc)->
        case io:get_line("") of
            Done when Done == eof; Done == "\n" ->
                output(lists:reverse(Acc));
            Line ->
                Val = string:strip(Line, right, $\n),
                Str = lists:duplicate(Repeat, Val),
                process_input(Repeat, [Str|Acc])
        end.
    
    output(Lines)->
        Out = [string:join(L, "\n")++"\n" || L <- Lines],
        io:format("~s", [Out]).
    
    start()->
        {ok, [Repeat]} = io:fread("", "~d"),
        process_input(Repeat, []).
    

    The process_input/2 function now takes an accumulator, which is initially an empty list. It calls itself recursively until it detects end of input, after which it prints the output. It calls io:get_line/1 and checks to see if it returns either eof or just a newline, and for that case it reverses its accumulator and prints its output. For any other input, it strips the final newline, repeats the input via lists:duplicate/2, stores the result in a new accumulator, and passes that to a recursive call.

    The output/1 function takes the accumulator from process_input/2, joins the repeated values with newlines, then prints the results. Note that this version of the solution module restricts the formatting of the result to the output/1 function, in case you wanted to use the raw result of process_input/2 for some other purpose.

    And finally, I renamed your main/0 to start/0 because running a function via the erl -s command line option assumes a function named start if none is given.

    We can use printf in a Unix shell to create an input file without a final newline:

    $ printf '3\n1\n2\n3\n4\n' > in
    

    and then run our compiled solution module like this, getting the tripled output we expect in this case:

    $ cat in | erl -noshell -s solution -s init stop
    1
    1
    1
    2
    2
    2
    3
    3
    3
    4
    4
    4
    

    Adding a final newline to the in file gives the same result (try it). We can also create input with more than a single character per line:

    $ printf '2\nhey\nhey\nwhat\ncan\nI\ndo' > in2
    $ cat in2 | erl -noshell -s solution -s init stop
    hey
    hey
    hey
    hey
    what
    what
    can
    can
    I
    I
    do
    do
    

    and for this in2 file, we get the doubled output we expect as well.