Advent of Code to wyzwanie, które podejmuję już od kilku lat. W tym roku udało mi się wytrwać 18 dni (z czego 15 dni do złotych gwiazdek), ale nie o tym. Jak co roku poza oczywistym wyzwaniem pod postacią wyborną fabułę zawierających zadań dodałem kilka własnych.
Moja codzienna rutyna przez początkowe dni to pobudka o 5:00-ish, kawa, rozpoczęcie robienia zadania ok. 6:00, submit, śniadanie i daily. Czy zdrowo? Raczej nie. Czy sportowo? Też raczej nie. Ale czy było efektywnie? Też nie. Ale o iluzji produktywności napiszę innym razem.
Side-questy
- Każdego roku wybierać nowy (względem wyzwania) język programowania. W 2021 roku padło na C++. Rok 2022 jest rokiem języka Lua.
- W tym roku dodatkowym wyzwaniem było użycie wersji Preview nowego edytora kodu od JetBrains – Fleet, o którym bardziej szczegółowo tak, ale nie teraz.
- Starać się nie używać ułatwiających zadanie bibliotek,
- co oznacza:
- żadnych bibliotek implementujących algorytmy lub
- kodu znalezionego na StackOverflow i innych miejscach z gotowymi rozwiązaniami
- za wyjątkiem:
- bibliotek wchodzących w skład definicji języka (np. stdlib c++) lub
- zawierających poważną matematykę (np. BigNum, sin/cos, you name it) lub
- przez tradycję niemalże wcielonych w język (dla Javy byłby to Lombok, Apache Commons itp.).
- co oznacza:
- Nie wyciągać z danych informacji o danych, zwłaszcza metodą organoleptyczną.
- Nie wczytywać wszystkich punktów z map (działać na macierzach rzadkich).
- Poznać jak najwięcej tego, co daje wybrany język programowania.
- Do końca wyzwania commitować kod w stanie brudnym (łącznie z bezczelnymi printami).
- Po wyzwaniu przejrzeć kod, wyciągnąć wnioski, ewentualnie poprawić.
Odkrycia
Dzień 1: Indeksowanie od 1
Indeksowanie tablic w Lua wygląda tak:
-- Indeksowanie od 1
tablica = {}
tablica[0] = 'Błąd'
tablica[1] = 'To zadziała'
tablica[#tablica] = 'To nadpisuje ostatni element'
tablica[#tablica + 1] = 'A tutaj dodajemy kolejny element'
Wprawne oko zauważy kontrowersyjną rzecz, a mianowicie indeksowanie od 1. Ale czy rzeczywiście jest to takie kontrowersyjne i nie na miejscu?
Lua powstało jako język rozszerzeń dla większych aplikacji napisanych w C/C++ do opisu rzeczy biznesowych – oznacza to, że podobnie jak w prawdziwym świecie tabele nie zaczynają się od pozycji zerowej, ale od jedynki. Obecnie ten język jest najszerzej wykorzystywany przez amarotów-skrypterów i twórców gier lub modyfikacji do nich.
Dla programisty-profesjonalisty indeksowanie od 0 jest naturalne. Dlaczego? Dawno, dawno temu w języku C zaprojektowano arytmetykę wskaźników – i miało to sens, ponieważ C jest bardzo blisko związane ze sprzętem, który przecież operuje na miejscach w pamięci. Dlatego w zamierzchłych czasach poniższe miało sens:
int arr[] = {0, 1, 2};
int pos = 0;
if (arr[pos] == pos[arr]) {
printf("Indeksowanie jest przemienne!");
}
Programując w Lua nie mamy możliwości odwoływania do konkretnych miejsc w pamięci, a Java już nawet nie wie co to jest pamięć, więc wybór wydaje się zasadny.
Jednakże indeksowanie od 1 w Lua jest dobrym pomysłem, ale złym byłoby zaimplementowanie tego w Javie. Do Javy siadają osoby, które mają już jakąś wiedzę i przyzwyczajenia z innych języków programowania, które pochodzą z tradycji przekazywanej od Denisa Ritchiego aż do dzisiaj. Jakby nagle tych ludzi posadzić przed nienaturalnym dla nich językiem to lepiej, żeby nie pisali systemów bankowych.
Dzień 2: Tablice
Na początku była tablica… Typ table
różnicuje się na tablicę i mapę dopiero w momencie, kiedy zapisywany jest do niej pierwszy element. Indeksujemy typ table
przy pomocy liczby? Otrzymujemy tablicę. W przypadku czegokolwiek innego mamy do czynienia z mapą. Korzyści? Nie. Problemy? Tak:
-- Tablica czy mapa?
t = {}
t[1] = 'a' -- OK
t['b'] = 'b' -- błąd
t = {}
t['a'] = 'a' -- OK
t[#t + 1] = 'b' -- błąd
W obu przypadkach mamy do czynienia z tym samym typem, ale jego zachowanie ulega zmianie. Nawet bym nie miał nic przeciwko i nazwał to 'Duck Typingiem’, ale Lua obsługuje jednak sprawdzanie typów. W tym przypadku musimy nie tylko mieć na uwadze typ danych, ale także typ typu danych…
Dzień 3: goto zamiast continue
Programowanie w QBASICu to w 90% GOTO
i w 10% logika aplikacji. I mogłoby się wydawać, że używanie goto to relikt, a w Javie zarezerwowane słowo kluczowe wywołujące błąd kompilacji, ale mamy Lua.
-- goto
for i=1,#t do
if i % 2 == 0 then
goto continue
end
print(t[i])
::continue::
end
Do wyboru mamy praktycznie dwie opcje – albo dzika indentacja i wrzucenie wszystkiego w ifa, albo goto.
Albo grzecznie poprosić twórców o dodanie continue
. (proszę…)
Dzień 5: Nauczyłem się wywoływać metody
Czasami potrzebuję trochę więcej czasu, żeby coś ogarnąć i się czegoś nauczyć. Tak było i w przypadku korzystania z obiektów (i przyznaję, że jest to dosyć Pythonowe rozwiązanie).
-- Wywołanie metody
s = '21:37'
g, h = string.match(s, '(%d+):(%d+)')
g, h = s:match('(%d+):(%d+)')
Dzień 7: Zaczyna mi się to nawet podobać
Po tygodniu walki z nieznajomością Lua pisanie w tym języku zaczęło mi się nawet podobać. I trzeba przyznać, że Lua posiada kilka cech, które ułatwiają życie:
for i = begin, end, step do ...
– iterowanie w przedziale z zadanym krokiem jako element składni języka#tablica
– bardzo szybkie odwołanie do ilości elementów tablicyfor k, v in pairs(tablica) do ...
– wsparcie dla for-each
Standardowo jednak istnieją przeszkadzajki:
for _, v in pairs(tablica) do ...
– jako jedyna opcja przy iterowaniu tablicy bez kluczyresult = result + 1
– brak operatorów do zwiększania i zmniejszania zmiennej- nieżyciowe tworzenie klas i obiektów
Dzień 9: table to mapa, set i tablica
Java, TypeScript i C++ przyzwyczaiło mnie do posiadanie wyspecjalizowanych typów danych typu List, Set, Map… W Lua wszystko można „rozpykać” typem table
:
-- Tablica
tablica = {1, 2, 3}
tablica[1] = 4
print(tablica[2])
-- Mapa
mapa = {['klucz1'] = 1, ['klucz2'] = 2}
mapa['klucz3'] = 3
print(mapa['klucz1'])
-- Set
mapa = {['A'] = 1, ['B'] = 1}
mapa['C'] = 1
print(mapa['D'] == nil)
Genialne w swojej prostocie.
Dzień 11: Używanie modułów
Tego dnia w drugiej części zadania poczułem potrzebę skorzystania z modułu BigInt
. Zadanie wymagało jednak optymalizacji i z niego nie skorzystałem. System modułów w Lua jest dość prosty i polega na wczytaniu pliku źródłowego funkcją require('module.lua')
, w ramach bonusu możemy zapisać sobie taki kontekst do zmiennej.
Dzień 13: Dodawanie stringów
JavaScript posiada mechanizm, który przy operacjach arytmetycznych próbuje sobie zamienić argumenty na liczby i spróbować je przetworzyć. Zazwyczaj korzyści takie rozwiązanie nie ma, a powoduje tylko głupie błędy (które zabierają dużą ilość życia przy debugowaniu – więcej o tym na koniec).
-- Dodawanie stringów?
b = '1' + 2
print(b) -- 3
Dzień 17: Spróbujmy tego modnego OOP
Lua wspiera programowanie obiektowe, z którego trochę skorzystałem. Niestety, przejrzystość definiowania klas w Lua nie powala i bardziej przeszkadza niż pomaga:
Klasa = {}
function Klasa:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
obiekt = Klasa:new{param = 1}
Widzę w tym potencjał podczas definiowania każdej klasy w oddzielnym pliku, ale bez oddzielania definicji poszczególnych klas jak na przykład w C++/Javie oddzielnymi blokami jest to bardzo mało czytelne.
Wnioski końcowe
- Lua dla swoich zastosowań jest spoko, ale to nie będzie mój ulubiony język.
- Pomimo, że jest to język skryptowy, Lua działa bardzo szybko i sprawnie.
- Brakuje możliwości debugowania kodu jak na przykład w Javie i Pythonie. Jedyne, co pomaga to printowanie wszystkiego na potęgę.
- Ze względu na problemy z typowaniem i średnio czytelną składnię, tego języka bym nie zastosował do krytycznych aplikacji.
- Trudno będąc przyzwyczajonym do języków obiektowych nagle przestawić się na programowanie proceduralne.
Dlaczego zrezygnowałem tak szybko?
Po pierwsze – nie szybko. Pobiłem swój rekord wytrwałości o całe 5 dni. Niestety z każdym kolejnym dniem walka z zadaniem, językiem i średnim (nieistniejącym) debuggerem przepala motywację. Brak woli walki ze strony współpracowników, z którymi też się ścigałem, nie uzupełniał niestety tego niedoboru motywacji.
To nie jest porażka, ale roztropne zostawienie sobie miejsca na rozwój.
Dodaj komentarz