Zabawy z Erlangiem

10/04/2009

Niestety, rozczaruję tych czytelników, którzy spodziewali się znaleźć tu pikantne opisy zabaw wyprawianych przez obsługę Central Abonenckich naszego drogiego monopolisty. Dziś zajmiemy się pewnym, mało znanym choć bardzo sympatycznym, językiem programowania.

Na blogu Jarosława Zabiełły trafiłem na notkę o projekcie Eulera a konkretnie o zadaniu 17 z tegoż.

Brzmi ono następująco (cytat za JZ):

Jeśli liczby od 1 do 5 zapisać za pomocą ich (angielskich) słów: one, two, three, four, five, to wtedy użyte zostanie 3 + 3 + 5 + 4 + 4 = 19 liter. Gdyby wszystkie liczby od 1 do 1000 zapisać za pomocą słów to ile w sumie by użyto liter? Uwaga: nie należy liczyć spacji ani myślników. Np. liczba 342 (three hundred and fourty-two) zawiera 23 litery a liczba 115 (one hundred and fifteen) zawiera 20 liter. Łącznik “and” jest wymagany w jęz. angielskim.

Autor zamieścił w notce przykładowe implementacje dla języków Ruby, Python i Scala. A ja postanowiłem sprawdzić, jak wyglądało by to w Erlangu. ;)

Ponieważ chodziło o stosunkowo mały program, który rozliczany byłby za elegancję implementacji a nie za szybkość działania, postanowiłem wspomóc się programem escript. Umożliwia on uruchamianie programów napisanych w Erlangu bez potrzeby ich uprzedniego kompilowania do kodu źródłowego.

Mimo, iż moje doświadczenie w programowaniu z Erlangiem jest bardzo ubogie, to jednak prostota tego języka sprawiła, iż w ciągu godziny miałem już całkiem zadowalające rozwiązanie (i troszkę mniej atrakcyjnych wprawek).

#!/usr/bin/env escript
%
%
main(_input) ->
        count_letters(1000,0).
%
%
count_letters(0, TotalLength) ->
    io:format("~B~n", [TotalLength]);
%
count_letters(N, TotalLength) ->
    count_letters(N-1, TotalLength + length(num_to_word(N))).
%
%
num_to_word(0) -> "";
%
num_to_word(X) when X 
    Words = ["one", "two", "three", "four", "five",
             "six", "seven", "eight", "nine", "ten",
             "eleven", "twelve", "thirteen", "fourteen",
             "fifteen", "sixteen", "seventeen", "eighteen",
             "nineteen"],
    lists:nth(X, Words);
%
num_to_word(X) when X 
    Words = ["ten", "twenty", "thirty", "forty", "fifty",
             "sixty", "seventy", "eighty", "ninety"],
    Tens = X div 10,
    Rest = X rem 10,
    lists:append([lists:nth(Tens, Words), num_to_word(Rest)]);
%
num_to_word(X) when X 
    Hundreds = X div 100,
    Rest     = X rem 100,
    if
        Rest == 0 -> Extra = "hundred";
        Rest >  0 -> Extra = "hundredand"
    end,
    lists:append([num_to_word(Hundreds), Extra, num_to_word(Rest)]);
%
num_to_word(1000) -> "onethousand".
%
% koniec

Można zapisać to także w nieco zwięźlejszej, choć nieco mniej czytelnej, formie:

#!/usr/bin/env escript
%
%
main(_input) ->
        count_letters(1000,0).
%
%
count_letters(0, TotalLength) ->
    io:format("~B~n", [TotalLength]);
%
count_letters(N, TotalLength) ->
    count_letters(N-1, TotalLength + num_to_word(N)).
%
%
num_to_word(0) -> 0; % none
%
% one, two, three, four, five, six, seven, eight, nine, ten, eleven, twelve,
% thirteen, fourteen, fifteen, sixteen, seventeen, eighteen, nineteen
%
num_to_word(X) when X 
    Words = [3, 3, 5, 4, 4, 3, 5, 5, 4, 3, 6, 6, 8, 8, 7, 7, 9, 8, 8],
    lists:nth(X, Words);
%
% ten, twenty, thirty, forty, fifty, sixty, seventy, eighty, ninety
%
num_to_word(X) when X 
    Words = [3, 6, 6, 5, 5, 5, 7, 6, 6],
    Tens = X div 10,
    Rest = X rem 10,
    lists:nth(Tens, Words) + num_to_word(Rest);
%
num_to_word(X) when X 
    Hundreds = X div 100,
    Rest     = X rem 100,

    if
        Rest == 0 -> num_to_word(Hundreds) + num_to_word(Rest) + 7;   % "hundred"
        Rest >  0 -> num_to_word(Hundreds) + num_to_word(Rest) + 10   % "hundredand"
    end;
%
num_to_word(1000) -> 11.  % "onethousand"
%
% koniec

Oczywiście można napisać to jeszcze zwięźlej (wyrzucając niektóre zmienne), ale wtedy uzyskujemy, IMVHO, mało czytelny kod.

Na koniec przekazać chciałbym serdeczne pozdrowienia dla JZ – wszak to dzięki jego notkom zwróciłem kiedyś uwagę na Erlanga – wcześniejsze próby opanowania Scheme skutecznie zniechęciły mnie do języków, zawierających w swej charakterystyce określenie funkcyjne.

%d blogerów lubi to: