Файлдармен байланыс
Файлдармен байланыс
Осыған дейін қолданушының программамен байланысының бір әдісін қолдандық: қолданушы программаға сұрақ қояды, ал программа айнымалыларды нақтылап, жауап береді. Мұндай механизм өте қарапайым әрі ыңғайлы; қарапайымдылығына қарамастан, ол ақпаратты енгізу мен шығаруды қамтамасыз етеді. Бірақ оның иілгіштігі төмен, сондықтан келесі жағдайларда бұл механизмді кеңейту қажет:
- Мәліметтерді сұрақ түрінде емес, басқа формада енгізу (мысалы, ағылшын тіліндегі сөйлем ретінде).
- Ақпаратты кездейсоқ (еркін) форматта енгізу.
- Ақпаратты тек қолданушы терминалымен ғана емес, файлдар арқылы да алмасу.
Бұл кеңейтуге арналған предикаттар әртүрлі Пролог нұсқаларында өзгеше болуы мүмкін. Мұнда стандартты предикаттарды қарастырамыз: алдымен файлдар арқылы ақпарат алмасуды, содан соң мәліметтерді әртүрлі форматта енгізу тәсілдерін.
Прологтағы ағындар (терминал және файлдар)
Кіріс және шығыс ағындарының түсінігі
Прологта терминалдан келіп түскен ақпарат кіріс ағыны ретінде, ал терминалға жіберілетін ақпарат шығыс ағыны ретінде қарастырылады. Бұл екі «псевдофайл» әдетте user атауына сілтеме жасайды. Қалған файл атауларын программист өзі қояды.
Пролог-программа жұмыс істеп тұрған кезде екі файл «активті» болады: бірі — енгізу үшін, екіншісі — шығару үшін. Олар ағымды кіріс ағыны және ағымды шығыс ағыны деп аталады. Бастапқыда бұл екі ағын терминалға сәйкес келеді.
Кіріс ағынын ауыстыру: see/1
Ағымды кіріс ағынын басқа файлға ауыстыру үшін see(ФайлАты) мақсаты қолданылады.
Мысал (идеясы)
...,
see(файл1),
файлдан_оқу(Ақпарат),
see(user),
...
Мұнда Ақпарат файл1-ден оқылады, ал енгізу ағыны қайтадан терминалға (user) ауыстырылады.
Шығыс ағынын ауыстыру: tell/1
Ағымды шығыс ағынын файлға ауыстыру үшін tell(ФайлАты) мақсаты пайдаланылады.
Мысал (идеясы)
...,
tell(файл3),
файлға_жазу(Ақпарат),
tell(user),
...
Мұнда кейбір ақпарат файл3-ке жазылады, содан кейін шығыс қайтадан терминалға бағытталады.
Ағындарды жабу және файлды өңдеу ерекшелігі
seen мақсаты ағымды кіріс файлын жабады, ал told мақсаты ағымды шығыс файлын жабады.
Файлдар тізбекті түрде өңделеді: оқу сұранысы әрдайым ағымды кіріс ағынынан оқуға алып келеді. Егер оқу файлдың соңында орындалса, жауап ретінде end_of_file атомы қайтарылады. Бір рет оқылған ақпарат қайтадан оқылмайды.
Барлық файлдар мәтіндік (символдардан тұратын) файл ретінде қарастырылады.
Файлмен енгізу-шығарудың екі тәсілі
1) Негізгі элемент — символ
Бұл тәсілде файлдың негізгі элементі ретінде бір символ алынады. Демек, енгізу/шығаруға жасалған әр сұраныс бір символдың оқылуына немесе жазылуына әкеледі.
Бұл мақсат үшін әдетте келесі предикаттар қолданылады: get, get0, put.
2) Негізгі элемент — Пролог термі
Бұл тәсілде файлдың негізгі элементі ретінде үлкенірек бірлік — Пролог термі алынады. Мұнда енгізу/шығаруға жасалған сұраныс толық термнің ағымды кіріс ағынынан оқылуына немесе ағымды шығыс ағынына жазылуына әкеледі.
Бұл үшін read және write предикаттары қолданылады. Мұнда файлдағы ақпарат термдер синтаксисіне сай болуы қажет.
Қай тәсілді қашан таңдау керек?
Файлды ұйымдастыру формасы тапсырмаға байланысты. Егер мәліметтерді термдер синтаксисіне сәйкес оқуға мүмкіндік болса, онда термдерден тұратын файлдарды таңдаған дұрыс: бір ғана шақыру арқылы мағыналы, толық фрагментті енгізуге немесе шығаруға болады.
Ал жүйе мен қолданушы арасында табиғи тілде (мысалы, ағылшын тілінде) диалог орнату қажет болса, онда файлды терм синтаксисіне бағынбайтын символдар тізбегі ретінде қарастырған жөн.
read және write предикаттары
read/1: термді оқу
read — ағымды кіріс ағынынан терм оқуға арналған орнатылған предикат. read(X) мақсаты келесі T термін оқып, оны X-пен салыстырады.
- Егер X айнымалы болса, онда X нақтыланып, оқылған термге тең болады.
- Егер беттесу орындалмаса, онда read(X) мақсаты орындалмайды.
- read — детерминирленген: ол сәтсіз аяқталған кезде келесі термді оқуға «қайтып келу» (backtracking арқылы қайта оқу) болмайды.
Файлдағы әрбір термнен кейін нүкте, не бос орын, не жолды аяқтау (carriage return / newline) символы болуы тиіс. Егер файлдың соңында read(X) орындалса, онда X end_of_file атомымен нақтыланады.
write/1: термді шығару
write термді ағымды шығыс ағынына шығарады. write(X) мақсаты X термін шығарады.
Прологтың бір ерекшелігі — write процедурасының терм қаншалықты күрделі болса да, оны қалай дұрыс көрсету керектігін «білуі».
Мысал: санның кубын есептейтін диалог
Процедуралық мағынасы
Бұл мысалдың декларативті мағынасын бірден құрастыру оңай емес, бірақ процедуралық мағынасы түсінікті: кубты есептеу үшін алдымен X-ті оқу керек; содан кейін оны өңдеу керек; егер X = тоқтау болса — жұмыс аяқталады, әйтпесе X³ шығарылады да, келесі мәндер үшін процедура рекурсивті түрде қайталанады.
Сөйлесу үлгісі
?- куб.
2.
8
5.
125
12.
1728
тоқтау.
yes
Мұнда 2, 5 және 12 сандарын қолданушы терминалдан енгізеді, ал нәтижелерді программа шығарады. Назар аударыңыз: енгізілген әр саннан кейін нүкте қойылады — бұл термнің аяқталғанын көрсетеді.
Неліктен «оңайлату» қате?
Төмендегі «оңайлату» дұрыс емес:
куб :- read(тоқтау), !.
куб :- read(N), C is N * N * N, write(C), куб.
Себебі, мысалы 5 санын оқу кезінде read(тоқтау) мақсаты сәтсіздікке ұшырайды да, оқылған терм «жоғалып кетеді»: келесі read жаңа термді оқуға көшеді. Ал «тоқтау» сигналы read(N) арқылы оқылса, онда сандық емес мәліметті көбейтуге талпыныс жасалып, қате жағдайға әкелуі мүмкін.
Қолданушыға «шақыру» (prompt) беру
Программа мен қолданушы арасында диалог жүретін кезде, программа терминалдан жаңа мәліметтерді оқымас бұрын қолданушыға дайын екенін және қандай ақпарат күтетіні туралы белгі бергені дұрыс. Бұл көбіне оқудың алдында «шақыру» мәтінін шығару арқылы іске асады.
куб :-
write('Келесі сан: '),
read(X),
обработать(X).
обработать(тоқтау) :- !.
обработать(N) :-
C is N * N * N,
write('Куб '),
write(N),
write(' тең '),
write(C),
nl,
куб.
Осы нұсқада сөйлесу барысы қолданушыға түсініктірек болады: әр енгізудің алдында программа қандай дерек күтіп тұрғанын көрсетеді.