Jakie masz ogólne wskazówki na temat gry w golfa w Prologu? Szukam pomysłów, które można by zastosować do ogólnych problemów z golfem, które są przynajmniej nieco specyficzne dla Prologa (np. Zmienne jednoliterowe nie są specyficzne dla Prologa w celu zmniejszenia rozmiaru programów).
Proszę wskazać w swoich wskazówkach, czy jest to specyficzne dla implementacji Prologu (np. Wbudowane specyficzne dla SWI-Prologu)
Proszę zamieścić tylko jedną wskazówkę na odpowiedź lub listę wskazówek, które są ściśle związane z tym samym głównym pomysłem.
prolog
Tag jest trochę bezużyteczne. O ile nie mamy wyzwania Interpret Prolog, nie potrzebujemy go.Odpowiedzi:
Użyj operatorów dla nazw predykatów
Możliwe jest nadanie operatorom predykatów nazw, pod warunkiem, że operator jest jednym z predefiniowanych operatorów (wymienionych tutaj ) i nie jest jeszcze zdefiniowany jako predykat. Oszczędza to kilka bajtów zarówno podczas definiowania, jak i wywoływania predykatu, ponieważ predykaty operatora nie muszą być zapisywane w normalnym trybie
name(arg1,arg2,etc..)
formie i można je wywoływać tak, jak można oczekiwać od operatorów.Dla predykatów jednego i dwóch argumentów można podać nazwy operatorów jednoargumentowych i binarnych. W przypadku predykatów o wyższej aryczności możemy nadal unikać nawiasów za pomocą dopasowania wzorca. Na przykład, jeśli mamy predykat
A+B+C:-...
, Prolog użyje reguł pierwszeństwa operatora i reguł asocjatywności, aby przekształcić go w(A+B)+C:-...
predykat operatora, w którym dopasowany jest pierwszy argumentA+B
. LubA-B+C*D:-...
który staje się(A-B)+(C*D)
tak, że jego pierwszy argument jest dopasowany do wzorca,A-B
a jego drugi jest dopasowany do wzorcaC*D
.Przykłady
Wyjście będzie
X = 5.
Wypróbuj online!
Następstwo
Ponieważ DCG są cukrami składniowymi dla predykatów, one również mogą otrzymać operatorów nazw. Działa to zgodnie z oczekiwaniami, nazywając je DCG z DCG lub przy użyciu
phrase
predykatów lub innych, które są przeznaczone do pracy z DCG. Podczas wywoływania ich jako predykatów wymagane są nawiasy (np.A+B-->...
Muszą być wywoływane jak+(A,B,...)
), ponieważ predykaty DCG przyjmują dodatkowe dwa argumenty dla swoich list różnic. W przypadku operatora o nazwie DCG z więcej niż dwoma argumentami używającymi dopasowywania wzorca operatora, ważne jest, aby upewnić się, nazywając go jako predykat, że operatorzy dopasowani do wzorca są poprawnie dystrybuowani.Nadawanie nazw operatorom DCG, które nie przyjmują dodatkowych argumentów, może być przydatne, jeśli trzeba wywoływać je w programie, ponieważ można to zrobić bez użycia nawiasów. Konieczna jest ostrożność, ponieważ może się zdarzyć, że to, co zaoszczędzisz w nawiasach, możesz stracić, dodając odstępy wymagane do parsowania sąsiednich operatorów.
Przykłady
Wyjście będzie
Wypróbuj online!
Ostrzeżenia
Z jednymi operatorami
+
i-
, Prolog będzie interpretował+20
lub-20
jako liczby zamiast połączenia z a+/1
lub-/1
predykatem. Predykaty podane jako jednoargumentowe+
lub-
jako imiona można nadal wywoływać na numer za pomocą nawiasów (+(20)
,-(20)
). Jeśli unikanie dodatkowych bajtów z nawiasów jest pożądane, inne jednoargumentowe operatory, takie jak\
:$
itp mogą być używane jako nazwy zamiast.Połączenie dopasowania wzorca i predykatów nazwanych przez operatora nie jest całkowicie pozbawione wad. Jeśli masz dwa predykaty, które mają ten sam operator co ich nazwa i przy dopasowaniu wzorca jeden jest ściśle bardziej ogólny niż drugi, to bardziej ogólny może zostać wywołany jako pierwszy lub jeśli mniej ogólny zawodzi (w zależności od ich kolejności w źródle) . Na przykład w powyższym przykładzie, jeśli
A-B+C*D
nie zgadza się z danymi wejściowymi, Prolog spróbuje zadzwonićX+Y
. Spowoduje to błąd, ponieważ będzielength/2
wymagałY
liczby całkowitej, która nie będzie, ponieważ będzie w formieC*D
. Można tego uniknąć, po prostu upewniając się, że żadne dwa predykaty nie mają tego samego operatora jako nazwy, a jeśli to się nie powiedzie, należy zastosować cięcia i staranne uporządkowanie źródła.źródło
Spróbuj umieścić każdą możliwą sprawę w jednej regule
Prostym sposobem programowania w Prologu jest zadeklarowanie wielu reguł dla tego samego predykatu. Na przykład predykat do odwrócenia listy za pomocą akumulatora wygląda następująco:
W Code-golf możemy usunąć pierwszą regułę i dodać
;
na końcu drugiej reguły, aby zakodować koniec rekurencji:Wiemy, że pierwszy warunek
r(T,[H|Z],R)
nie powiedzie się, jeśli T będzie pusty, tj. Jeśli rekursja się skończy, dlatego możemy dodać nasze zakończenie jako klauzulę lub klauzulę po nim.Ta sama zasada działa w wielu sytuacjach. Zauważ jednak, że czasami krótsze jest zadeklarowanie innej reguły niż zrobienie tego.
źródło
Użyj operatorów arytmetycznych jako konstruktorów krotek i par wad
Jeśli potrzebujesz przekazać pojedynczą strukturę składającą się z dwóch lub więcej wartości, najbardziej oczywistą rzeczą jest użycie listy, np
[A,B]
. Ale to naprawdę gadatliwe.Istnieje alternatywa. Wartości Prolog mogą przechowywać prawie dowolną zagnieżdżoną strukturę, która nie jest oceniana. Oto przykład pokazujący, jak to działa:
member(A,B)
jest tylko nazwaną krotką w tej sytuacji i na zewnątrzmember
(które jest wywołaniem funkcji), traktuje to jako takie.Chociaż nazwane krotki są dość przydatne w programowaniu Prologa bez gry w golfa, mogą wydawać się bardziej szczegółowe niż podejście oparte na liście. W nazwie konstruktora krotek możemy jednak używać dowolnych znaków (przy założeniu, że są odpowiednio cytowane); zamiast czegoś uroczego
member
lub pojedynczej postacia
, możemy zrobić coś takiego:Tutaj naszymi konstruktorami krotek są
'-'
i'/'
. Ciekawe jest to, co zrobiła z nimi ładna drukarka; to za pomocą Infix notacji dla krotek. To jest naprawdę zwięzłe i analizuje w taki sam sposób, jak zrobiłaby to porównywalna operacja arytmetyczna. (To również wyjaśnia, dlaczego arytmetyczne zastosowaniais
nie=
;A = 1+2
byłoby ujednolicenieA
z krotki'+'(1,2)
, potrzebna jest więc osobne składnia faktycznie ocenić unevaluated wyrażenia arytmetycznego.) Ponieważ konstruktor krotki musi być nazywany coś , równie dobrze możesz użyć znaku, który ma zwięzłość składnia (i jako bonus-
oraz/
są jednymi z najczęstszych wyborów w kodzie innym niż golfowy, gdy chcą raczej szybkiego konstruktora krotek zamiast czegoś znaczącego, w ten sam sposób, któryi
jest często używany jako zmienna pętli, więc jest całkowicie rozsądny do użycia w danych wejściowych i wyjściowych, jeśli z jakiegoś powodu chcesz mieć krotkę).'-'
i'/'
są dobrym wyborem dla konstruktorów krotek, ponieważ mają dobrze zachowane i przydatne pierwszeństwo, pozwalając ci na krótkie pisanie krotek. Należy jednak pamiętać, że nie trzeba się martwić o pierwszeństwo, gdy wartości pośrednie są generowane w programie. Prolog przechowuje krotki przechowywane jako drzewo, a nie jako kod źródłowy, a ładne drukarki mogą je wypisać jednoznacznie:Ponieważ składnia krotki jest tak zwięzła (
f(A,B)
nie jest krótsza niżf(A-B)
), możesz zastąpić wiele argumentów predykatowych krotkami bez żadnych kosztów, co oznacza, że jeśli predykat musi przekazać dwa lub więcej swoich argumentów do innego predykatu, często możesz je uformować w krotkę i wystarczy przekazać krotkę (chociaż będzie to wymagało zmiany wszystkich wywołań do predykatu, oprócz samego predykatu, w celu użycia odpowiedniej kombinacji konstruktorów krotek i przecinków).Kolejną zaletą tej składni jest to, że trzeba używać list wewnętrznie (zamiast współdziałać ze standardowymi predykatami); lista jest w zasadzie tylko zestawem zagnieżdżonych komórek Cons, a komórka Cons to tylko krotka z konstruktorem
'.'
, jak widać tutaj:Jeśli Twój kod używa list „ręcznie”, sensowne może być użycie mniej rozbudowanego konstruktora krotek niż
'.'
. Częstym wyborem jest dla mnie reprezentowanie komórki przeciwnej jako'/'(Tail,Head)
(ponieważ jest to najbardziej czytelny wynik debugowania bez marnowania znaków). Zauważ, że prawdopodobnie będziesz także chciał mieć swój własny[]
odpowiednik; Państwo mogli korzystać[]
, ale to dwa bajty długości i istnieje wiele atomów jedno-bajtowych (wszystkie małe litery), które można używać zamiast.Na przykład następująca lista:
można przekonwertować na ręczne przedstawienie przy użyciu takiej samej liczby znaków, jak ten:
zyskując przy tym tę zaletę, że
[H|T]
dopasowania do stylu w stylu można teraz pisać bardziej zwięźle jakoT/H
, a test na pustej liście jako po prostux
zamiast dłuższej[]
. (Oczywiście, to przychodzi z oczywistą wadę, żemember
,append
itp, nie będzie działać na tej reprezentacji).źródło
-
i/
. To już są całkowicie normalne atomy..
, pod warunkiem, że następujące znaki nie są ani%
ani układem.Jedna sztuczka, która jest często przydatna: Użyj ograniczeń CLP (FD) dla arytmetyki liczb całkowitych, aby uzyskać predykaty, które można automatycznie stosować w kilku kierunkach, unikając w ten sposób warunków oraz dedykowanych gałęzi i wariantów.
Użyj B-Prolog lub GNU Prolog, jeśli takie ograniczenia są dostępne od razu, bez konieczności ładowania bibliotek.
źródło
library(clpfd)
będzie dostępny jako wstępnie załadowana lub przynajmniej automatycznie ładowana biblioteka również w SWI-Prolog. Może upłynąć kilka lat, zanim deklaratywna arytmetyka zostanie w pełni zrozumiana i doceniona przez wszystkich użytkowników, którzy do tej pory zgromadzili dziesięciolecia doświadczenia z przestarzałymi funkcjami niskiego poziomu. Im więcej korzystasz i popierasz CLP (FD), tym szybciej otrzymamy go domyślnie. Do tego czasu, można po prostu umieścić:- use_module(library(clpfd)).
w swoim~/.swiplrc
i właśnie stanie, że używasz tego „Wariant” SWI-Prologu.Krótsza składnia list list i sposób deklarowania map
Możesz zapisać bajty na listach list. Jeśli masz listę
[[1,2],[3,4]]
, możesz ją zadeklarować jako[1:2,3:4]
, co oszczędza 4 nawiasy = 4 bajty. Pamiętaj, że możesz użyć czegoś innego niż:
(na przykład^
).1:2
tak naprawdę nie jest listą w tym przypadku (podczas gdy[1,2]
była), jest reprezentowana wewnętrznie jako:(1,2)
. Dlatego nie można używać predykatów, które działają na listach na tych podlistach, które używają dwukropków.Ta sztuczka służy głównie do deklarowania map, tj. Listy kluczy z dołączonymi wartościami. Na przykład, jeśli chcesz zadeklarować mapę
M
zawierającą pisownię cyfry w języku angielskim i francuskim, możesz zrobić coś takiego:Możesz na przykład pobrać elementy mapy z wbudowanym predykatem, takim jak
member/2
. Na przykład, jeśli chcesz cyfrę i angielskie słowo odpowiadające'Quatre'
inM
, możesz:źródło
[[1,2],[3,4]]
jako1*2+3*4
, który jest,+(*(1,2),*(3,4))
a zatem również użyć tylko jednego bajtu, w którym w innym przypadku potrzebujesz dwóch, do otwierania i zamykania nawiasów."abc"
zamiast[a,b,c]
Jedna fajna sztuczka: gdy musisz zawieść , użyj czegoś, co jest równoważne z fałszem / 0 , ale krótszego, na przykład:
źródło
\+!
jako 3 bajty dziwny sposób na niepowodzenie, które w rzeczywistości nie powoduje cięcie!
(zobacz ten dla dlaczego ). Nie sądzę, że można zawieść w mniej niż 3 bajty.\+!
kleje po lewej stronie do innych postaci graficznych, podczas gdy0=1
kleje po lewej stronie do nazw.Ponownie użyj predykatu z różnymi trybami połączeń
Na przykład można analizować i drukować strukturę z tym samym predykatem, raz z argumentem zmiennej, a innym razem z terminem podstawowym. Zastosowałem to podejście w Make the Stretchy Snakes Kiss . Oczywiście nie jest to możliwe we wszystkich wyzwaniach.
źródło