Поради щодо гольфу в Пролозі


16

Які загальні поради щодо гольфу в Пролозі? Я шукаю ідеї, які можна застосувати для кодування проблем із гольфом взагалі, які принаймні дещо специфічні для Prolog (наприклад, зміна однієї літери не характерна для Prolog для зменшення розміру програм).

Будь-ласка, вкажіть у своїх підказках, чи стосується вона конкретної реалізації Prolog (наприклад, вбудовані програми SWI-Prolog)

Будь ласка, опублікуйте лише одну пораду на відповідь або список підказок, які всі тісно пов’язані з однією головною ідеєю.


1
prologТег цікаве марно. Якщо у нас є виклик Interpret Prolog, він нам не потрібен.
кіт

Відповіді:


11

Використовуйте оператори для предикатних імен

Можна вказати оператори предикатів як імена, якщо оператор є одним із заздалегідь визначених операторів (перерахований тут ) і ще не визначений як предикат. Це заощаджує кілька байтів як при визначенні, так і при виклику предиката, оскільки предикати операторів не потрібно писати у звичайній name(arg1,arg2,etc..)формі і їх можна викликати так, як можна було очікувати з операторами.

Для одного та двох аргументів-аргументів вони можуть давати імена унарних та двійкових операторів відповідно. Для більш високих аркадних предикатів ми все ще можемо уникати дужок, використовуючи відповідність шаблонів. Наприклад, якщо у нас є предикат A+B+C:-..., Prolog використовує правила його пріоритетності та асоціативності, щоб перетворити його в (A+B)+C:-...предикат оператора, де узгоджений перший аргумент A+B. Або A-B+C*D:-...що стає (A-B)+(C*D)таким, що його перший аргумент узгоджений, A-Bа другий - шаблон C*D.

Приклади

_+_+_.
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.

Вихід буде X = 5.

Спробуйте в Інтернеті!

Наслідок

Оскільки DCG є синтаксичним цукром для предикатів, їм теж можна давати оператори для імен. Це працює, як очікувалося, коли викликає їх як DCG або з DCG або використовує phraseпредикати або інші, призначені для роботи з DCG. При виклику їх як предикатів потрібні дужки (наприклад, їх A+B-->...потрібно називати подібними +(A,B,...)), оскільки предикати DCG беруть додаткові два аргументи для їх списків відмінностей. Для операторів з ім'ям DCG з більш ніж двома аргументами, що використовують узгодження шаблону оператора, тоді важливо переконатися, коли викликає його як предикат, що оператори, які відповідають шаблону, розподілені правильно.

Надання імен операторів DCG, які не беруть додаткових аргументів, може бути корисним, якщо вам потрібно викликати їх у межах вашої програми з тих пір, ви можете це робити без використання дужок. Необхідна обережність, оскільки може статися так, що те, що ви зберігаєте в дужках, ви можете втратити на доданий інтервал, необхідний для розбору суміжних операторів.

Приклади

/ -->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,[]).

Вихід буде

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

Спробуйте в Інтернеті!

Коваджі

З одинарними операторами +і -, Prolog буде інтерпретувати +20або -20як числа замість виклику до +/1або -/1предиката. Предикати, задані одинарними +або -як імена, все ще можна викликати за номером за допомогою круглих дужок ( +(20), -(20)). Якщо уникнути додаткових байтів з дужок бажані інші унарні оператори , такі як \, $і т.д. можуть бути використані в якості імен замість цього.

Поєднання відповідності шаблонів та предикатів з іменем оператора не зовсім без недоліків. Якщо у вас є два предикати, які мають одного і того ж оператора, як їх ім'я, і ​​один з узорним шаблоном є строго більш загальним, ніж інший, тоді більш загальний з них може бути викликаний першим або якщо менш загальний невдалий (залежно від їх упорядкування в джерелі) . Наприклад , у наведеному вище прикладі , якщо A-B+C*Dне буде відповідати його вхід , то Пролог намагатиметься покликанням X+Y. Це призведе до помилки, оскільки length/2потрібно Yбути цілим числом, якого не буде, оскільки воно буде у формі C*D. Цього можна уникнути, просто переконавшись, що жодне з двох предикатів не має одного оператора, як їх ім’я, або якщо це не вдається, використовуючи скорочення та ретельне впорядкування джерела.


3
Дивно, наскільки корисний цей трюк для кодового гольфу. Я просто скоротив кількість відповідей на 16%, використовуючи лише цей підказку!
DLosc

7

Постарайтеся поставити всі можливі випадки в єдине правило

