Wskazówki do gry w golfa w Prologu

16

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.

Fatalizować
źródło
1
prologTag jest trochę bezużyteczne. O ile nie mamy wyzwania Interpret Prolog, nie potrzebujemy go.
kot

Odpowiedzi:

11

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 trybiename(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 argument A+B. Lub A-B+C*D:-...który staje się (A-B)+(C*D)tak, że jego pierwszy argument jest dopasowany do wzorca, A-Ba jego drugi jest dopasowany do wzorca C*D.

Przykłady

_+_+_.
A-B+C*D:-between(A,B,C),C+D.
\X:-X>1,X<10.
X+Y:-length(Y,X),member(X,Y).



5+[10,5,3,2,5],a+b+c,0-20+X*[2,4,6,5,40],\9.

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 phrasepredykató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

/ -->a+b+X,X+d+e.
A+B+C-->[A],[B],[C].


X/[],member(c,X),phrase(f+o+o,Y),+(b+a,r,Z,[]).

Wyjście będzie

X = [a, b, c, c, d, e],
Y = [f, o, o],
Z = [b, a, r].

Wypróbuj online!

Ostrzeżenia

Z jednymi operatorami +i -, Prolog będzie interpretował +20lub -20jako liczby zamiast połączenia z a +/1lub -/1predykatem. 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*Dnie zgadza się z danymi wejściowymi, Prolog spróbuje zadzwonić X+Y. Spowoduje to błąd, ponieważ będzie length/2wymagał Yliczby całkowitej, która nie będzie, ponieważ będzie w formie C*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.

0 '
źródło
3
To zadziwiające, jak przydatna jest ta sztuczka w golfa kodu. Właśnie zmniejszyłem liczbę bajtów odpowiedzi o 16%, korzystając tylko z tej wskazówki!
DLosc
7

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:

r([],Z,Z).
r([H|T],Z,R):-r(T,[H|Z],R).

W Code-golf możemy usunąć pierwszą regułę i dodać ;na końcu drugiej reguły, aby zakodować koniec rekurencji:

r([H|T],Z,R):-r(T,[H|Z],R);R=[H|Z].

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.

Fatalizować
źródło
7

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(member(A,B),C).
C = [member(A,B)|_] ? ;
C = [_,member(A,B)|_] ? ;
(etc.)

member(A,B) jest tylko nazwaną krotką w tej sytuacji i na zewnątrz member (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 memberlub pojedynczej postaci a, możemy zrobić coś takiego:

| ?- A = '-'('/'(1,2), '/'(3,4)).
A = 1/2-3/4

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 zastosowania isnie =; A = 1+2byłoby ujednolicenie Az 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:

| ?- A = '-'('-'(1,2), '-'(3,4)).
A = 1-2-(3-4)

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:

| ?- Q = '.'('.'(A,B),'.'(C,D)).
Q = [[A|B],C|D]

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:

[1,2,3]

można przekonwertować na ręczne przedstawienie przy użyciu takiej samej liczby znaków, jak ten:

x/3/2/1

zyskując przy tym tę zaletę, że [H|T]dopasowania do stylu w stylu można teraz pisać bardziej zwięźle jako T/H, a test na pustej liście jako po prostu xzamiast dłuższej []. (Oczywiście, to przychodzi z oczywistą wadę, że member, appenditp, nie będzie działać na tej reprezentacji).


źródło
+1! Nie ma potrzeby stosowania pojedynczych cytatów podczas korzystania z -i /. To już są całkowicie normalne atomy.
mat
I nie ma potrzeby stosowania pojedynczych cudzysłowów ., pod warunkiem, że następujące znaki nie są ani% ani układem.
false
6

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.

mata
źródło
2
Jest to zawsze coś, czego nienawidzę w SWI-Prolog, kiedy wykonuję tutaj wyzwania. Korzystanie z CLPFD sprawiłoby, że niektóre rzeczy byłyby znacznie czystsze i krótsze, ale musisz dodać dużo dodatkowego kodu, aby działał (samo włączenie jest dużo ...), co zwykle nie sprawia, że ​​warto. Myślę, że użyłem go tylko w tej odpowiedzi .
Fatalize
3
Ja też tego nie znoszę i na pewno nadejdzie czas, kiedy 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 ~/.swiplrci właśnie stanie, że używasz tego „Wariant” SWI-Prologu.
mat
5

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:2tak 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ę Mzawierającą pisownię cyfry w języku angielskim i francuskim, możesz zrobić coś takiego:

M=[0:'Zero':'Zéro',1:'One':'Un',2:'Two':'Deux', ... ]

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'in M, możesz:

member(Digit:Name:'Quatre',M).
Fatalizować
źródło
1
Możesz to uogólnić. Na przykład, możesz podłączyć [[1,2],[3,4]]jako 1*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.
mat
Listy podwójnych cytatów są jeszcze krótsze: "abc"zamiast[a,b,c]
fałszywe
5

Jedna fajna sztuczka: gdy musisz zawieść , użyj czegoś, co jest równoważne z  fałszem / 0 , ale krótszego, na przykład:

? - powtórz, writeln (cześć), 0 = 1 .
mata
źródło
3
Obowiązkowy cytat Art of Prolog : „ W programowaniu Prologu (być może w przeciwieństwie do życia w ogóle) naszym celem jest jak najszybsze niepowodzenie ”. Ciekawostka: można również użyć \+!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.
Fatalize
Zastanawiałem się też nad tym cytatem, kiedy to napisałem ;-)
mat
2
Naprawdę potrzebujemy obu wersji, \+!kleje po lewej stronie do innych postaci graficznych, podczas gdy 0=1kleje po lewej stronie do nazw.
false
5

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.

rdzeń rdzeniowy
źródło