Определение правила, которое пользователь не может запросить
Как определить правило, которое пользователь не может запросить? Я только хочу, чтобы сама программа вызывала это правило через другое правило.
Пример:
правило1():- правило2 ().
rule2 (): - 1<5.
?-Rule1().
правда
?-Rule2 ().
(Я не знаю, каков будет ответ, я просто хочу, чтобы этот запрос не прошел!)
3 ответа
Используйте объект Logtalk для инкапсуляции ваших предикатов. Только предикаты, которые вы объявляете общедоступными, могут быть вызваны (вне объекта). Модули Prolog не препятствуют вызову каких-либо предикатов, так как при использовании квалификации explcit обходится список явно экспортированных предикатов.
Простой пример:
:- object(rules).
:- public(rule1/1).
rule1(X) :-
rule2(X).
rule2(X) :-
X < 5.
:- end_object.
После компиляции и загрузки объекта выше:
?- rules::rule1(3).
true.
?- rules::rule2(3).
error(existence_error(predicate_declaration,rule2(3)),rules::rule2(3),user)
Если вы отредактируете объектный код и явно объявите rule2/1 как private, вы получите вместо этого ошибку:
?- rules::rule2(3).
error(permission_error(access,private_predicate,rule2(3)),rules::rule2(3),user)
Больше информации и множество примеров на http://logtalk.org/
Сначала несколько заметок:
Я думаю, что вы имеете в виду "предикат" вместо "правило". Предикат является
name/k
такая вещь, какhelp/0
(а такжеhelp/1
другое) и может иметь несколько положений, в том числе факты и правила, например,length([], 0).
(факт) иlength([H|T], L) :- ... .
(правило) - это два предложения одного предикатаlength/2
,Не используйте пустые скобки для предикатов без аргументов - по крайней мере, в SWI-Prolog это не будет работать вообще. Просто используйте
predicate2
вместоpredicate2()
во всех местах.Если вы попытаетесь вызвать неопределенный предикат, SWI-Prolog скажет
ERROR: toplevel: Undefined procedure: predicate2/0 (DWIM could not correct goal)
и Сикст-Пролог скажет{EXISTENCE ERROR: predicate2: procedure user:predicate2/0 does not exist}
Теперь к ответу. Две идеи приходят мне в голову.
(1) Это хак, но вы можете утверждать предикаты каждый раз, когда они вам нужны, и сразу же убирать их:
predicate1 :-
assert(predicate2), predicate2, retractall(predicate2).
Если вы хотите тело и аргументы для predicate2
, делать assert(predicate2(argument1, argument2) :- (clause1, clause2, clause3))
,
(2) Другим способом достижения этой цели было бы ввести дополнительный аргумент для предиката, который вы не хотите вызывать пользователем, и использовать его для идентификации, которую пользователь не может предоставить, но которую вы можете предоставить из своего вызова сказуемое. Это может быть большое постоянное число, которое выглядит случайным, или даже предложение. Это даже позволяет вам выводить пользовательское сообщение об ошибке в случае неправильной идентификации.
Пример:
predicate1 :-
predicate2("Identification: 2349860293587").
predicate2(Identification) :-
Identification = "Identification: 2349860293587",
1 < 5.
predicate2(Identification) :- Identification \= "Identification: 2349860293587",
write("Error: this procedure cannot be called by the user. Use predicate1/0 instead."),
fail.
Я не использую эквивалент predicate2("Identification: 2349860293587")
для первого пункта predicate2/0
потому что я не уверен, где заголовок предложения может появиться в сообщениях Пролога, а вы этого не хотите. Я использую fail
в конце второго предложения, чтобы Prolog печатал false
вместо true
после сообщения об ошибке. И, наконец, я понятия не имею, как запретить пользователю просматривать исходный код с помощью listing(predicate2)
так что это все еще позволит просто найти правильный идентификационный код, если он / она действительно хочет. Если это просто для предотвращения случайного вреда для пользователя, это должно быть достаточной защитой.
Это напоминает мне объект, найденный в Java. Там можно запросить текущий стек вызовов и использовать его для регулирования разрешений вызова метода. В переводе с Пролога мы находим в старом Прологе DEC-10 следующий предикат:
предки (L)
Объединяет L со списком целей предков для текущего предложения. Список начинается с родительской цели и заканчивается самым последним предком, исходящим из вызова в скомпилированном предложении. Список печатается с использованием print, и каждой записи предшествует номер вызова в скобках, за которым следует номер глубины (как указано в сообщении трассировки). Если в вызове нет номера (это произойдет, если режим отладки не был включен до тех пор, пока не выполнится дальнейшее выполнение), то это будет помечено знаком "-". Недоступно для скомпилированного кода.
Поскольку верхним уровнем обычно является скомпилированный предикат prolog/0, его можно использовать для написания предиката, который проверяет свой собственный стек вызовов, а затем решает, хочет ли он перейти в службу или нет.
rule2 :- ancestors(L), length(L,N), N<2, !, write('Don't call me'), fail.
rule2 :- 1<5.
В современных Прологах мы уже не часто находим предикат /1. Но это можно смоделировать по следующим направлениям. Просто выдавайте ошибку, и в случае, если эта ошибка украшена трассировкой стека, вы получите все, что вам нужно:
ancestors(L) :- catch(sys_throw_error(ignore),error(ignore,L),true).
Но остерегайтесь оптимизации устранения стека, может уменьшить стек и, следовательно, список, возвращаемый предками /1.
С уважением
PS: Оптимизация удаления стека уже объясняется здесь: [4] Уоррен, DHD (1983): Набор абстрактных инструкций по Прологу, Техническая записка 309, SRI International, октябрь, 1983
Обсуждение Jekejeke Prolog можно найти здесь: http://www.jekejeke.ch/idatab/doclet/prod/en/docs/10_pro08/13_press/03_bench/05_optimizations/03_stack.html