Чистим способом програмування в Prolog є оголошення кількох правил для одного предиката. Наприклад, предикат для зворотного переліку списку за допомогою акумулятора виглядатиме так:

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

У Code-golf ми можемо видалити перше правило та додати ; кінці другого правила, щоб кодувати кінець рекурсії:

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

Ми знаємо, що перша умова r(T,[H|Z],R)провалиться, якщо T порожній, тобто якщо рекурсія повинна закінчитися, і, таким чином, ми можемо додати своє припинення як або після нього.

Цей же принцип працює у багатьох ситуаціях. Однак зауважте, що іноді буває коротше оголосити інше правило, а не робити це.


7

Використовуйте арифметичні оператори як кортежні конструктори і мінуси

Якщо вам потрібно передати одну структуру, що складається з двох або більше значень, найбільш очевидним, що слід використовувати, є список, наприклад [A,B] . Це справді багатослівно.

Є альтернатива. Значення Prolog можуть зберігати досить багато довільної вкладеної структури, яка не оцінюється. Ось приклад, який показує, як це працює:

| ?- member(member(A,B),C).
C = [member(A,B)|_] ? ;
C = [_,member(A,B)|_] ? ;
(etc.)

member(A,B)є лише названим кортежем у цій ситуації, і зовні member(що є викликом функції) трактується як такий.

Хоча названі кортежі досить корисні в програмі Prolog, що не використовується для гольфу, вони можуть здатися ще більш багатослівними, ніж підхід до списку. Однак ми можемо використовувати досить багато довільних символів в імені конструктора кортежів (якщо припустимо, що вони цитуються належним чином); замість чогось подібного милого memberабо одного персонажа, як aми, можемо зробити щось подібне:

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

Ось наші конструктори кортежів є '-'і '/'. І цікаво відзначити, що зробив з ними симпатичний принтер; він використовує позначення інфіксів для кортежів. Це дійсно коротко і аналізує так само, як і порівняльна арифметична операція. (Це також пояснює, чому використання арифметики isне використовується =; A = 1+2було б уніфіковано Aз кортежем '+'(1,2) , тому потрібен окремий синтаксис, щоб фактично оцінити неоцінений арифметичний вираз.) Оскільки конструктор кортежу повинен називатися чимось, , ви можете також використовувати символ, який має терс синтаксис (і як бонус, -і/є одним із найпоширеніших варіантів у коді, який не використовується для гольфу, коли вони хочуть швидкого конструктора, що викидається, а не чогось значущого, майже таким же чином, якийi часто використовується як циклічна змінна, тому їх цілком розумно використовувати при введенні та виведенні, якщо вам здається, що з якоїсь причини потрібен кортеж).

'-'і '/'є гарним вибором для кортежних конструкторів, оскільки вони мають добре поводитися та корисні переваги, дозволяючи вам писати кортежні букви буквально. Однак зауважте, що вам не потрібно турбуватися про перевагу, коли проміжні значення виробляються всередині програми. Prolog зберігає кортежі, які зберігаються як дерево, а не як вихідний код, і симпатичні принтери можуть виводити його однозначно:

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

Тому що синтаксис кортежу настільки лаконічний ( f(A,B)не коротший заf(A-B) ), ви можете безкоштовно замінити кілька аргументів передбачення кортежами, тобто якщо предикату потрібно передати два чи більше його аргументів іншому предикату, ви можете часто їх формувати кортеж і просто передати кортеж (хоча це потребує зміни всіх викликів до присудка, крім самого присудка, використовувати відповідну суміш конструкторів кортежу та коми).

Ще одна перевага цього синтаксису полягає в тому, що вам потрібно використовувати списки внутрішньо (а не взаємодіяти зі стандартними предикатами); список - це лише набір вкладених комірок проти, а комірка проти - це лише кортеж із конструктором '.', як це видно тут:

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

Якщо ваш код використовує списки "вручну", то може використовувати багато сенсу використовувати менш об'ємний конструктор кортежів, ніж '.'. Поширений вибір для мене полягає в тому, щоб представити мінусову клітинку як '/'(Tail,Head)(бо мова йде про найбільш читабельний, який ви можете отримати у виведенні налагодження, не витрачаючи символів). Зауважте, що ви, ймовірно, хочете і власного []еквівалента; ви могли б використовувати[] але це два байти, і є багато однобайтових атомів (усіх малих літер), які ви можете використовувати замість цього.

Так, наприклад, наступний список:

[1,2,3]

можна перетворити в ручне подання у такій же кількості символів, як ця:

x/3/2/1

