Назад | Содержание| Вперёд 6. 2. Обработка файлов термов empty-line...

Назад | Содержание| Вперёд

6. 2.    Обработка файлов термов

6. 2. 1.    reаd  и write

Встроенный предикат readиспользуется для чтения термов из текущеговходного потока. Цель

        read( X)

вызывает чтение следующего терма Т исопоставление его с X. Если Х - переменная, то врезультате Х конкретизируется и становитсяравным Т. Если сопоставление терпит неудачу, цельread( X) тоже терпит неудачу. Предикат read- детерминированный в том смысле, что в случаенеуспеха не происходит возврата для вводаследующего терма. После каждого терма во входномфайле должна стоять точка или пробел, или символвозврата каретки.

Если read( X) вычисляется в тот момент,когда достигнут конец текущего входного файла,тогда Х конкретизируется атомом end_of_file(конец файла).

Встроенный предикат writeвыводит терм. Поэтому цель

        write( X)

выведет терм Х в текущий выходной файл. Х будетвыведен в той же стандартной форме, в какойобычно пролог-система изображает на экране илираспечатке значения переменных. Полезнойособенностью Пролога является то, что процедура write"знает", как изображать любой терм, как бысложен он не был.

Существуют дополнительные встроенныепредикаты для форматирования вывода. Онивставляют пробелы в дополнительные строки ввыходной поток. Цель

        tab(N)

выводит N пробелов. Предикат nl (без аргументов) приводит кпереходу на новую строку.

Следующие примеры иллюстрируют использованиеэтих процедур.

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

        куб( N, С) :-

             С is N * N * N.

Предположим далее, что мы хотим применить еедля вычисления кубов элементов некоторойпоследовательности чисел. Это можно сделать спомощью последовательности вопросов:

        ?-  куб( 2, X).

        Х = 8

        ?-  ку6( 5, Y).

        Y = 125

        ?-  куб( 12, Z).

        Z = 1728

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

        куб :-

               read( X),

               обработать( X).

        обработать( стоп) :-  !.

        обработать( N) :-

               С is N * N * N,

               write( С),

               куб.

Это был пример программы, декларативный смыслкоторой трудно сформулировать. В то же время еепроцедурный смысл совершенно ясен: чтобывычислить куб, сначала нужно считать X, азатем его обработать; если Х = стоп, товсе сделано, иначе вывести Х3 и рекурсивнозапустить процедуру куб для обработкиостальных чисел.

С помощью этой новой процедуры таблица кубовчисел может быть получена таким образом:

        ?-  куб.

        2.

        8

        5.

        125

        12.

        1728

        стоп.

        yes

Числа  2,  5  и  12  были введеныпользователем с терминала, остальные числа быливыведены программой. Заметьте, что после каждогочисла, введенного пользователем, должна стоятьточка, которая сигнализирует о конце терма.

Может показаться, что приведённую процедуру кубможно упростить. Однако следующая попытка такогоупрощения является ошибочной:

        куб :-

              read(стоп),   !.

        куб :-

              read( N),

              С is N * N* N,

              write( С),

              куб.

Причина, по которой эта процедура работаетнеправильно, станет очевидной, если проследить,какие действия она выполняет с входнымаргументом, скажем с числом 5. Цель read( стоп)потерпит неудачу при чтении этого числа, и онобудет потеряно навсегда. Следующая цель readвведет следующий терм. С другой стороны можетслучиться, что сигнал стоп будет считанцелью read( N), что приведет к попыткеперемножить нечисловую информацию.

Процедура куб ведет диалог междупользователем и программой. В таких случаяхобычно желательно, чтобы программа перед тем, какчитать с терминала новые данные, дала сигналпользователю о том, что она готова к приемуинформации, а также, возможно, и о том, какого видаинформация ожидается. Это делается обычно путемвыдачи "приглашения" перед чтением. Нашупроцедуру куб можно для этого изменить,например, так:

        куб :-

              write('Следующее число, пожалуйста:'),

              read( X),

             обработать( X).

        о6работать( стоп) :-  !.

        обработать( N) :-

              С isN * N * N,

              write('Куб'),  write( N),  write( 'равен').

              write(С), nl,

              куб.

