Prindeal (wymawiane prin-dee-al ) to nowy ezoteryczny język programowania, który ma tylko cztery polecenia: pr int , in crement , de crement i al ias . Pomimo minimalizmu skomplikowane operacje matematyczne można wykonywać w Prindeal, sprytnie łącząc cztery polecenia.
Twoim zadaniem w tym wyzwaniu golfowym jest napisanie najkrótszego programu, który może uruchomić kod Prindeal.
Specyfikacja jest długa, ale starałem się, aby była jak najbardziej klarowna i wierzę, że jeśli spróbujesz nauczyć się Prindeal, przekonasz się, że jest dość elegancki!
Interpretowanie Prindeal
Przetwarzanie wstępne
Przed interpretacją programu Prindeal należy usunąć z niego następujące rzeczy w następującej kolejności:
- Wszystko po
#
znaku do końca linii jest włączone, plus#
sam. (To są komentarze.) - Końcowe białe znaki w dowolnej linii.
- Całkowicie puste linie.
Na przykład program Prindeal
p cat #The next line has 7 trailing spaces.
p dog
#p mouse
zostanie wstępnie przetworzony w
p cat
p dog
Odtąd założymy, że ten krok przetwarzania wstępnego został wykonany.
Zmienne
Musimy szybko zdefiniować zmienne, zanim pokażemy, jak są używane.
Zmienne (i odniesienia do zmiennych) są przekazywane do argumentów poleceń Prindeal. Zmienne są zawsze globalne , więc modyfikacje zmiennej, bez względu na to, gdzie występują, są odzwierciedlane wszędzie.
Każda zmienna zawiera nieujemną liczbę całkowitą o dowolnej dokładności (0, 1, 2, 3, ...). Zmienne nie muszą być inicjowane wstępnie - zawsze zaczynają się od wartości 0 przy pierwszym użyciu lub wywołaniu.
Nazwą zmiennej może być dowolny niepusty ciąg znaków alfanumerycznych i znaków podkreślenia, który nie zaczyna się od cyfry - [a-zA-Z_][0-9a-zA-Z_]*
w wyrażeniu regularnym . Rozróżniają małe spiny_lumpsuck3r
i wielkie litery, a więc Spiny_lumpsuck3r
są różnymi zmiennymi.
Wykonanie
Prindeal to imperatywny język programowania. Po uruchomieniu programu Prindeal jego instrukcje są wykonywane od góry do dołu w kolejności, a następnie program się kończy.
Każda linia bez wcięcia w programie Prindeal jest instrukcją, która wymaga wykonania pojedynczej komendy, która może przyjmować lub nie argumenty.
Wcięte linie występują tylko po poleceniach aliasu . W szczególności dokładnie trzy wiersze wcięte pojedynczymi spacjami występują po każdym poleceniu aliasu i są uważane za jego część. Zatem instrukcje aliasów mają tak naprawdę cztery linie. (Mogą to być jedna linia, cztery są po prostu bardziej czytelne).
Oświadczenia inne niż alias
Z wyjątkiem aliasu każda instrukcja w programie Prindeal ma postać:
[command name] [argument 1] [argument 2] [argument 3] ...
Może istnieć dowolna liczba argumentów (w tym żadna). Każdy argument jest zawsze zmienną lub (jak zobaczymy podczas omawiania aliasu ) odniesieniem do zmiennej .
Po zakończeniu wykonywania każda instrukcja jest oznaczana jako błąd lub sukces w zależności od tego, czy wystąpiły błędy, czy nie. (To naprawdę ma znaczenie tylko wtedy, gdy przejdziemy do używania aliasu ).
Wbudowane opcje drukowania , zwiększania i zmniejszania są instrukcjami o powyższej formie. Oto, co robią:
print ma nazwę polecenia
p
i przyjmuje jeden argument. Wyświetla nazwę przekazywanej zmiennej i jej wartość (w systemie dziesiętnym) oddzieloną „=”, a następnie znakiem nowej linii. Zawsze jest oznaczany jako sukces .Na przykład program Prindeal
p _MyVariable_321 p screaming_hairy_armadillo
wyjdzie
_MyVariable_321 = 0 screaming_hairy_armadillo = 0
ponieważ wszystkie zmienne zaczynają się od 0. (Wymagane są spacje przed i po znaku równości).
increment ma nazwę polecenia
i
i przyjmuje jeden argument. Zwiększa wartość zmiennej przekazywanej o 1. Zawsze jest oznaczana jako sukces .Na przykład program
i alpaca p alpaca i alpaca p alpaca
wyjdzie
alpaca = 1 alpaca = 2
Zwróć uwagę, w jaki sposób
alpaca
zwiększano od 0 do 1, mimo że nigdy wcześniej nie był dostępny.dekrement ma nazwę polecenia
d
i przyjmuje jeden argument. Jeśli przekazywana zmienna jest niezerowa, jej wartość jest zmniejszana o 1, a instrukcja jest oznaczana jako sukces . Jeśli przekazana zmienna ma wartość 0, nic się nie dzieje, a instrukcja jest oznaczana jako błąd .Na przykład program
i malamute p malamute d malamute #success p malamute d malamute #failure p malamute d akita #failure p akita
wyjdzie
malamute = 1 malamute = 0 malamute = 0 akita = 0
Zauważ, że zmniejszenie wartości zmiennej o wartości 0 jest jedynym sposobem na wywołanie błędu .
Instrukcja alias i polecenia z aliasem
Komenda alias ma specjalną składnię i jest najsilniejsza, ponieważ może być używana do definiowania nowych komend. Nazwa polecenia alias to, a
a instrukcja alias ma postać:
a [name of new command]
[statement A]
[statement B]
[statement C]
Gdzie każdy [statement X]
reprezentuje dowolną nie- aliasową instrukcję, tj. Coś w formie [command name] [argument 1] [argument 2] [argument 3] ...
.
Aliasowana nazwa polecenia [name of new command]
może być dowolnym niepustym ciągiem znaków alfanumerycznych i znaków podkreślenia, który nie zaczyna się od cyfry - [a-zA-Z_][0-9a-zA-Z_]*
w wyrażeniu regularnym.
(Jest to ten sam zestaw nazw co zmienne, ale polecenia i zmienne aliasy są różnymi rzeczami używanymi w różnych miejscach . Zmienna może być nazwana tak samo jak polecenie bez negatywnych konsekwencji).
Po wykonaniu instrukcji aliasu nowe polecenie jest dodawane obok czterech oryginalnych p
i
d
a
poleceń. Nowa komenda może być używana jako instrukcja [command name]
in i wywoływana z argumentami, tak jak każda inna komenda inna niż alias .
Kiedy wykonywana jest instrukcja z aliasowaną nazwą polecenia, uruchamiane są dokładnie dwie kolejne instrukcje z oryginalnej instrukcji alias :
[statement A]
jest zawsze uruchamiany[statement B]
jest uruchamiany, jeśli[statement A]
był sukcesem[statement C]
jest uruchamiany, jeśli[statement A]
była awaria
Instrukcje A, B i C są zawsze uruchamiane leniwie , tzn. Są oceniane w locie w czasie ich uruchamiania.
Po zakończeniu wykonywania komenda aliasowana jest oznaczana z tą samą flagą sukcesu lub niepowodzenia co instrukcja B lub C, w zależności od tego, która z nich została wykonana . ( same instrukcje aliasów nie muszą być oflagowane, ponieważ nie mogą występować wewnątrz siebie).
Alias Przykład 1
Powiedzmy, że chcemy nowego polecenia, które zwiększy zmienną
frog
dwukrotnie. To oświadczenie aliasu osiąga to:a increment_frog_twice i frog i frog d frog
Instrukcja A (
i frog
) jest zawsze uruchamiana i zawsze oznaczana jako sukces, więc instrukcja B (i frog
) jest również zawsze uruchamiana, a zatem zmiennafrog
jest zwiększana o 2.increment_frog_twice
Polecenie jest zawsze oznaczane jako sukces, ponieważ instrukcja B jest zawsze uruchamiana, a B jest zawsze sukces . Instrukcja C (d frog
) nigdy nie jest uruchamiana.Więc wyjście do
a increment_frog_twice i frog i frog d frog p frog increment_frog_twice p frog
byłoby
frog = 0 frog = 2
Możemy uogólnić ten przykład, tak aby dowolną zmienną można zwiększyć dwukrotnie, podając argument aliasowanemu poleceniu.
W instrukcji aliasu dodatnie liczby całkowite 1, 2, 3 itd. Reprezentują argumenty 1, 2, 3 itd. Przekazane do polecenia aliasu. (Te argumenty mogą być zwykłymi zmiennymi lub odwołaniami do samych zmiennych.) Liczby te mogą pojawiać się tylko w argumentach instrukcji A, B i C w instrukcji aliasowej . Nie ma sensu, aby pojawiały się gdzie indziej.
Alias Przykład 2
Uogólnia to ostatni przykład - każda przekazana zmienna
increment_twice
zostanie zwiększona o 2, ponieważ1
jest to odwołanie do pierwszego przekazanego argumentu:a increment_twice i 1 i 1 d 1 #never reached p toad increment_twice toad p toad
Wyjście tego programu byłoby
toad = 0 toad = 2
Możemy wtedy dokonać aliasu innego polecenia, które przyjmuje dwa argumenty i wywołuje
increment_twice
oba z nich:a increment_twice i 1 i 1 d 1 #never reached a increment_both_twice increment_twice 1 increment_twice 2 d 1 #never reached increment_both_twice platypus duck p platypus p duck
Wynik byłby tutaj
platypus = 2 duck = 2
Ważne jest, aby zdawać sobie sprawę, że polecenia aliasy mogą być rekurencyjne, ponieważ na tym polega ich prawdziwa moc. Na przykład możemy wykonać polecenie, które ustawia dowolną zmienną przekazaną na 0:
Alias Przykład 3
set_to_zero
Polecenie przyjmuje jeden argument i ustawia zmienną na 0 i jest oznaczona jako sukces po zakończeniu:a set_to_zero d 1 set_to_zero 1 i _dummy_ i oryx i oryx i oryx p oryx set_to_zero oryx p oryx
Wyjście tego programu byłoby
oryx = 3 oryx = 0
To, co się dzieje, polega na tym, że po
set_to_zero oryx
uruchomieniud 1
pomyślnie zmniejsza sięoryx
z 3 do 2, a następnieset_to_zero 1
jest wywoływany, co jest równoznaczne zset_to_zero oryx
ponownym wywołaniem . Tak więc proces powtarza się, dopóki nie zakończyd 1
się niepowodzeniem , zatrzymując rekurencję i zwiększając wartość_dummy_
zmiennej, aby osiągnąć sukces .
Wyzwanie
Napisz program, który może wykonać kod Prindeal dokładnie tak, jak opisano powyżej. Pobierz kod Prindeal przez stdin, wiersz poleceń lub jako plik tekstowy. Wydrukuj wyniki programu Prindeal na standardowe wyjście lub najbliższą alternatywę dla twojego języka.
Alternatywnie możesz napisać funkcję, która pobiera kod jako ciąg znaków i wypisuje lub zwraca ciąg wyjściowy.
Dodatkowo możesz założyć, że:
- Wejściowy kod Prindeal będzie zawierał tylko znaki nowej linii i drukowalny kod ASCII oraz (opcjonalnie), że kończy się pustą linią.
- Kod wejściowy będzie prawidłowy Prindeal - dobrze uformowany i poprawny składniowo.
- Uruchomienie kodu nie wygeneruje nieskończonych pętli ani żadnych nieprawidłowych odwołań do poleceń, które nie zostały zdefiniowane lub argumentów, które nie zostały podane.
- Nazwy poleceń
p
,i
,d
, ia
nigdy nie będzie aliasem skończona. (Być może nie przyjąć, że zmienne nie będą miały te nazwy).
Nie ma również znaczenia, czy wartości zmiennych nie są liczbami całkowitymi o dokładności arbitralnej, ponieważ testowane będą tylko liczby mniejsze niż około 1000. Jest również w porządku, jeśli Twój język ma ograniczenia rekurencyjne (takie jak Python ), które mogą napotkać bardziej złożone programy Prindeal, o ile działa poniższy program testowy.
Program testowy
Oto duży program Prindeal, który buduje operacje dodawania, mnożenia i potęgowania za pomocą zmiennych zastępczych (zaczynając _
od konwencji) i wielu aliasów pomocniczych:
#Command Definitions:
a s #flag as a success
i _
d _
d _
a f #flag as a failure
d _
d _
d _
a z #1 = zero
d 1
z 1
s
a n #1 = one
z 1
i 1
s
a move #2 += 1, 1 = zero
moveH 1 2
move 1 2
s
a moveH #move helper
d 1
i 2
f
a dupe #2 += 1, 3 += 1, 1 = zero
dupeH1 1 2 3
dupe 1 2 3
s
a dupeH1 #dupe helper
d 1
dupeH2 2 3
f
a dupeH2 #dupe helper
i 1
i 2
s
a copy #2 = 1
z 2
copyH 1 2
s
a copyH #copy helper
dupe 1 2 _copy
move _copy 1
s
a addTo #1 += 2
copy 2 _add
#testing comments #
move _add 1#in weird places # just because #
s
#it's a g##d idea
###
a add #1 = 2 + 3
#its a good idea
z 1
addH 1 2 3
s
##
#
a addH #add helper
#this is a comment
addTo 1 2 #as is this
addTo 1 3
s
a mul #1 = 2 * 3
mulH1 1 2
mulH2 1 3
s
a mulH1 #mul helper
z 1
copy 2 _mul
s
a mulH2 #mul helper
mulH3 1 2
mulH2 1 2
s
a mulH3 #mul helper
d _mul
addTo 1 2
f
a mulBy #1 *= 2
mul _mulBy 1 2
copy _mulBy 1
s
a pow #1 = 2^3
powH1 1 3
powH2 1 2
s
a powH1 #pow helper
n 1
copy 2 _pow
s
a powH2 #pow helper
powH3 1 2
powH2 1 2
s
a powH3 #pow helper
d _pow
mulBy 1 2
f
#Running Tests:
p A
p B
p C
n A #A = 1
n B #B = 1
add C A B #C = A + B = 1 + 1 = 2
p ____
p A
p B
p C
add B A C #B = A + C = 1 + 2 = 3
p ____
p A
p B
p C
mul d B C #d = B * C = 3 * 2 = 6
p ____
p d
mulBy d B #d = d * B = 6 * 3 = 18
p ____
p d
d A #A = A - 1 = 1 - 1 = 0
mulBy d A #d = d * A = 18 * 0 = 0
p ____
p d
pow A C B #A = C ^ B = 2 ^ 3 = 8
p ____
p A
p B
p C
pow A B C #A = B ^ C = 3 ^ 2 = 9
p ____
p A
p B
p C
pow C A B #C = A ^ B = 9 ^ 3 = 729
p ____
p A
p B
p C
(Jeśli bawisz się tym kodem, pamiętaj, że wiele poleceń nie powiedzie się, jeśli ta sama zmienna zostanie podana wielokrotnie jako argument. Można to łatwo naprawić, ale wynikowy kod jest dłuższy.)
Twój tłumacz Prindeal powinien być w stanie wygenerować dokładny wynik:
A = 0
B = 0
C = 0
____ = 0
A = 1
B = 1
C = 2
____ = 0
A = 1
B = 3
C = 2
____ = 0
d = 6
____ = 0
d = 18
____ = 0
d = 0
____ = 0
A = 8
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 729
Punktacja
Najkrótszy kod w bajtach wygrywa. Tiebreaker przechodzi do wcześniejszego zgłoszenia.
Brownie Bonus: Napisz fajny program w Prindeal. Zaimplementowałem dodawanie i mnożenie, czy możesz odejmować lub dzielić?
p
, a następniep p
, co by wydrukowało 1, prawda?