Costas-Tyros_rus_MS
.pdf
stdio::write(Us), stdio::nl. end implement main
goal mainExe::run(main::run).
implement main open core
domains
pp2 = (real Argument1, real Argument2) > real ReturnType.
clauses
classInfo("main", "zip").
class predicates sum:pp2. prod:pp2.
red:(pp2, real*, real, real) procedure (i, i, i, o). zip:(pp2, real*, real*, real*) procedure (i, i, i, o). dotprod:(real*, real*, real) procedure (i, i, o).
clauses
sum(X, Y)= Z : Z=X+Y. prod(X, Y)= Z : Z=X*Y.
zip(_P, [], _, []) : !. zip(_P, _, [], []) : !.
zip(P, [X|Xs], [Y|Ys], [Z|As]) : Z= P(X, Y), zip(P, Xs, Ys, As).
red(_P, [], A, A) : !. red(P, [X|Xs], A, Ans) :
red(P, Xs, P(X, A), Ans).
dotprod(Xs, Ys, D) : zip(prod, Xs, Ys, Z), red(sum, Z, 0, D).
run(): console::init(),
V1= [1,2,3,4,5], V2= [2,2,2,2,2], dotprod( V1, V2, X), stdio::write(X), stdio::nl.
end implement main
goal mainExe::run(main::run).
Рисунок 6.2 Zip в Прологе
51
Попытайтесь понять, как работает frontToken, потому что это весьма полезный предикат. Он ис пользуется для разбиения строки на токены (token), в процессе, называемом лексическим анализом. [Так же, как в русском языке предложение состоит из отдельных слов «лексем», строка кода состоит из знаков «то кенов» прим. пер.]. Например, если вы выполните
frontToken(“break December”, T, R)
вы получите T=”break”, и R=” December”.
Часто требуется трансформировать строковое представление терма в пригодное для работы. В этом случае, можно использовать функцию toTerm. В примере ниже Sx=stdio::readLine() читает строку из командной строки в Sx; затем toTerm трансформирует строковое представление в действи тельное число; вызов hasdomain(real, IX) удостоверит, что toTerm вернёт действительное число.
implement main clauses
classinfo("main", "toTerm_example"). run(): console::init(),
Sx= stdio::readLine(), hasdomain(real, IX), IX= toTerm(Sx), stdio::write(IX^2).
end implement main
goal mainExe::run(main::run).
Ниже вы сможете увидеть, что эта схема работает также для списков и других составных структур данных. К сожалению, hasdomain не принимает знак звездочки ‘*’ в имени домена. Таким образом, вы должны объявить домен списка самостоятельно, или использовать заранее объявленный домен,
такой, как core::integer_list.
implement main clauses
classInfo("main", "hasdomain_example"). run(): console::init(),
hasdomain(core::integer_list, Xs), Xs= toTerm(stdio::readLine()), stdio::write(Xs), stdio::nl.
end implement main
goal mainExe::run(main::run).
Вы научились преобразовывать строковое представление типов данных в термы Пролога. Те перь, давайте посмотрим, как делать обратную операцию, т. е., преобразовывать терм в его строко вое представление. Если вам нужен стандартный вид терма, то вы можете использовать предикат toString.
implement main clauses
classInfo("main", "toString_example"). run(): console::init(),
Xs= [3.4, 2, 5, 8], Str= toString(Xs),
Msg= string::concat("String representation: ", Str), stdio::write(Msg), stdio::nl.
end implement main
goal mainExe::run(main::run).
52
Если вы хотите покрасивее отформатировать числа или другие простые термы при преобразова нии их в строку, вы можете использовать функцию форматирования string::format; пример приве дён ниже.
implement main clauses
classInfo("main", "formatting"). run(): console::init(),
Str1= string::format("%8.3f\n %10.1e\n", 3.45678, 35678.0), Str2= string::format("%d\n %10d\n", 456, 18),
Str3= string::format("% 10d\n %010d\n", 18, 18), stdio::write(Str1, Str2, Str3).
end implement main goal
mainExe::run(main::run).
В вышеприведённом примере формат "%8.3f\n означает, что я хочу вывести на экран действи тельное число и возврат каретки; ширина поля, отведённого для числа, равна 8 знаков и число долж но быть отображено с тремя знаками после запятой. Формат "%010d\n" требует целого числа, вы ровненного по правому краю поля шириной 10; пустые места поля должны быть заполнены нулями. Формат “% 10d\n” определяет отображение целого числа, выровненного по левому краю поля ши риной 10; знак минус указывает на левое выравнивание; правое выравнивание является установкой по умолчанию. Формат “%10.1e\n” определяет научную запись для действительных чисел. Ниже вы можете увидеть то, что пример выведет на экран.
Еще ниже вы найдёте список типов данных, принимаемых строкой формата. Заметьте, что, когда в текстовое отображение конвертируются действительные числа, они обрезаются и округляются до длины в 17 цифр, если не была указана иная точность.
f Форматировать как действительное число с фиксированной запятой (как в 123.4).
e Форматировать действительное число в экспоненциальной записи (как в 1.234e+002). g Форматировать в формате f или e, где получается короче.
d Форматировать как знаковое целое. u Форматировать как беззнаковое целое.
x Форматировать как шестнадцатеричное число. o Форматировать как восьмеричное число.
c Форматировать как символ1.
B Форматировать как двоичный тип Visual Prolog.
R Форматировать как номер ссылки базы данных. P Форматировать как параметр процедуры.
s Форматировать как строку.
1 Имеется в виду тип char, то есть одиночный символ. В Прологе наряду с этим типом используется тип symbol, который тоже переводится как «символ», но подразумевает уже любые последовательности символов, как знакомый по другим языкам тип string.
53
6.4.1. Полезные предикаты для работы со строками
Ниже вы найдёте список других строковых предикатов, которые вы можете найти полезными в разработке ваших приложений; adjustBehaviour, adjustSide, и caseSensivity следует считать имеющими следующие определения:
domains adjustBehaviour =
expand();
cutRear(); cutOpposite().
adjustSide = left(); right(). caseSensitivity =
caseSensitive();
caseInsensitive(); casePreserve().
adjust : (string Src, charCount FieldSize, adjustSide Side)> string AdjustedString.
adjust : (string Src, charCount FieldSize, string Padding, adjustSide Side)> string AdjustedString.
adjustLeft : (string Src, charCount FieldSize)> string AdjustedString.
adjustLeft : (string Src, charCount FieldSize, string Padding)> string AdjustedString.
adjustRight : (string Src, charCount FieldSize)> string AdjustedString.
adjustRight : (string Src, charCount FieldSize, string Padding)> string AdjustedString.
adjustRight : (string Src, charCount FieldSize, string Padding, adjustBehaviour Behaviour) > string AdjustedString.
/********************************* Project Name: adjust_example
UI Strategy: console
***********************************/
implement main clauses
classInfo("main", "adjust_example"). run(): console::init(),
FstLn= "Rage Goddess, sing the rage of Peleus' son Achilles,", Str= string::adjust(FstLn, 60, "*", string::right), stdio::write(Str), stdio::nl.
end implement main
goal mainExe::run(main::run).
concat : (string First, string Last)
> string Output procedure (i,i).
concatList : (core::string_list Source)> string Output procedure (i).
54
concatWithDelimiter : (core::string_list Source, string Delimiter)> string Output procedure (i,i).
create : (charCount Length) > string Output procedure (i).
create : (charCount Length, string Fill)> string Output procedure (i,i).
createFromCharList : (core::char_list CharList)> string String.
/********************************* Project Name: stringops
UI Strategy: console
***********************************/
implement main clauses
classInfo("main", "stringops"). run():
console::init(),
SndLn= [ "murderous", "doomed",
"that cost the Achaeans countless losses"], Str= string::concatWithDelimiter(SndLn, ", "), Rule= string::create(20, " "),
stdio::write(Str), stdio::nl, stdio::write(Rule), stdio::nl.
end implement main
goal mainExe::run(main::run).
equalIgnoreCase : (string First, string Second) determ (i,i).
front : (string Source, charCount Position, string First, string Last) procedure (i,i,o,o).
frontChar : (string Source, char First, string Last) determ (i,o,o).
frontToken : (string Source, string Token, string Remainder) determ (i,o,o).
getCharFromValue : (core::unsigned16 Value) > char Char.
getCharValue : (char Char) > core::unsigned16 Value.
hasAlpha : (string Source) determ (i).
hasDecimalDigits : (string Source) determ (i).
hasPrefix : (string Source, string Prefix, string Rest) determ (i,i,o).
hasSuffix : (string Source, string Suffix, string Rest) determ (i,i,o).
isLowerCase : (string Source) determ (i).
isUpperCase : (string Source) determ (i).
isWhiteSpace : (string Source) determ.
lastChar : (string Source, string First, char Last) determ (i,o,o).
length : (string Source) > charCount Length procedure (i).
replace : (string Source, string ReplaceWhat,
string ReplaceWith, caseSensitivity Case)
55
> string Output procedure
replaceAll : ( string Source, string ReplaceWhat, string ReplaceWith) > string Output.
replaceAll : ( string Source, string ReplaceWhat, string ReplaceWith, caseSensitivity Case)> string Output.
caseSensitivity = caseSensitive; caseInsensitive; casePreserve.
search : (string Source, string LookFor)> charCount Position determ (i,i).
search : (string Source, string LookFor, caseSensitivity Case)> charCount Position determ (i,i,i).
split : (string Input, string Separators) > string_list.
split_delimiter : (string Source, string Delimiter)
> core::string_list List procedure (i,i).
subString : (string Source, charCount Position, charCount HowLong)> string Output procedure (i,i,i).
toLowerCase : (string Source) > string Output procedure (i).
Конвертирует буквы в строке в строчные
toUpperCase : (string Source) > string Output procedure (i).
Конвертирует буквы в строке в прописные
trim : (string Source) > string Output procedure (i).
Удаляет предшествующие и последующие строке пробелы.
trimFront : (string Source) > string Output procedure (i).
Удаляет предшествующие строке пробелы.
trimInner : (string Source) > string Output procedure (i).
Удаляет группы пробелов из строки Source.
trimRear : (string Source) > string Output procedure (i).
Удаляет следующие за строкой пробелы.
/********************************* Project Name: trim_example
UI Strategy: console
***********************************/
implement main clauses
classInfo("main", "trim_example"). run():
console::init(),
Str= " murderous, doomed ", T= string::trim(Str), stdio::write(T), stdio::nl, T1= string::trimInner(T), stdio::write(T1), stdio::nl.
end implement main
goal mainExe::run(main::run).
56
6.5. |
|
|
|
|
Грамматика для предикатов |
|
|
||||||||
ДавайтеПредставления о логике: |
|
|
|
|
|
|
|
|
|
|
|
||||
|
определим неформальные правила грамматики для исчисления предикатов. |
||||||||||||||
• |
Символ |
p t ,t ,…t ,t |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
называется литералом. В литерале p/n является n местным |
||||||||||||||
|
символом, отличающимся от всех остальных символов языка, а t являются термами; тер |
||||||||||||||
|
мами могут быть |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
o |
Функциональные символы, например, символы вида |
что |
. Функцио |
|||||||||||
|
|
нальный символ может иметь нулевую арность, что означает, |
|
|
и |
||||||||||
|
|
,,… |
|
, , |
, |
|
|||||||||
|
|
тоже могут быть функциональными символами. |
|
|
|
|
|||||||||
|
o Числительные, строки и символы тоже могут быть термами. |
|
|
|
|
|
|||||||||
|
Если и термы, то |
|
|
|
формул, |
|
, |
и |
являются литералами и их |
||||||
• |
можно использовать для создания, |
. |
|
|
|
|
|
|
|
||||||
|
Если P и Q формулы, то |
|
|
, |
|
|
|
и |
тоже формулы. Кроме этого, если |
||||||
• |
P – формула а X – переменная, |
, |
(P,истинно при любом X) и |
|
(существует такой |
||||||||||
X, который обращает P в истину) тоже являются формулами.
•Переменная, перед которой стоит знак , называется экзистенциальной переменной. Та
ким образом, переменная X в формуле
sin 0
является экзистенциальной переменной. Все подобные переменные возможно убрать из формулы, подставив вместо них константы. В данном примере, вместо X можно подста
вить π:
sin 0
Нормальная форма1
Любое предложение предиката может быть переписано в нормальной форме, то есть в виде со вокупности дизъюнкций и конъюнкций, без использования квантора существования и квантора общ ности в скобках. Так как запись обозначает дизъюнкцию, а – конъюнкцию, любая форму
ла может быть переписана следующим образом:
…
……
…
…
где – литералы или отрицания литералов. Мы уже видели, как избавиться от кванторов суще ствования. Давайте рассмотрим оставшиеся необходимые шаги для получения нормальной формы.
1.Избавиться от импликаций и эквиваленций.
2.Распределить отрицания. Используйте таблицы истинности, чтобы доказать, что
и что
Затем, используйте полученные результаты для того, чтобы осуществить следующие под становки:
3.Тораф Альберт Сколем (Thoraf Albert Skolem) (1887 1963) – норвежский математик, кото рый изобрёл названный в его честь способ2 избавляться от экзистенциальных перемен ных. Например
, |
, |
1 В оригинале автор использует термин clausal form – что то наподобие «форма высказываний», но в
данном случае совершенно точно идёт речь о коньюктивной нормальной форме (англ. эквивалент –
Conjunctive normal form, согласно en.wikipedia.org), отсюда перевод.
2 Имеется в виду сколемизация
57
4. Распределить конъюнкции, используя следующие правила:
58
Глава 7: Грамматика
Философ Витгенштейн (Wittgenstein), в своем «Логико философском трактате» (Tratactus Logicus Philosophicus), выразил мнение, что Мир есть положение вещей. Предметы и объекты имеют свой
ства – такие, как месторасположение, целостность1, цвет, и т.д. Набор значений, которые эти свойст ва принимают, называется состоянием. Например, согласно Евклиду, точка не имеет свойств, кроме
местоположения. Значит, состояние точки может быть задано её координатами.
Первое предложение Tratactus: The World is all, that is the case – «Мир представляет собой всё,
что имеет место». Последнее предложение: Whereof one cannot speak, thereof one must be silent – «о
чем нельзя говорить, о том нужно молчать». Практически невозможно создать структуру данных для того, чтобы отобразить идеи Витгенштейна на компьютере. Хотя, даже задуманная лингвистическим гением, эта структура данных определённо будет иметь три конструктора: узла world, узла case и узла silent. Вот семантический класс, чьё единственное применение – в экспорте семантического домена
Tratactus’а:
class semantic open core
domains
category=art; nom; cop; rel.
tree=case(category, string); world(tree, tree); silence.
predicates
classInfo : core::classInfo. end class semantic
В этом листинге есть новшество: использование конструкторов структур данных. Конструктор имеет вид функции, то есть, у него есть функтор и аргументы. Например, конструктор case/2 имеет два аргумента, грамматическую категорию и строку, обозначающую знак.
7.1.Грамматическийанализ
Действовать значит изменять свойства объектов. Таким образом, результат действия измене ние состояния. Например, если предикат разбирает список слов, мир изменяет своё состояние. Под
конец разбора мир приходит в состояние, в котором парсер разобрал часть списка. Чтобы понять эти вещи, давайте создадим класс german, чтобы разобрать первое предложение Tratactus. Используя
его, как модель, вы можете написать более мощный класс, чтобы разобрать целую книгу. Верное де ло, потому что Tratactus очень короткий.
% Файл german.cl class german
open core, semantic predicates
classInfo : core::classInfo.
article:(tree, string*, string*) nondeterm (o, i, o). noun:(tree, string*, string*) nondeterm (o, i, o). nounphr:(tree, string*, string*) nondeterm (o, i, o). copula:(tree, string*, string*) nondeterm (o, i, o). phr:(tree, string*, string*) nondeterm (o, i, o).
end class german
1 Оно целое или разбитое? – прим. авт.
59
% Файл german.pro implement german
open core, semantic clauses
classInfo("german", "1.0").
article(case(art, ""), ["die"|Rest], Rest). article(case(art, ""), ["der"|Rest], Rest).
noun(case(nom, "Welt"), ["Welt"|R], R). noun(case(nom, "Fall"), ["Fall"|R], R).
copula(world(case(cop, "ist"), T), ["ist"|R1], R) : nounphr(T, R1, R).
nounphr(world(T1, T2), Start, End) : article(T1, Start, S1), noun(T2, S1, End).
nounphr(case(nom, "alles"), ["alles"|R], R).
phr(world(T1, T2), Start, End) :
nounphr(T1, Start, S1), copula(T2, S1, End). end implement german
Если вы обратите внимание на объявление article, вы заметите, что его квалификатор направ лений ввода вывода (o, i, o), то есть, он получает список слов и выводит дерево разбора (parse tree1), вместе с частично обработанным входным списком. Определение noun работает в точности так же, как и article. Пример стоит тысячи слов; давайте подставим ["die", "Welt", "ist", "alles"]
в article/3.
article(T1, ["die", "Welt", "ist", "alles"], S1).
Этот вызов будет соответствовать первому предложению article/3, с
Rest=["Welt", "ist", "alles"].
В результате будет выведено T1=case(art, ""), и S1= ["Welt", "ist", "alles"]. Дальше, да вайте подставим S1= ["Welt", "ist", alles"], в noun/3.
noun(T2, ["Welt", "ist", "alles"], End)
Вышеприведённый вызов будет соответствовать первому предложению noun/3, с R=["ist", "alles"]. Результатом будет T2=case(nom, "Welt"), и End=["ist", "alles"]. Определение nounphr делает эти два вызова по очереди, производя группу существительного ["die", "Welt"]. copula/3 и phr/3 ведут себя как nounphr.
7.2.Грамматический синтез
В то время как немецкий разбор принимает предложение и выводит узел, английский разбор, приведённый ниже, получает узел, из которого создаёт предложение. Можно сказать, что узел пред ставляет собой значение предложения. Немецкий разбор обрабатывает предложение на немецком,
ивыводит его значение. Английский разбор получает значение и строит английский перевод.
1Если вы не знаете эти лингвистические термины, просмотрите книги об обработке естественных языков, или построению компиляторов, что вам больше нравится. – прим. авт.
60