Разговор с новой версией мог бы быть, например,таким:

        ?-  куб.

        Следующее число,пожалуйста: 5.

        Куб 5 равен 125

        Следующее число,пожалуйста: 12.

        Куб 12 равен 1728

        Следующее число,пожалуйста: стоп.

        yes

В некоторых реализациях для того, чтобыприглашение появилось на экране передчтением, необходимо выдать дополнительныйзапрос (такой, скажем, как ttyflush)после записи.

В последующих разделах мы увидим некоторыетипичные примеры операций, в которых участвуютчтение и запись.

6. 2. 2.    Вывод списков

Кроме стандартного прологовского формата длясписков существуют несколько другихестественных форм их внешнего представления,которые в некоторых ситуациях являются болеепредпочтительными. Следующая процедура

        вывспис( L)

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

        вывспис( [ ]).

        вывспис( [X | L) :-

               write( X), n1.

               вывспис( L).

Если у нас есть список списков, то одной изестественных форм его выводе является такая, прикоторой все элементы каждого списказаписываются на отдельной строке. Для этого мыопределим процедуру вывспис2. Вотпример ее использования:

        ?-  вывспис2( [ [а, b,с], [d, e, f], [g, h, i] ] ).

        а   b   с

        d   e   f

        g   h   i

Процедура, выполняющая эту работу, такова:

        вывспис2( [ ]).

        вывспис2( [L | LL] ) :-

               строка( L), n1,

               вывспис1( LL).

        строка( [ ]).

        строка( [X | L] ) :-

               write( X), tab( 1),

               строка( L).

Список целых чисел иногда удобно представить ввиде диаграммы. Следующая процедура диагрвыводит список в такой форме (предполагается, чточисла списка заключены между 0 и 80). Пример ееиспользования:

        ?- диагр( [3, 4, 6, 5] ).

        ***

        ****

        ******

        *****

        Процедуру диагрможно определить так:

               диагр( [N | L]) :-

                   звездочки( N), n1,

                   диагр( L).

               звеэдочки( N) :-

                   N > 0,

                   write( *),

                   Nl is N - 1,

                   звездочки( Nl).

               звездочки( N) :-

                   N =< 80.

6. 2. 3.    Формирование термов

Предположим, наша программа имеет дело ссемьями, которые представлены в виде термов так,как это сделано в гл. 4 (рис. 4.1). Тогда, если,перемен-

        родители

               том фокс, датарожд 7 май 1950, работает bbс,

                                                                       оклад 15200

               энн фокс, датарожд 9 май 1951, неработает

        дети

               пат фокс, датарожд 5 май 1973, неработает

               джим фокс, датарожд 5 май 1973, неработает

Рис. 6. 2.  Улучшенныйформат вывода термов, представляющих семью.

ная F конкретизирована термом, изображенный нарис. 4.1, то цель

        write( F)

вызовет вывод этого терма в стандартной формепримерно так:

        семья( членсемьи(том, фокс, дата( 7, май,1950),

                        работает( bbс, 15200)),

               членсемьи( энн, фокс, дата( 9, май, 1951),

                        неработает),

               [членсемьи( пат, фокс, дата( 5, май, 1973),

                        неработает),

               членсемьи( джим, фокс, дата( 5, май, 1973),

                        неработает)])

Здесь содержится полная информация, однакоформа представления легко может запутать,поскольку трудно проследить, какие ее частиобразуют самостоятельные семантические единицы.Поэтому обычно предпочитают выводить такуюинформацию в каком-либо формате, например так,как показано на рис. 6.2. Процедура

        вывсемью( F)

с помощью которой это достигается, приведена нарис. 6.3.

        вывсемью( семья (Муж, Жена, Дети) :-

               nl, write( родители), nl, nl,

               вывчленсемьи( Муж), nl,

               вывчленсемьи( Жена), nl, nl,

               write( дети), nl, nl,

               вывчленсемьи( Дети).

        вывчленсемьи(членсемьи( Имя, Фамилия, дата( Д, М, Г), Работа) ) :-

               tab(4), write( Имя),

               tab(1), write( Фамилия),

               write( ', дата рождения'),

               write( Д), tab( 1),

               write( M), tab( 1),

               write( Г), write( ','),

               вывработу( Работа).

        вывсписчлсемьи( [ ]).

        вывсписчлсемьи( [Р |L]) :-

               вывчленсемьи( Р), nl,

               вывсписчлсемьи( L),

        вывработу( неработает) :-

               write( неработает).

        вывработу( работаетМесто, Оклад) ) :-

               write(' работает '), write( Место),

               write( ', оклад '), write( Оклад).

Рис. 6. 3.  Программа,обеспечивающая вывод в формате, представленномна рис. 6.2.

6. 2. 4.    Обработка произвольного файлатермов

Типичная последовательность целей дляобработки файла F от начала до конца будетвыглядеть примерно так:

        . . . , see( F),обработкафайла, sеe( user), . . .

Здесь обработкафайла - процедура,которая читает и обрабатывает последовательнокаждый терм файла F один за другим до тех пор, покане встретится конец файла. Приведем типичнуюсхему для процедуры

        обработкафайла:

               обработкафайла :-

                       read( Терм),

                       обработка( Терм).

        обработка( end_of_file) :-  !.

                                   % Все сделано

        обработка( Терм) :-

               обраб( Терм),

                                   % Обработать текущий элемент

               обработкафайла.

                                   % Обработать оставшуюся часть файла

Здесь обраб( Терм) представляетпроцедуру обработки отдельного терма. В качествепримера такой обработки рассмотрим процедуру,которая выдает на терминал каждый терм вместе сего порядковым номером. Назовем эту процедуру показфайла.У нее должен быть дополнительный аргумент дляподсчета прочитанных термов:

        показфайла( N) :-

               read( Терм),

               показ( Терм, N).

        показ( Терм, N) :-  !

               write( N), tab( 2), write( Терм),

               Nl is N + 1,

               показфайла( Nl).

Вот другой пример программы обработки файлов,построенной по подобной схеме. Пусть есть файл сименем файл1, термы которого имеют форму

        изделие( НомерИзд,Описание, Цена, ИмяПоставщика)

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

        создатьфайл(Поставщик)

Например, если исходный каталог хранится вфайле файл1, а мы хотим создатьспециальный каталог в файле файл2,содержащий всю информацию о том, что поставляетГаррисон, тогда мы применим процедуру создатьфайлследующим образом:

        ?-  seе( файл1), tеll(файл2), создатьфайл( гаррисон),

                 see( user), tell( user).

Процедуру создатьфайл можноопределить так:

        создатьфайл(Поставщик) :-

               write( Поставщик), write( '.'), nl,

               создатьостальное( Поставщик).

        создатьостальное(Поставщик) :-

               read( Изделие),

               обработать( Изделие, Поставщик).

        обработать( end_ot_file):-  !.

        обработать(Изделие( Ном, Опис, Цена, Поставщик),

                               Поставщик) :-  !,

               write( Изделие( Ном, Опис, Цена) ),

               write( '.'), nl,

               создатьостальное( Поставщик).

           обработать ( _, Поставщик) :-

               создатьостальное( Поставщик).

Обратите внимание на то, что обработатьвписывает точки между термами, чтобывпоследствии файл мог быть прочитан процедурой read.

Упражнения

6. 1.    Пусть f  -  файл термов. Определите процедуру

        найтитерм( Терм)

которая выводит на терминал новый терм из f,сопоставимый с Терм'ом.

Посмотреть ответ

6. 2.    Пусть f  -  файл термов. Напишите процедуру

        найтивсетермы(Терм)

которая выводит на экран все термы из f,сопоставимые с Tepм'ом. Обеспечьте приэтом, чтобы во время поиска Терм неконкретизировался (это могло бы помешать емусопоставиться с другими термами дальше по файлу).

Посмотреть ответ

Назад | Содержание| Вперёд









Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх