Советы по игре в гольф в Прологе


16

Какие общие советы у вас есть для игры в гольф в Прологе? Я ищу идеи, которые могут быть применены к задачам по коду в целом, которые, по крайней мере, несколько специфичны для Пролога (например, переменные из одной буквы не специфичны для Пролога для уменьшения размера программ).

Пожалуйста, укажите в ваших советах, является ли это специфическим для реализации Пролога (например, встроенные модули SWI-Prolog)

Пожалуйста, оставляйте только один совет на ответ или список советов, которые тесно связаны с одной и той же основной идеей.


1
prologТег любопытное бесполезно. Если у нас нет задачи Interpret Prolog, она нам не нужна.
кот

Ответы:


10

Используйте операторы для имен предикатов

Можно указывать операторы предикатов в качестве имен, если оператор является одним из предопределенных операторов (перечисленных здесь ) и еще не определен в качестве предиката. Это экономит несколько байтов как при определении, так и при вызове предиката, поскольку предикаты операторов не нужно записывать в нормальной 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].

Попробуйте онлайн!

Предостережения

С унарными операторами +и -, Пролог будет интерпретировать +20или -20как числа вместо вызова предиката +/1или -/1. Предикаты с одинарными +или именными -именами можно вызывать по номеру с помощью скобок ( +(20), -(20)). Если желательно избегать лишних байтов из скобок, в качестве имен можно использовать другие унарные операторы, такие как \, $и т. Д.

Сочетание сопоставления с образцом и предикатов с именами операторов не лишено недостатков. Если у вас есть два предиката с тем же оператором, что и их имя, и с сопоставлением с шаблоном, один из них является строго более общим, чем другой, то более общий из них может быть вызван первым или если произойдет сбой менее общего (в зависимости от их порядка в источнике) , Например, в приведенном выше примере, если A-B+C*Dне удается сопоставить его входные данные, то Prolog попытается вызвать X+Y. Это приведет к ошибке, потому что length/2требуется Yцелое число, которого не будет, поскольку оно будет в форме C*D. Этого можно избежать, просто убедившись, что никакие два предиката не имеют такой же оператор, как их имя, или если это не удастся, то используйте сокращения и тщательное упорядочение источника.


3
Удивительно, насколько полезен этот трюк для игры в гольф. Я только уменьшил количество байтов ответа на 16%, используя только этот совет!
DLosc

6

Постарайтесь объединить все возможные случаи в одно правило.

Чистый способ программирования на Прологе - объявить несколько правил для одного и того же предиката. Например, предикат для обращения к списку с помощью аккумулятора будет выглядеть следующим образом:

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 пусто, т. Е. Если рекурсия должна завершиться, и, таким образом, мы можем добавить наше завершение в качестве предложения or после него.

Тот же принцип работает во многих ситуациях. Однако обратите внимание, что иногда на самом деле короче объявлять другое правило, чем делать это.


6

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

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


2
Это всегда то, что я ненавижу с SWI-Прологом, когда выполняю задачи здесь. Использование CLPFD сделало бы некоторые вещи намного чище и короче, но вы должны добавить много дополнительного кода, чтобы заставить его работать (само по себе включение очень много ...), что обычно не стоит того. Я думаю, что только когда-либо использовал это в этом ответе .
Fatalize

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

6

Используйте арифметические операторы как конструкторы кортежей и пары минусов

Если вам нужно передать одну структуру, состоящую из двух или более значений, наиболее очевидная вещь - это список, например [A,B] . Это действительно многословно, хотя.

Есть альтернатива. Значения Пролога могут хранить в значительной степени произвольную вложенную структуру, которая не оценивается. Вот пример, показывающий, как это работает:

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

member(A,B) это просто именной кортеж в этой ситуации, а снаружи member объект (который является вызовом функции) рассматривает его как таковой.

Хотя именованные кортежи довольно полезны в прологическом программировании без игры в гольф, они могут показаться даже более многословными, чем подход списка. Тем не менее, мы можем использовать произвольные символы в имени конструктора кортежей (при условии, что они правильно указаны в кавычках); вместо чего-то милого 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) ), вы можете бесплатно заменить несколько аргументов предиката на кортежи, что означает, что если предикату нужно передать два или более своих аргументов другому предикату, вы часто можете сформировать их в кортеж и просто передайте кортеж (хотя для этого потребуется изменить все вызовы предиката, в дополнение к самому предикату, чтобы использовать соответствующее сочетание конструкторов кортежа и запятых).

Еще одним преимуществом этого синтаксиса является необходимость использования списков для внутреннего использования (а не для взаимодействия со стандартными предикатами); список - это просто набор вложенных cons-ячеек, а cons-ячейка - это просто кортеж с конструктором '.', как можно увидеть здесь:

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

Если ваш код использует списки «вручную», имеет смысл использовать менее громоздкий конструктор кортежей, чем '.'. Обычный выбор для меня состоит в том, чтобы представлять ячейку cons как '/'(Tail,Head)(потому что она является наиболее читаемой, которую вы можете получить в отладочном выводе без потери символов). Обратите внимание, что вы, вероятно, []тоже захотите свой собственный эквивалент; вы могли бы использовать[] но это два байта длиной, и есть много однобайтовых атомов (все строчные буквы), которые вы можете использовать вместо этого.

Так, например, следующий список:

[1,2,3]

может быть преобразовано в ручное представление в том же количестве символов, как это:

x/3/2/1

в то же время, получая преимущество, что [H|T]сопоставление с шаблоном-стиля теперь может быть записано более кратко как T/Hи проверка пустого списка как более точного , xчем более длинного []. (Конечно, это приходит с очевидным недостатком , что member, appendи т.д., не будет работать на этом представлении.)


+1! Нет необходимости использовать одинарные кавычки при использовании -и /. Это уже совершенно нормальные атомы.
коврик

И нет необходимости в одинарных кавычках ., при условии, что следующие символы не являются %ни макетом.
неверно

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'in 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
Обязательная цитата «Искусство Пролога» : « В программировании на Прологе (в отличие, возможно, от жизни в целом) наша цель - потерпеть неудачу как можно быстрее ». Интересный факт: вы можете также использовать \+!как 3 байта странный путь к провалу , который фактически не вызывает сокращение !(см это для почему ). Я не думаю, что возможно потерпеть неудачу менее чем за 3 байта.
Fatalize

Я также думал об этой цитате, когда писал это ;-)
mat

2
Нам действительно нужны обе версии: \+!склеивание слева от других графических символов, тогда как 0=1склеивание слева для имен.
неверно

5

Повторно использовать предикат с разными режимами вызова

Например, вы можете проанализировать и распечатать структуру с тем же предикатом, один раз с переменным аргументом, а другой раз с наземным термином. Я использовал этот подход в «Поцелуе растягивающихся змей» . Конечно, это возможно не во всех проблемах.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.