Настоящая глава посвящена примерам решения задач на языке Visual Prolog. Создается небольшая экспертная система. Приводится пример ее использования для определения мифологических животных. Рассматривается так называемая "головоломка Эйнштейна" (задача, авторство которой приписывается Эйнштейну). Последний параграф посвящен моделированию фейерверка.
Рассмотрим экспертную систему о мифологических животных. Система распознает следующих животных: грифон, дракон, единорог, сатир и сфинкс (для того чтобы она распознавала и других животных, достаточно внести сведения о них в базу данных). Они определяются по следующим признакам:
Программа вычисляет животное следующим образом. Она перебирает животных из базы данных (об этом пользователю не сообщается) и проверяет в диалоге с пользователем, имеются ли у них в наличии необходимые признаки. Если выяснилось, что признак имеется, то впоследствии повторно вопрос о нем не задается. Полученные сведения могут быть противоречивыми. В этом случае программа сообщает, что для ответа ей недостает сведений. Если оказалось, что все признаки некоторого животного имеются в наличии, и что признаки, которыми он не обладает, действительно отсутствуют, то делается заключение, что животное распознано. Пользователю задаются вопросы, на которые он отвечает "да", "нет" или "?". Например, "Верно ли что, форма крыльев — орлиные?". Если пользователь вводит знак вопроса "?", то программа объясняет, почему задается такой вопрос, например: "Идет проверка гипотезы, что чудовище — грифон <далее перечисляются признаки>. Известно, что чудовище — крылатое".
Все сведения о животных хранятся в двух видах фактов — rule/4 и cond/3. Отношение rule описывает виды животных, в нем указывается список номеров необходимых признаков. Отношение cond описывает сами признаки. Идея реализации экспертной системы взята из примера GENI системы Visual Prolog 5.2. Ниже приводится более простой вариант экспертной системы.
В директории Exe проекта следует создать файл monsters.txt и поместить в него приведенные ниже факты.
clauses
rule(1, "чудовище", "грифон", [1, 6, 12, 18]).
rule(2, "чудовище", "дракон", [1, 4, 15, 19]).
rule(3, "чудовище", "единорог", [1, 5, 2, 7, 9, 11, 16]).
rule(4, "чудовище", "сатир", [2, 8, 10, 14, 17]).
rule(5, "чудовище", "сфинкс", [3, 13, 18]).
cond(1, "чудовище", "крылатое").
cond(2, "чудовище", "рогатое").
cond(3, "чудовище", "без крыльев и рогов").
cond(4, "форма крыльев", "летучей мыши").
cond(5, "форма крыльев", "лебединые").
cond(6, "форма крыльев", "орлиные").
cond(7, "форма рогов", "прямые").
cond(8, "форма рогов", "изогнутые").
cond(9, "количество рогов", "1").
cond(10, "количество рогов", "2").
cond(11, "голова", "лошадиная").
cond(12, "голова", "орлиная").
cond(13, "голова", "человечья").
cond(14, "голова", "обезьянья").
cond(15, "голова", "крокодилья").
cond(16, "туловище", "лошадиное").
cond(17, "туловище", "козлиное").
cond(18, "туловище", "львиное").
cond(19, "туловище", "змеиное").
topic("чудовище").
Реализация экспертной системы приведена в листинге 15.2.
open core, console, string, list
class facts - db
rule: (positive, string, string, positive*).
cond: (positive, string, string).
topic: (string).
class facts
answer: (positive, integer).
class predicates
expert: (string).
ask: (positive, positive*) determ.
ask: (integer, positive, positive*) determ.
explanation: (positive).
getAnswer: () -> integer.
getAnswer: (char) -> integer determ.
correct: (positive*) determ.
clauses
expert(Topic):-
rule(K, Topic, Goal, QList),
ask(K, QList),
correct(QList),
!,
writef("Заключение: % - %.", Topic, Goal).
expert(Topic):-
writef("Не хватает сведений, чтобы определить %.", Topic).
ask(_, [N | _]):-
answer(N, 0), % если признак отсутствует,
!, % то это животное не подходит
fail.
ask(K, [N | QList]):-
answer(N, 1), % если признак присутствует,
!, % то следующий вопрос
ask(K, QList).
ask(K, [N | QList]):-
cond(N, X, Y), % задается вопрос о признаке
writef("Верно ли, что % - %? (да/нет/?): > ", X, Y),
A = getAnswer(), % ответ от пользователя
!,
ask(A, K, [N | QList]). % реакция на ответ
ask(_, []).
ask(2, K, QList):- !, % если введен знак ?, то
explanation(K), % выдается объяснение,
ask(K, QList). % опрос продолжается
ask(A, _, [N | _]):-
assert(answer(N, A)), % ответ запоминается в БД
fail.
ask(1, K, [_ | QList]):- % если ответ положительный,
ask(K, QList). % то опрос продолжается
explanation(K):-
rule(K, Topic, Goal, QList),
writef("Проверяется гипотеза, что % - %. Это так, если\n",
Topic, Goal),
F = {(I):- cond(I, X, Y), !, writef("\t% - %\n", X, Y); succeed},
forAll(QList, F),
L = [N || answer(N, 1), isMember(N, QList)],
L <> [],
!,
write("Известно, что\n"),
forAll(L, F).
explanation(_).
correct(QList):-
answer(N, 1), % если есть признак,
not(isMember(N, QList)), % которого не должно быть,
!, % то животное не подходит
fail;
succeed().
getAnswer() = R:-
S = frontChar(toLowerCase(trim(readLine()))),
R = getAnswer(S),
!.
getAnswer() = 0.
getAnswer(S) = 1:- % положительный ответ
(S = 'д'; S = 'y'; S = 'l'), % не зависит от раскладки
!.
getAnswer('?') = 2.
run():-
file::consult("monsters.txt", db),
topic(Topic),
expert(Topic),
!,
_ = readLine();
succeed().
Знаменитая головоломка Эйнштейна формулируется следующим образом.
С другой стороны улицы подряд стоят пять домов, каждый своего цвета. В каждом доме живет человек, все пять разных национальностей. Каждый человек предпочитает уникальную марку сигарет, напиток и домашнее животное. Известно, что
Определите для каждого дома его цвет, а также национальность, любимый напиток, марку сигарет и домашнее животное хозяина [9].
Ниже приводится решение задачи методом "образовать и проверить". Сначала создается список, состоящий из пяти пустых структур, соответствующих пяти домам. Этот список постепенно заполняется в соответствии с условиями, приведенными в задаче: выбирается произвольным образом одна или две структуры (они берутся по очереди), в зависимости от условия, и делается попытка вставить в нее или в них значения из этого условия. Если этого сделать не удается, то делается откат.
open core, console, list
constants
empty : symbol = "".
domains % описание дома
house = h(symbol Colour, symbol Nationality, symbol Pet,
symbol Drink, symbol Cigarette).
class predicates
colour: (house, symbol Colour) -> house determ.
nationality: (house, symbol Nationality) -> house determ.
pet: (house, symbol Pet) -> house determ.
drink: (house, symbol Drink) -> house determ.
cigarette: (house, symbol Cigarette) -> house determ.
unif: (symbol, symbol) determ.
leftmost: (house [out], positive [out], house*) determ.
middle: (house [out], positive [out], house*).
next: (house, positive, house, positive, house*)
nondeterm (o,o,o,o,i).
right: (house, positive, house, positive, house*)
nondeterm (o,o,o,o,i).
right: (house, positive, positive, house, positive, positive, house*)
nondeterm (o,i,o,o,i,o,i).
solve: () -> house* nondeterm.
clauses
colour(h(Cl, N, P, D, S), C) = h(C, N, P, D, S):-
unif(Cl, C).
nationality(h(C, Nt, P, D, S), N) = h(C, N, P, D, S):-
unif(Nt, N).
pet(h(C, N, Pt, D, S), P) = h(C, N, P, D, S):-
unif(Pt, P).
drink(h(C, N, P, Dr, S), D) = h(C, N, P, D, S):-
unif(Dr, D).
cigarette(h(C, N, P, D, Sg), S) = h(C, N, P, D, S):-
unif(Sg, S).
unif(empty, _):- !.
unif(X, X).
% крайний слева дом имеет номер 0 и стоит первым в списке
leftmost(X, 0, [X | _]).
% средний дом находится в середине списка
middle(nth(I, L), I, L):-
I = length(L) div 2.
% пары соседних домов, где дом B находится справа от дома A
right(A, I, B, J, L):-
right(A, 0, I, B, 1, J, L).
right(A, I, I, B, J, J, [A, B | _]).
right(A, C1, I, B, C2, J, [_ | L]):-
right(A, C1 + 1, I, B, C2 + 1, J, L).
% пары соседних домов
next(A, Na, B, Nb, Houses):-
right(A, Na, B, Nb, Houses).
next(A, Na, B, Nb, Houses):-
right(B, Nb, A, Na, Houses).
solve() = Houses20:-
Houses = [h(empty, empty, empty, empty, empty) ||
_ = std::fromTo(0, 4)],
% англичанин живет в красном доме
memberIndex_nd(H1, C1, Houses),
setNth(C1, Houses,
colour(nationality(H1, "Englishman"), "red"), Houses1),
% у испанца есть собака
memberIndex_nd(H2, C2, Houses1),
setNth(C2, Houses1, pet(nationality(H2, "Spaniard"), "dog"),
Houses2),
% кофе пьют в зеленом доме
memberIndex_nd(H3, C3, Houses2),
setNth(C3, Houses2, drink(colour(H3, "green"), "coffee"),
Houses3),
% украинец пьет чай
memberIndex_nd(H4, C4, Houses3),
setNth(C4, Houses3, drink(nationality(H4, "Ukrainian"), "tea"),
Houses4),
% зеленый дом – первый справа от дома цвета слоновой кости
right(H5, C5, H6, C6, Houses4),
setNth(C5, Houses4, colour(H5, "ivory"), Houses5),
setNth(C6, Houses5, colour(H6, "green"), Houses6),
% курильщик "Winston" держит улиток
memberIndex_nd(H7, C7, Houses6),
setNth(C7, Houses6, pet(cigarette(H7, "Winston"), "snails"),
Houses7),
% сигареты "Kool" курят в жёлтом доме
memberIndex_nd(H8, C8, Houses7),
setNth(C8, Houses7, colour(cigarette(H8, "Kool"), "yellow"),
Houses8),
% молоко пьют в среднем доме
middle(H9, C9, Houses8),
setNth(C9, Houses8, drink(H9, "milk"), Houses9),
% норвежец живет в крайнем слева доме
leftmost(H10, C10, Houses9),
setNth(C10, Houses9, nationality(H10, "Norwegian"),
Houses10),
% курящий "Chesterfield", живет по соседству с лисой
next(H11, C11, H12, C12, Houses10),
setNth(C11, Houses10, cigarette(H11, "Chesterfield"),
Houses11),
setNth(C12, Houses11, pet(H12, "fox"), Houses12),
% "Kool" курят в доме, соседнем с домом, где имеется лошадь
next(H13, C13, H14, C14, Houses12),
setNth(C13, Houses12, cigarette(H13, "Kool"), Houses13),
setNth(C14, Houses13, pet(H14, "horse"), Houses14),
% предпочитающий "Lucky Strike" пьет апельсиновый сок
memberIndex_nd(H15, C15, Houses14),
setNth(C15, Houses14, drink(cigarette(H15, "Lucky Strike"),
"orange juice"), Houses15),
% японец курит сигареты "Parliament"
memberIndex_nd(H16, C16, Houses15),
setNth(C16, Houses15, cigarette(nationality(H16, "Japanese"),
"Parliament"), Houses16),
% норвежец живет в доме рядом с голубым домом
next(H17, C17, H18, C18, Houses16),
setNth(C17, Houses16, nationality(H17, "Norwegian"),
Houses17),
setNth(C18, Houses17, colour(H18, "blue"), Houses18),
% у одного из мужчин есть зебра
memberIndex_nd(H19, C19, Houses18),
setNth(C19, Houses18, pet(H19, "zebra"), Houses19),
% один из мужчин пьет воду
memberIndex_nd(H20, C20, Houses19),
setNth(C20, Houses19, drink(H20, "water"), Houses20).
run():-
Houses = solve(),
forAll(Houses, {(h(C, N, P, D, S)):-
writef("%-10%-14%-10%-16%\n", C, N, P, D, S)}),
fail;
_ = readLine().
В следующей программе моделируется фейерверк. Она представляет собой анимационную поздравительную открытку (рис. 15.1).
(рис 15.1) Новогодний фейерверк
На фоне праздничного фейерверка появляются поздравительные надписи, выбранные случайным образом. Цвет надписей и цвет каждого элемента фейерверка также выбирается случайным образом. На каждую надпись приходится три залпа. Местоположение фейерверка и надписи выбирается случайным образом перед каждым залпом. Перед появлением или исчезновением каждого элемента фейерверка делается небольшая пауза. Поток залпов и поздравлений прекращается после щелчка левой кнопкой мыши в окне консоли.
open core, console, console_native, math, list, programControl
constants
% Congratulation-phrases for random choice
phraseList : string* =
[
"Счастливого Нового Года и Рождества!",
"From Russia with love!",
"Vivat Visual Prolog!",
"We wish you a Merry Christmas and a happy New Year!",
"Joyeux Noёl et bonne Annee!",
"Frohe Weihnachten und ein gutes neues Jahr!",
"Buon Natale e felice Anno Nuovo!",
"Feliz Navidad y Prospero Ano Nuevo!",
"Glaedelig Jul og Godt Nytar!",
"Kurisumasu Omedeto!",
"Natale hilareet Annum Faustum!"
].
% Symbols that will be randomly used for building a firework
symbolList : string* = ["*", "+", "x"].
class facts
xp : unsigned16 := 28.
yp : unsigned16 := 37.
phrase : string := "".
n : positive := 3.
class predicates
ellipse: (integer Rx, integer Ry, integer XCent, integer YCent,
string Symbol, positive Sleep).
arc: (integer Rx, integer Ry, integer XCent, integer YCent,
real PercentBeg, real PercentEnd, string Symb, positive Sleep).
line: (coord P1, coord P2, string, positive Sleep).
print: (string Symbol).
stop: () determ.
clauses
print(Symbol):-
setTextAttribute(math::random(7) + 9),
write(Symbol).
% drawing an ellipse with the center in (XCent; YCent)
ellipse(Rx, Ry, XCent, YCent, Symbol, Sleep):-
arc(Rx, Ry, XCent, YCent, 0, 1, Symbol, Sleep).
% drawing an arc with the center in (XCent; YCent)
arc(Rx, Ry, XCent, YCent, PercentBeg, PercEnd, Symb, Sleep):-
X = std::downTo(round(PercEnd *31), round(PercentBeg *31)),
Angle = (2 * pi) * (X / 32),
XCur = round(XCent + Rx * cos(Angle)),
YCur = round(YCent - Ry * sin(Angle)),
setLocation(coord(XCur + 0, YCur + 0)),
print(Symb),
sleep(Sleep),
fail;
succeed().
% Drawing a vertical line
line(coord(X, Y1), coord(X, Y2), Symbol, Sleep):- !,
(Y = std::fromTo(Y1, Y2),
setLocation(coord(X, Y)),
print(Symbol),
sleep(Sleep),
fail;
succeed()).
% Drawing a non-vertical line
line(coord(X1, Y1), coord(X2, Y2), Symbol, Sleep):-
X = std::between(X1, X2),
Y = round(Y1 + (X - X1) * (Y2 - Y1) / (X2 - X1)),
setLocation(coord(X, Y + 0)),
print(Symbol),
sleep(Sleep),
fail;
succeed().
stop():-
L = getEventQueue(),
mouse(_, _, 1, _, 0) = getMember_nd(L),
!.
% Drawing a firework using random symbols from the list
% and replacing the firework by that of blank spaces
constants
% Parametres for 7 branches of the firework
startx : integer* = [42, 38, 42, 38, 41, 39, 39].
starty : integer* = [20, 20, 18, 18, 21, 21, 18].
vx : integer* = [7, -6, 4, -3, 5, -5, -2].
vy : integer* = [7, 7, 9, 9, 5, 5, 11].
class facts
parametres : tuple{integer, integer, integer, integer}* :=
zip(startx, starty, vx, vy).
class predicates
printPoint: (real X0, real Xspeed, real T, real Y0, real Yspeed,
real G, string Symbol).
firework: (boolean IsDrawing, real X0, real Y0, real G,
positive Sleep, string Symbol, positive SymbolListLength).
getSymbol: (boolean IsDrawing, string Symbol, positive) -> string.
clauses
/* Calculation of the trajectory of a thrown object (symbol) for
forming a firework line. The object is thrown at certain angle
and falls with free-fall acceleration G */
printPoint(X0, Xspeed, T, Y0, Yspeed, G, Symbol):-
X = round(X0 + XSpeed * T),
Y = round(Y0 - (YSpeed * T - G * T ^ 2/2)),
setLocation(coord(X + 0, Y + 0)),
print(Symbol).
firework(IsDrawing, Xc, Yc, G, Sleep, Symbol, N):-
I = std::fromTo(0, 29),
forAll(parametres, {(tuple(Cx, Cy, Vx, Vy)):-
printPoint(Cx - Xc, Vx, I * 0.1, Cy + Yc, Vy, G,
getSymbol(IsDrawing, Symbol, N))}),
sleep(Sleep),
fail;
succeed().
getSymbol(true, _, N) = nth(random(N), symbolList):- !.
getSymbol(_, Symbol, _) = Symbol.
run():-
FSleep = 15,
setConsoleTitle("Happy New Year"),
%A circle that will contain the characters
ellipse(22, 11, 40, 12, "*", 0),
vpi::alarm(2),
% Three
arc(7, 4, 40, 7, -0.25, 0.40, "$", FSleep),
arc(9, 5, 40, 16, -0.43, 0.25, "$", FSleep),
sleep(750),
% Erasing 'three'
% To erase something we replace it by the same figure
% formed by blank spaces
arc(7, 4, 40, 7, -0.25, 0.40, " ", FSleep),
arc(9, 5, 40, 16, -0.43, 0.25, " ", FSleep),
vpi::alarm(2),
% Two
arc(7, 4, 40, 7, -0.1, 0.50, "$", FSleep),
line(coord(46, 9), coord(34, 18), "$", FSleep),
line(coord(33, 19), coord(48, 19), "$", FSleep),
sleep(750),
% Erasing 'two'
arc(7, 4, 40, 7, -0.1, 0.50, " ", FSleep),
line(coord(46, 9), coord(34, 18), " ", FSleep),
line(coord(33, 19), coord(48, 19), " ", FSleep),
vpi::alarm(2),
% One
line(coord(33, 11), coord(43, 5), "$", FSleep),
line(coord(43, 6), coord(43, 19), "$$", FSleep),
sleep(750),
% Erasing 'one'
line(coord(33, 11), coord(43, 5), " ", FSleep),
line(coord(43, 6), coord(43, 19), " ", FSleep),
vpi::alarm(2),
% Zero
ellipse(11, 7, 40, 12, "$$", FSleep),
sleep(200),
% Erasing 'zero'
ellipse(11, 7, 40, 12, " ", 0),
% Erasing the initial circle
ellipse(22, 11, 40, 12, " ", 0),
LengthOfPhraseList = length(phraseList),
LengthOfSymbolList = length(symbolList),
vpi::alarm(3),
I = varM::new(0),
/* The cycle that draws fireworks and writes random
congratulation-phrases in a random place */
std::repeat(),
Xc = random(11),
Yc = random(5),
G = 4 + random(3),
/* The first time a congratulation-phrase appears
(all in all one phrase exists during three fireworks) */
if I:value = 0 then
setLocation(coord(xp, yp)),
phrase := nth(n, phraseList)
end if,
/* The last time the current phrase appears; it is replaced
by a string of blank spaces. Then new random location is
chosen and a new congratulation-phrase is written */
if I:value mod 3 = 0, I:value > 0 then
setLocation(coord(xp, yp)),
write(string::create(55, " ")),
phrase := nth(n, phraseList),
xp :=random(getConsoleWidth() - string::length(phrase)),
yp := random(30),
setLocation(coord(xp, yp)),
write(phrase)
% Not the first or the last time the current phrase appears
else
setLocation(coord(xp, yp)),
write(phrase)
end if,
I:value := I:value + 1,
n := random(LengthOfPhraseList),
% Drawing a firework
firework(true, Xc, Yc, G, 30, "", LengthOfSymbolList),
% Erasing the firework
firework(false, Xc, Yc, G, 3, " ", 0),
stop(),
!,
_ = readLine();
succeed().
В упражнениях 6 – 9 требуется написать программу, которая решает логическую задачу.
Настоящая глава посвящена примерам решения задач на языке Visual Prolog. Создается небольшая экспертная система. Приводится пример ее использования для определения мифологических животных. Рассматривается так называемая "головоломка Эйнштейна" (задача, авторство которой приписывается Эйнштейну). Последний параграф посвящен моделированию фейерверка.
Рассмотрим экспертную систему о мифологических животных. Система распознает следующих животных: грифон, дракон, единорог, сатир и сфинкс (для того чтобы она распознавала и других животных, достаточно внести сведения о них в базу данных). Они определяются по следующим признакам:
Программа вычисляет животное следующим образом. Она перебирает животных из базы данных (об этом пользователю не сообщается) и проверяет в диалоге с пользователем, имеются ли у них в наличии необходимые признаки. Если выяснилось, что признак имеется, то впоследствии повторно вопрос о нем не задается. Полученные сведения могут быть противоречивыми. В этом случае программа сообщает, что для ответа ей недостает сведений. Если оказалось, что все признаки некоторого животного имеются в наличии, и что признаки, которыми он не обладает, действительно отсутствуют, то делается заключение, что животное распознано. Пользователю задаются вопросы, на которые он отвечает "да", "нет" или "?". Например, "Верно ли что, форма крыльев — орлиные?". Если пользователь вводит знак вопроса "?", то программа объясняет, почему задается такой вопрос, например: "Идет проверка гипотезы, что чудовище — грифон <далее перечисляются признаки>. Известно, что чудовище — крылатое".
Все сведения о животных хранятся в двух видах фактов — rule/4 и cond/3. Отношение rule описывает виды животных, в нем указывается список номеров необходимых признаков. Отношение cond описывает сами признаки. Идея реализации экспертной системы взята из примера GENI системы Visual Prolog 5.2. Ниже приводится более простой вариант экспертной системы.
В директории Exe проекта следует создать файл monsters.txt и поместить в него приведенные ниже факты.
clauses
rule(1, "чудовище", "грифон", [1, 6, 12, 18]).
rule(2, "чудовище", "дракон", [1, 4, 15, 19]).
rule(3, "чудовище", "единорог", [1, 5, 2, 7, 9, 11, 16]).
rule(4, "чудовище", "сатир", [2, 8, 10, 14, 17]).
rule(5, "чудовище", "сфинкс", [3, 13, 18]).
cond(1, "чудовище", "крылатое").
cond(2, "чудовище", "рогатое").
cond(3, "чудовище", "без крыльев и рогов").
cond(4, "форма крыльев", "летучей мыши").
cond(5, "форма крыльев", "лебединые").
cond(6, "форма крыльев", "орлиные").
cond(7, "форма рогов", "прямые").
cond(8, "форма рогов", "изогнутые").
cond(9, "количество рогов", "1").
cond(10, "количество рогов", "2").
cond(11, "голова", "лошадиная").
cond(12, "голова", "орлиная").
cond(13, "голова", "человечья").
cond(14, "голова", "обезьянья").
cond(15, "голова", "крокодилья").
cond(16, "туловище", "лошадиное").
cond(17, "туловище", "козлиное").
cond(18, "туловище", "львиное").
cond(19, "туловище", "змеиное").
topic("чудовище").
Реализация экспертной системы приведена в листинге 15.2.
open core, console, string, list
class facts - db
rule: (positive, string, string, positive*).
cond: (positive, string, string).
topic: (string).
class facts
answer: (positive, integer).
class predicates
expert: (string).
ask: (positive, positive*) determ.
ask: (integer, positive, positive*) determ.
explanation: (positive).
getAnswer: () -> integer.
getAnswer: (char) -> integer determ.
correct: (positive*) determ.
clauses
expert(Topic):-
rule(K, Topic, Goal, QList),
ask(K, QList),
correct(QList),
!,
writef("Заключение: % - %.", Topic, Goal).
expert(Topic):-
writef("Не хватает сведений, чтобы определить %.", Topic).
ask(_, [N | _]):-
answer(N, 0), % если признак отсутствует,
!, % то это животное не подходит
fail.
ask(K, [N | QList]):-
answer(N, 1), % если признак присутствует,
!, % то следующий вопрос
ask(K, QList).
ask(K, [N | QList]):-
cond(N, X, Y), % задается вопрос о признаке
writef("Верно ли, что % - %? (да/нет/?): > ", X, Y),
A = getAnswer(), % ответ от пользователя
!,
ask(A, K, [N | QList]). % реакция на ответ
ask(_, []).
ask(2, K, QList):- !, % если введен знак ?, то
explanation(K), % выдается объяснение,
ask(K, QList). % опрос продолжается
ask(A, _, [N | _]):-
assert(answer(N, A)), % ответ запоминается в БД
fail.
ask(1, K, [_ | QList]):- % если ответ положительный,
ask(K, QList). % то опрос продолжается
explanation(K):-
rule(K, Topic, Goal, QList),
writef("Проверяется гипотеза, что % - %. Это так, если\n",
Topic, Goal),
F = {(I):- cond(I, X, Y), !, writef("\t% - %\n", X, Y); succeed},
forAll(QList, F),
L = [N || answer(N, 1), isMember(N, QList)],
L <> [],
!,
write("Известно, что\n"),
forAll(L, F).
explanation(_).
correct(QList):-
answer(N, 1), % если есть признак,
not(isMember(N, QList)), % которого не должно быть,
!, % то животное не подходит
fail;
succeed().
getAnswer() = R:-
S = frontChar(toLowerCase(trim(readLine()))),
R = getAnswer(S),
!.
getAnswer() = 0.
getAnswer(S) = 1:- % положительный ответ
(S = 'д'; S = 'y'; S = 'l'), % не зависит от раскладки
!.
getAnswer('?') = 2.
run():-
file::consult("monsters.txt", db),
topic(Topic),
expert(Topic),
!,
_ = readLine();
succeed().
Знаменитая головоломка Эйнштейна формулируется следующим образом.
С другой стороны улицы подряд стоят пять домов, каждый своего цвета. В каждом доме живет человек, все пять разных национальностей. Каждый человек предпочитает уникальную марку сигарет, напиток и домашнее животное. Известно, что
Определите для каждого дома его цвет, а также национальность, любимый напиток, марку сигарет и домашнее животное хозяина [9].
Ниже приводится решение задачи методом "образовать и проверить". Сначала создается список, состоящий из пяти пустых структур, соответствующих пяти домам. Этот список постепенно заполняется в соответствии с условиями, приведенными в задаче: выбирается произвольным образом одна или две структуры (они берутся по очереди), в зависимости от условия, и делается попытка вставить в нее или в них значения из этого условия. Если этого сделать не удается, то делается откат.
open core, console, list
constants
empty : symbol = "".
domains % описание дома
house = h(symbol Colour, symbol Nationality, symbol Pet,
symbol Drink, symbol Cigarette).
class predicates
colour: (house, symbol Colour) -> house determ.
nationality: (house, symbol Nationality) -> house determ.
pet: (house, symbol Pet) -> house determ.
drink: (house, symbol Drink) -> house determ.
cigarette: (house, symbol Cigarette) -> house determ.
unif: (symbol, symbol) determ.
leftmost: (house [out], positive [out], house*) determ.
middle: (house [out], positive [out], house*).
next: (house, positive, house, positive, house*)
nondeterm (o,o,o,o,i).
right: (house, positive, house, positive, house*)
nondeterm (o,o,o,o,i).
right: (house, positive, positive, house, positive, positive, house*)
nondeterm (o,i,o,o,i,o,i).
solve: () -> house* nondeterm.
clauses
colour(h(Cl, N, P, D, S), C) = h(C, N, P, D, S):-
unif(Cl, C).
nationality(h(C, Nt, P, D, S), N) = h(C, N, P, D, S):-
unif(Nt, N).
pet(h(C, N, Pt, D, S), P) = h(C, N, P, D, S):-
unif(Pt, P).
drink(h(C, N, P, Dr, S), D) = h(C, N, P, D, S):-
unif(Dr, D).
cigarette(h(C, N, P, D, Sg), S) = h(C, N, P, D, S):-
unif(Sg, S).
unif(empty, _):- !.
unif(X, X).
% крайний слева дом имеет номер 0 и стоит первым в списке
leftmost(X, 0, [X | _]).
% средний дом находится в середине списка
middle(nth(I, L), I, L):-
I = length(L) div 2.
% пары соседних домов, где дом B находится справа от дома A
right(A, I, B, J, L):-
right(A, 0, I, B, 1, J, L).
right(A, I, I, B, J, J, [A, B | _]).
right(A, C1, I, B, C2, J, [_ | L]):-
right(A, C1 + 1, I, B, C2 + 1, J, L).
% пары соседних домов
next(A, Na, B, Nb, Houses):-
right(A, Na, B, Nb, Houses).
next(A, Na, B, Nb, Houses):-
right(B, Nb, A, Na, Houses).
solve() = Houses20:-
Houses = [h(empty, empty, empty, empty, empty) ||
_ = std::fromTo(0, 4)],
% англичанин живет в красном доме
memberIndex_nd(H1, C1, Houses),
setNth(C1, Houses,
colour(nationality(H1, "Englishman"), "red"), Houses1),
% у испанца есть собака
memberIndex_nd(H2, C2, Houses1),
setNth(C2, Houses1, pet(nationality(H2, "Spaniard"), "dog"),
Houses2),
% кофе пьют в зеленом доме
memberIndex_nd(H3, C3, Houses2),
setNth(C3, Houses2, drink(colour(H3, "green"), "coffee"),
Houses3),
% украинец пьет чай
memberIndex_nd(H4, C4, Houses3),
setNth(C4, Houses3, drink(nationality(H4, "Ukrainian"), "tea"),
Houses4),
% зеленый дом – первый справа от дома цвета слоновой кости
right(H5, C5, H6, C6, Houses4),
setNth(C5, Houses4, colour(H5, "ivory"), Houses5),
setNth(C6, Houses5, colour(H6, "green"), Houses6),
% курильщик "Winston" держит улиток
memberIndex_nd(H7, C7, Houses6),
setNth(C7, Houses6, pet(cigarette(H7, "Winston"), "snails"),
Houses7),
% сигареты "Kool" курят в жёлтом доме
memberIndex_nd(H8, C8, Houses7),
setNth(C8, Houses7, colour(cigarette(H8, "Kool"), "yellow"),
Houses8),
% молоко пьют в среднем доме
middle(H9, C9, Houses8),
setNth(C9, Houses8, drink(H9, "milk"), Houses9),
% норвежец живет в крайнем слева доме
leftmost(H10, C10, Houses9),
setNth(C10, Houses9, nationality(H10, "Norwegian"),
Houses10),
% курящий "Chesterfield", живет по соседству с лисой
next(H11, C11, H12, C12, Houses10),
setNth(C11, Houses10, cigarette(H11, "Chesterfield"),
Houses11),
setNth(C12, Houses11, pet(H12, "fox"), Houses12),
% "Kool" курят в доме, соседнем с домом, где имеется лошадь
next(H13, C13, H14, C14, Houses12),
setNth(C13, Houses12, cigarette(H13, "Kool"), Houses13),
setNth(C14, Houses13, pet(H14, "horse"), Houses14),
% предпочитающий "Lucky Strike" пьет апельсиновый сок
memberIndex_nd(H15, C15, Houses14),
setNth(C15, Houses14, drink(cigarette(H15, "Lucky Strike"),
"orange juice"), Houses15),
% японец курит сигареты "Parliament"
memberIndex_nd(H16, C16, Houses15),
setNth(C16, Houses15, cigarette(nationality(H16, "Japanese"),
"Parliament"), Houses16),
% норвежец живет в доме рядом с голубым домом
next(H17, C17, H18, C18, Houses16),
setNth(C17, Houses16, nationality(H17, "Norwegian"),
Houses17),
setNth(C18, Houses17, colour(H18, "blue"), Houses18),
% у одного из мужчин есть зебра
memberIndex_nd(H19, C19, Houses18),
setNth(C19, Houses18, pet(H19, "zebra"), Houses19),
% один из мужчин пьет воду
memberIndex_nd(H20, C20, Houses19),
setNth(C20, Houses19, drink(H20, "water"), Houses20).
run():-
Houses = solve(),
forAll(Houses, {(h(C, N, P, D, S)):-
writef("%-10%-14%-10%-16%\n", C, N, P, D, S)}),
fail;
_ = readLine().
В следующей программе моделируется фейерверк. Она представляет собой анимационную поздравительную открытку (рис. 15.1).
(рис 15.1) Новогодний фейерверк
На фоне праздничного фейерверка появляются поздравительные надписи, выбранные случайным образом. Цвет надписей и цвет каждого элемента фейерверка также выбирается случайным образом. На каждую надпись приходится три залпа. Местоположение фейерверка и надписи выбирается случайным образом перед каждым залпом. Перед появлением или исчезновением каждого элемента фейерверка делается небольшая пауза. Поток залпов и поздравлений прекращается после щелчка левой кнопкой мыши в окне консоли.
open core, console, console_native, math, list, programControl
constants
% Congratulation-phrases for random choice
phraseList : string* =
[
"Счастливого Нового Года и Рождества!",
"From Russia with love!",
"Vivat Visual Prolog!",
"We wish you a Merry Christmas and a happy New Year!",
"Joyeux Noёl et bonne Annee!",
"Frohe Weihnachten und ein gutes neues Jahr!",
"Buon Natale e felice Anno Nuovo!",
"Feliz Navidad y Prospero Ano Nuevo!",
"Glaedelig Jul og Godt Nytar!",
"Kurisumasu Omedeto!",
"Natale hilareet Annum Faustum!"
].
% Symbols that will be randomly used for building a firework
symbolList : string* = ["*", "+", "x"].
class facts
xp : unsigned16 := 28.
yp : unsigned16 := 37.
phrase : string := "".
n : positive := 3.
class predicates
ellipse: (integer Rx, integer Ry, integer XCent, integer YCent,
string Symbol, positive Sleep).
arc: (integer Rx, integer Ry, integer XCent, integer YCent,
real PercentBeg, real PercentEnd, string Symb, positive Sleep).
line: (coord P1, coord P2, string, positive Sleep).
print: (string Symbol).
stop: () determ.
clauses
print(Symbol):-
setTextAttribute(math::random(7) + 9),
write(Symbol).
% drawing an ellipse with the center in (XCent; YCent)
ellipse(Rx, Ry, XCent, YCent, Symbol, Sleep):-
arc(Rx, Ry, XCent, YCent, 0, 1, Symbol, Sleep).
% drawing an arc with the center in (XCent; YCent)
arc(Rx, Ry, XCent, YCent, PercentBeg, PercEnd, Symb, Sleep):-
X = std::downTo(round(PercEnd *31), round(PercentBeg *31)),
Angle = (2 * pi) * (X / 32),
XCur = round(XCent + Rx * cos(Angle)),
YCur = round(YCent - Ry * sin(Angle)),
setLocation(coord(XCur + 0, YCur + 0)),
print(Symb),
sleep(Sleep),
fail;
succeed().
% Drawing a vertical line
line(coord(X, Y1), coord(X, Y2), Symbol, Sleep):- !,
(Y = std::fromTo(Y1, Y2),
setLocation(coord(X, Y)),
print(Symbol),
sleep(Sleep),
fail;
succeed()).
% Drawing a non-vertical line
line(coord(X1, Y1), coord(X2, Y2), Symbol, Sleep):-
X = std::between(X1, X2),
Y = round(Y1 + (X - X1) * (Y2 - Y1) / (X2 - X1)),
setLocation(coord(X, Y + 0)),
print(Symbol),
sleep(Sleep),
fail;
succeed().
stop():-
L = getEventQueue(),
mouse(_, _, 1, _, 0) = getMember_nd(L),
!.
% Drawing a firework using random symbols from the list
% and replacing the firework by that of blank spaces
constants
% Parametres for 7 branches of the firework
startx : integer* = [42, 38, 42, 38, 41, 39, 39].
starty : integer* = [20, 20, 18, 18, 21, 21, 18].
vx : integer* = [7, -6, 4, -3, 5, -5, -2].
vy : integer* = [7, 7, 9, 9, 5, 5, 11].
class facts
parametres : tuple{integer, integer, integer, integer}* :=
zip(startx, starty, vx, vy).
class predicates
printPoint: (real X0, real Xspeed, real T, real Y0, real Yspeed,
real G, string Symbol).
firework: (boolean IsDrawing, real X0, real Y0, real G,
positive Sleep, string Symbol, positive SymbolListLength).
getSymbol: (boolean IsDrawing, string Symbol, positive) -> string.
clauses
/* Calculation of the trajectory of a thrown object (symbol) for
forming a firework line. The object is thrown at certain angle
and falls with free-fall acceleration G */
printPoint(X0, Xspeed, T, Y0, Yspeed, G, Symbol):-
X = round(X0 + XSpeed * T),
Y = round(Y0 - (YSpeed * T - G * T ^ 2/2)),
setLocation(coord(X + 0, Y + 0)),
print(Symbol).
firework(IsDrawing, Xc, Yc, G, Sleep, Symbol, N):-
I = std::fromTo(0, 29),
forAll(parametres, {(tuple(Cx, Cy, Vx, Vy)):-
printPoint(Cx - Xc, Vx, I * 0.1, Cy + Yc, Vy, G,
getSymbol(IsDrawing, Symbol, N))}),
sleep(Sleep),
fail;
succeed().
getSymbol(true, _, N) = nth(random(N), symbolList):- !.
getSymbol(_, Symbol, _) = Symbol.
run():-
FSleep = 15,
setConsoleTitle("Happy New Year"),
%A circle that will contain the characters
ellipse(22, 11, 40, 12, "*", 0),
vpi::alarm(2),
% Three
arc(7, 4, 40, 7, -0.25, 0.40, "$", FSleep),
arc(9, 5, 40, 16, -0.43, 0.25, "$", FSleep),
sleep(750),
% Erasing 'three'
% To erase something we replace it by the same figure
% formed by blank spaces
arc(7, 4, 40, 7, -0.25, 0.40, " ", FSleep),
arc(9, 5, 40, 16, -0.43, 0.25, " ", FSleep),
vpi::alarm(2),
% Two
arc(7, 4, 40, 7, -0.1, 0.50, "$", FSleep),
line(coord(46, 9), coord(34, 18), "$", FSleep),
line(coord(33, 19), coord(48, 19), "$", FSleep),
sleep(750),
% Erasing 'two'
arc(7, 4, 40, 7, -0.1, 0.50, " ", FSleep),
line(coord(46, 9), coord(34, 18), " ", FSleep),
line(coord(33, 19), coord(48, 19), " ", FSleep),
vpi::alarm(2),
% One
line(coord(33, 11), coord(43, 5), "$", FSleep),
line(coord(43, 6), coord(43, 19), "$$", FSleep),
sleep(750),
% Erasing 'one'
line(coord(33, 11), coord(43, 5), " ", FSleep),
line(coord(43, 6), coord(43, 19), " ", FSleep),
vpi::alarm(2),
% Zero
ellipse(11, 7, 40, 12, "$$", FSleep),
sleep(200),
% Erasing 'zero'
ellipse(11, 7, 40, 12, " ", 0),
% Erasing the initial circle
ellipse(22, 11, 40, 12, " ", 0),
LengthOfPhraseList = length(phraseList),
LengthOfSymbolList = length(symbolList),
vpi::alarm(3),
I = varM::new(0),
/* The cycle that draws fireworks and writes random
congratulation-phrases in a random place */
std::repeat(),
Xc = random(11),
Yc = random(5),
G = 4 + random(3),
/* The first time a congratulation-phrase appears
(all in all one phrase exists during three fireworks) */
if I:value = 0 then
setLocation(coord(xp, yp)),
phrase := nth(n, phraseList)
end if,
/* The last time the current phrase appears; it is replaced
by a string of blank spaces. Then new random location is
chosen and a new congratulation-phrase is written */
if I:value mod 3 = 0, I:value > 0 then
setLocation(coord(xp, yp)),
write(string::create(55, " ")),
phrase := nth(n, phraseList),
xp :=random(getConsoleWidth() - string::length(phrase)),
yp := random(30),
setLocation(coord(xp, yp)),
write(phrase)
% Not the first or the last time the current phrase appears
else
setLocation(coord(xp, yp)),
write(phrase)
end if,
I:value := I:value + 1,
n := random(LengthOfPhraseList),
% Drawing a firework
firework(true, Xc, Yc, G, 30, "", LengthOfSymbolList),
% Erasing the firework
firework(false, Xc, Yc, G, 3, " ", 0),
stop(),
!,
_ = readLine();
succeed().
В упражнениях 6 – 9 требуется написать программу, которая решает логическую задачу.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.