при цьому отримуючи перевагу в тому, що [H|T]поєднання шаблону стилю тепер можна писати більш коротко T/H, а тест проти порожнього списку як раз, xа не довший []. (Звісно, це приходить з очевидним недоліком , що member, appendі т.д., не буде працювати на цій виставі.)


+1! Немає необхідності в одинарних котируваннях при використанні -та /. Це вже абсолютно нормальні атоми.
мат

І немає необхідності в одинарних цитатах за .умови, що наступні символи не є %ні анотацією.
фальшивий

6

Один фокус, який часто корисний: використовуйте обмеження CLP (FD) для цілої арифметики, щоб отримати предикати, які можна автоматично використовувати в декількох напрямках, таким чином уникаючи умов та виділених гілок та варіантів.

Використовуйте B-Prolog або GNU Prolog, де такі обмеження доступні поза коробкою, не потребуючи завантаження будь-яких бібліотек.


2
Це завжди те, що я ненавиджу з SWI-Prolog, коли роблю тут проблеми. Використання CLPFD зробить деякі речі набагато чистішими та коротшими, але вам доведеться додати багато додаткового коду, щоб він працював (включення саме по собі багато ...), що зазвичай не робить цього вартим. Я думаю, я лише коли-небудь використовував це у цій відповіді .
Фаталізувати

3
Я теж ненавиджу це, і, звичайно, прийде час, коли library(clpfd)він буде доступний як попередньо завантажена або принаймні автозавантажена бібліотека також у SWI-Prolog. Може пройти кілька років, поки декларативна арифметика буде повністю зрозуміла та оцінена всіма користувачами, які вже накопичили десятиліття досвіду застарілих функцій низького рівня. Чим більше ви використовуєте та підтримуєте CLP (FD), тим швидше ми отримаємо його за замовчуванням. До цього часу ви можете просто ввести :- use_module(library(clpfd)).свій ~/.swiplrcі просто заявити, що ви використовуєте той "варіант" SWI-Prolog.
мат

5

Коротший синтаксис списків списків та спосіб оголошення карт

Ви можете зберігати байти у списках списків. Якщо у вас є список [[1,2],[3,4]], ви можете фактично оголосити його як [1:2,3:4], що зберігає 4 дужки = 4 байти. Зауважте, що ви можете використовувати щось інше, ніж :(наприклад, ^).

1:2насправді не є списком у цьому випадку (тоді як [1,2]був), він представляється внутрішньо як :(1,2). Тому ви не можете використовувати предикати, які працюють у списках у тих списках, що використовують колонки.

Цей трюк в основному використовується для оголошення карт, тобто списку ключів із доданими до них значеннями. Наприклад, якщо ви хочете оголосити карту, Mяка містить написання цифри англійською та французькою мовами, ви можете зробити щось подібне:

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

Потім ви можете, наприклад, отримати елементи карти зі вбудованим присудком member/2. Наприклад, якщо ви хочете відповідати цифрі та англійському слову 'Quatre'в M, ви можете зробити:

member(Digit:Name:'Quatre',M).

1
Ви можете узагальнити це. Наприклад, ви можете wirte [[1,2],[3,4]]як 1*2+3*4, що є, +(*(1,2),*(3,4))і, таким чином, також використовувати лише один байт, де вам інакше знадобиться два, для відкриття та закриття дужок.
мат

Подвійні списки цитат ще коротші: "abc"замість[a,b,c]
помилковий

5

Один акуратний трюк: коли вам потрібно помилитися , використовуйте щось, що еквівалентно  false / 0 , але коротше, наприклад:

? - повторити, Writeln (привіт), 0 = 1 .

3
Обов'язкове цитування « Мистецтво Пролога» : « У програмуванні Prolog (на відміну від, мабуть, у цілому життя) наша мета - провалити якомога швидше ». Цікавий факт: ви можете також використовувати \+!як 3 байта дивний шлях до провалу , який фактично не викликає скорочення !(див це для чому ). Я не думаю, що неможливо вийти з ладу менше ніж за 3 байти.
Фаталізувати

Я також думав про цю цитату, коли писав це ;-)
мат

2
Нам дійсно потрібні обидві версії, \+!склеюємо зліва для інших графічних символів, тоді як 0=1клеї ліворуч для імен.
помилково

5

Використовуйте повторно присудок у різних режимах виклику

Наприклад, ви можете проаналізувати та надрукувати структуру з тим самим предикатом, один раз із аргументом змінної та іншим разом із основним терміном. Я використовував цей підхід у поцілунку "Здійснюють зміїних змій" . Звичайно, це неможливо у всіх викликах.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.