Spis treści Artykuł originalny « Poprzedni artykul Nastepny artykul »

Gry z językiem programowania “ {prolog} ”

Ludoj kun programlingvo “prolog”

Orignał w języku EsperantoMaszynowe tłumaczenie na język polski

Prolog estas programa lingvo, kiu bazas sur predikatkalkulo. Onidire tiu lingvo havas mirindajn eblojn kaj estas uzata por artefarita intelekto kaj ankaŭ en lingvistika programado. Tio estas por mi la kaŭzo pli detale rigardi la lingvon. Mian intereson pri tia programado vekis fine la ĉapitro pri logika programado en libro “Structure and Interpretation of Computer Programs” de Harold Abelson, Gerlald Jay Sussman kun Julie Sussman. La prezentita algoritmo nomiĝas angle “unification”. Mi iam ankaŭ en mia ŝtudperiodo provis prologon, sed tiam mi ne sukcesic kompreni pli ol la konata ekzemplo kun familio.

Feliĉe en nuna tempo oni per interreto tre facile povas trovi liberan programaron kaj dokumentaron. Do mi instalis SWI-Prolog kaj komencis legi la instruan interetan libron pri prologo
(Lern Prolog Now!).

La ideo de prologo estas tre alloga. Oni ne difinas kiel fari ion, sed nur logikaj reguloj. La prologo mem trovas la rezulton de demando. Do programo estas la bazo de reguloj. Tio estas pro specifaj tipoj de problemoj tre potenca ilo. La prologo estas ankaŭ tre avantaĝa por prilaboro de arbaj strukturoj kaj listoj. Kvankam en prologo oni ŝajne ne devas pensi pri la afero, kiel la programo solvas problemojn, post pli detala rigardo la interna kalkulo de prologo estas esenca afero. Do estas tre grave kiel oni difinas regulojn kaj ankaŭ la ordo de reguloj. Ne estas ankaŭ tre facile kompreni la rekursion en tiuj reguloj. Sen mia okupo pri lingvo Scheme mi preskaŭ ne havus ŝancojn kompreni la principon de akumulatoro ĉe rekursio. Mi pensas, ke sen scio pri funkciaj lingvoj kiel “Lispo, Scheme” oni tre malfacile povus kompreni prologon. Insterese, ke mi retrovis ĉe tiu kazo la saman ekzercon kiel ĉe ŝemo, kiu pritraktas la reversadon de listo kun kaj sen akumulatoro

;; naiva programado
naiverev([],[]).
naiverev([H|T],R) :- naiverev(T,RevT),append(RevT,[H],R).
;; kun akumulatoro
accRev([H|T],A,R) :- accRev(T,[H|A],R).
accRev([],A,A).
rev(L,R) :- accRev(L,[],R).

;; la sama tasko en ŝemo.
;; invert : (listof X)  ->  (listof X)
;; to construct the reverse of alox
(define (invert alox0)
  (local (;; accumulator is the reversed list of all those items
	  ;; on alox0 that precede alox
	  (define (rev alox accumulator)
	    (cond
	      [(empty? alox) accumulator]
	      [else
		(rev (rest alox) (cons (first alox) accumulator))])))
    (rev alox0 empty)))

La programo en prologo estas mirinde mallonga eĉ ĉe komparo kun ŝemo. Por mi nova estas en prologo la fakto, ke mi aŭ tuj sukcesas difini korektan regulon aŭ mi longe pensas pri 2 linioj de programo. Ekzemple mi tre longe pensis pri ekzerco 6.3.
La tasko estis: Difinu la regulon swapfl/2 kiu pruvas ĉe du listoj estas samaj, krome ke la unua kaj ka lasta elemento estis interŝanĝitaj.

swapf([1,2,3,4],[4,2,3,1])
 Yes
swapf([1,2,3,4],[1,2,3,4])
 No

La ekzerco sugestis uzi la predikaton append/3. Kaj antaŭaj ekzercoj prilaboris la predikaton final(X,List), kiu testas, ĉu X estas la fino de listo L. Do mia unua provo estis.

final([F],F)
final(F,[H|T]) :- final(F,T).
swapf([H1|T1],[H2|T2]) :-
  final(H2,T1),
  final(H1,T2),
  append(R,[H1],T2),
  append(R,[H2],T1).

La programo funkciis perfekte por jes/ne demandoj, sed kiam mi volis demandi kun variablo, la programo donis la unuan rezulton kaj poste dronis en senfina laborado.

35 ?- swapfln([1,2,3,4],[4,2,3,1]).

Yes
36 ?- swapfln([1,2,3,4],X).

X = [4, 2, 3, 1] ;

Action (h for help) ? abort
% Execution Aborted

Mi longe penis, kiel plibonigi tion. Fine mi difinis la regulojn kun rekursio kaj akumulatro.

swapflInner([End],[Begin],Begin,End).
swapflInner([E|T1],[E|T2],Begin,End) :- swapflInner(T1,T2,Begin,End).
swapfl([H1|T1],[H2|T2]) :- swapflInner(T1,T2,H1,H2).

Tiu difino sukcesis ankaŭ solvi la demandojn swapfln([1,2,3,4],X) kaj swapfln(X,[1,2,3,4]). Do prologo ne estas vere facila maniero de programado pro komencantoj kaj povas enhavi multajn aĉajn embuskojn. Mi povas imaĝi, ke de iu grando la flegado de tiuj programoj estas tre malfacila kaj bezonas longan sperton. Por mi ankoraŭ stranga estas la funkciado de predikato rek/2.

;; en tiu direkto funkcias ĉio
2 ?- rev([1,2,3],X).

X = [3, 2, 1] ;

No
;; en alia direkto okazas eraro
3 ?- rev(X,[1,2,3]).
ERROR: Out of local stack
   Exception: (209,511) accRev(_G1257054, [_G1257053, _G1257047|...], [1, 2, 3]) ?

Do ŝajne en prologo oni devas zorge pripensi, kien oni povas enmeti variablojn. En la instrua libro oni tamen neniam pri tio diris kaj ŝajne komentarojn kaj priskriboj de predikatoj ne estas kutimaj.

Nun mi daŭre lernos tiun lingvon kaj mi volas apenaŭ vidi kelkajn ekzemplojn pri lingvistika programado per prologo. Interesa estas ankaŭ por mi vidi la programojn, kiuj estas taŭgaj por reala mondo. Do programoj, kiu havas uzulan interfacon aŭ prilaboras dosierojn. Feliĉe la labormanieron de prologo oni povas uzi de ankaŭ en aliaj lingvoj. Ofte medioj por prologo ebligas la komunikadon kun programoj skribitaj en C kaj Java. Ekzistas ankaŭ moduloj por javo, kiu ebligas la uzadon de logika programado. Ofte tiuj moduloj estas uzataj por difini kaj traserĉi bazojn de datumoj.

leciono por Esperantilo

Certe de programlingvo prolog oni povas lerni multon ankaŭ por aliaj programaj lingvoj. La ĉefa algoritmo de prologo, kiu estas “unification” oni povas ankaŭ programi en aliaj lingvoj. Ankaŭ ekzistas ekzempla programado por Tcl. Mi pensas, ke per metodoj de prologo, oni povus tre pure difini la regulojn por serĉado kaj transformigo de sintaksaj arboj. Penseblaj estas reguloj por gramatika korektilo, kiuj estas difinitaj laŭ logika maniero.

Nun reguloj aspektas kiel sube:

match {
      folioj {
         OR {b kilogramo b kvanto b litro b amaso b metro}
         v de
      }
}

Tiu lingvo estas tre simila al prefiksa predikata matematiko (+ 2 2) kiu estas uzata en ŝemo. Tio nun sufiĉas por facilaj reguloj.
Esprimo (v de) signifas = (folio de sintaksa arbo, kies vorto estas “de”). Se oni povus uzi variablojn kaj logikaj reguloj, tiuj difinoj povus estas pli kompleksaj. Ekzistas nun projektoj, kiuj uzas prologon por difini regulojn de XML-transformado. Onidire tiuj reguloj estas pli klaraj ol tiuj de XSLT. La alia eblo estas la difino de sintaksa analizilo per Prologo. La fama DLT projekto uzis prologon por tiu tasko. Mi havas dubojn pri tio, ĉu vere prologo estas tre taŭga por tiu celo. Unue oni plej ofte interesas ne pri ĉiuj eblaj analizoj sed nur pri unu analizo, kiu estas la plej verŝajna. Due oni volas havi la rezultojn de analizo, eĉ se la plena korekta analizo ne estas ebla. Tial la labormaniero de prologo, kiu estas “top-down” ne estas avantaĝa. Mi pensas ankaŭ, ke difino de esceptoj de natura lingvo (ankaŭ de Esperanto) povas esti tre malfacila en prologo. Mi ĝis nun ne trovis la pritakson de tiu problemo kaj estas tre neverŝajne, ke tio ekzistus por Esperanto. Ŝajne ĉiuj nunaj sintaksaj analiziloj ne plu uzas prologon aŭ logikan programadon. Eble la ĉefa kazo estas la rapideco de tiuj programoj. La alia kampo estas la difino de semantikaj retoj. Tio ŝajne estas la forta trajto de prologo. En prologo estas eble difini demandoj sur rekursiaj strukturoj kaj evidente semantikaj retoj estas rekursiaj. Tio ne estas eble en normala SQL de kutimaj datumbazoj. Semantikaj retoj estas kondiĉo por pli bona maŝina tradukado.

Ne estas facila tasko tralabrori la abundan teorion kaj enprogrami tion en la taŭga programo, kiu donas al la uzantoj iun profiton. Ofte estas por uzantoj tre utilaj tute simplaj funkcioj. Aliaj funkcioj bezonas fundan teorion kaj longan rezonadon.
Avantaĝe estas, ke pro interreto nun eĉ la neprofesiulo kiel mi havas la senpagan aliron al modernaj programoj kaj dokumentaro.
Ankaŭ hodiaŭaj hejmaj komputiloj estas sufiĉe rapidaj por tiaj taskoj.

Prolog jest język programowy, który bazuje na {predikatkalkulo}. Rzekomo ten język ma kapitalne możliwości i jest użytkowany dla sztucznego intelektu i także w lingwistycznym programowaniu. To jest dla mnie powód szczegółowiej patrzeć na język. Mój interes o takim programowaniu budził w końcu rozdział o logicznym programowaniu w książce “Structure and Interpretation of Computer Programs” od Haroldu {Abelson}, Gerlald Jay {Sussman} z Czerwcowo {Sussman}. Przedstawiony algorytm nazywa się z angielskiego “unification”. Kiedyś także w moim {ŝtudperiodo} próbowałem przedmowę, lecz wtedy ja nie {sukcesic} rozumieć więcej aniżeli znany przykład z rodziną.

Szczęśliwie w obecnym czasie siecią globalną bardzo lekko można znaleźć wolne oprogramowanie i dokumentację. Więc instalowałem {SWI-Prolog} i zacząłem odtwarzać szkoleniową międzymałą książkę o przedmowie
(Lern Prolog Now!).

Pomysł przedmowy jest bardzo atrakcyjny. Nie definiuje się jak zrobić coś, lecz jedynie logiczne prawidła. Przedmowa sama znajduje wynik pytania. Więc program jest bazą prawideł. To jest z powodu swoistych typów problemów bardzo przemożny przyrząd. Przedmowa jest także bardzo przyjazna dla obróbki drzewnych struktur i spisów. Aczkolwiek w przedmowie na pozór nie trzeba myśleć o sprawie, jak program rozwiązuje problemy, po dokładniejszym spojrzeniu wewnętrzna kalkulacja przedmowy, jest merytoryczna sprawa. Więc jest bardzo ważnie jak definiuje prawidła i także ład prawideł. Nie jest także bardzo lekko rozumieć rekursję w tych prawidłach. Bez mojej posady o języku {Scheme} nieomal nie miałbym szans rozumieć zasadę akumulatora przy rekursji. Myślę, że bez wiedzy o funkcjonalnych językach jak “{Lispo}, {Scheme}” bardzo trudno można by było rozumieć przedmowę. {Insterese}, że odnalazłem przy tym przypadku takie same zadanie jak przy {ŝemo}, który zajmuje się {reversadon} spisa z i bez akumulatorem

;; naiva programado
naiverev([],[]).
naiverev([H|T],R) :- naiverev(T,RevT),append(RevT,[H],R).
;; kun akumulatoro
accRev([H|T],A,R) :- accRev(T,[H|A],R).
accRev([],A,A).
rev(L,R) :- accRev(L,[],R).

;; la sama tasko en ŝemo.
;; invert : (listof X)  ->  (listof X)
;; to construct the reverse of alox
(define (invert alox0)
  (local (;; accumulator is the reversed list of all those items
	  ;; on alox0 that precede alox
	  (define (rev alox accumulator)
	    (cond
	      [(empty? alox) accumulator]
	      [else
		(rev (rest alox) (cons (first alox) accumulator))])))
    (rev alox0 empty)))

Program w przedmowie jest wspaniale krótki nawet przy porównaniu z {ŝemo}. Dla mnie nowego jest w przedmowie fakt, że ja albo natychmiast mam powodzenie definiować prawidło korektowe albo długo myślę o 2 liniach programu. Na przykład bardzo długo myślałem o zadaniu 6.3.
Zadanie było: Definiuj prawidło {swapfl} / 2, który udowadnia przy dwóch spisach jest taki samy, prócz tego, że pierwszy i {ka} ostatni element został zamieniony.

swapf([1,2,3,4],[4,2,3,1])
 Yes
swapf([1,2,3,4],[1,2,3,4])
 No

Zadanie podsuwało użytkować orzeczenie {append} / 3. I uprzednie zadania przetwarzały orzeczenie {final} (X, List), który testuje, czy X jest końcem spisa L. Więc moja pierwsza próba była.

final([F],F)
final(F,[H|T]) :- final(F,T).
swapf([H1|T1],[H2|T2]) :-
  final(H2,T1),
  final(H1,T2),
  append(R,[H1],T2),
  append(R,[H2],T1).

Program funkcjonował wspaniale dla tak / nie pytania, lecz, kiedy chciałem zapytać ze zmienną, program dał pierwszy wynik i potem utonął w bezgranicznej pracy.

35 ?- swapfln([1,2,3,4],[4,2,3,1]).

Yes
36 ?- swapfln([1,2,3,4],X).

X = [4, 2, 3, 1] ;

Action (h for help) ? abort
% Execution Aborted

Długo trudziłem się, jak ulepszyć to. W końcu definiowałem prawidła z rekursją i {akumulatro}.

swapflInner([End],[Begin],Begin,End).
swapflInner([E|T1],[E|T2],Begin,End) :- swapflInner(T1,T2,Begin,End).
swapfl([H1|T1],[H2|T2]) :- swapflInner(T1,T2,H1,H2).

Ta definicja miała powodzenie także rozwiązywać pytania {swapfln} ([ 1, 2, 3, 4 ], X) i {swapfln} (X, [ 1, 2, 3, 4 ]). Więc przedmowa nie jest naprawdę łatwym sposobem programowania z powodu początkujących i może mieścić liczne wstrętne zasadzki. Mogę {imaĝi}, że od jakiegoś zasięgu konserwacja tych programów jest bardzo trudna i potrzebuje długie doświadczenie. Dla mnie jeszcze osobliwego jest operacja orzeczenia {rek} / 2.

;; en tiu direkto funkcias ĉio
2 ?- rev([1,2,3],X).

X = [3, 2, 1] ;

No
;; en alia direkto okazas eraro
3 ?- rev(X,[1,2,3]).
ERROR: Out of local stack
   Exception: (209,511) accRev(_G1257054, [_G1257053, _G1257047|...], [1, 2, 3]) ?

Więc na pozór w przedmowie trzeba starannie przemyśleć, dokąd można wstawiać zmienne. We szkoleniowej książce jednak nigdy o tym nie powiedziano i na pozór komentarze i opisy orzeczeń nie są zwyczajne.

Teraz nadal będę uczyć się tego języka i chcę zaledwie widzieć kilka przykładów o lingwistycznym programowaniu przedmową. Interesujące jest także dla mnie widzieć programy, które są zdatne dla rzeczywistego świata. Więc programy, który ma użytkowniczy interfejs albo przetwarza pliki. Szczęśliwie {labormanieron} przedmowy można użytkować od także w innych języków. Często środowiska dla przedmowy umożliwiają komunikację z programami napisane w C i {Java}. Istnieją także moduły dla {javo}, który umożliwia użycie logicznego programowania. Często te moduły są użytkowane aby definiować i przeszukać bazy danych.

lekcja dla Esperantilo

Z pewnością od języka programowania prolog można uczyć się mnóstwa także dla innych języków programowych. Główny algorytm przedmowy, która jest “unification” można także programować w innych językach. Także istnieje przykładowe programowanie dla Tcl. Myślę, że metodami przedmowy, można by było bardzo czysto definiować prawidła dla poszukiwania i transformacji syntaktycznych drzew. Możliwe do myśli są prawidła dla gramatycznego {korektilo}, które są konieczne według logiczny sposób.

Teraz prawidła wyglądają jak pod spodem:

match {
      folioj {
         OR {b kilogramo b kvanto b litro b amaso b metro}
         v de
      }
}

Ten język jest bardzo podobny do orzeczeniowej matematyki prefiksowej (+ 2 2) który jest użytkowany w {ŝemo}. To teraz starcza dla łatwych prawideł.
Wyrażenie ({v} od) znaczy = (liść syntaktycznego drzewa, czyjego słowo jest “od”). Jeśli można by było użytkować zmienne i logiczne prawidła, te definicje mogłyby są bardziej skąplikowane. Istnieją teraz projekty, które użytkują przedmowę aby definiować prawidła {XML-transformado}. Rzekomo te prawidła są czystsze aniżeli ci XSLT. Inna możliwość jest definicją syntaktycznego analizatora Przedmową. Sławny DLT projekt użytkowało przedmowę dla tego zadania. Mam wątpliwości o tym, czy naprawdę przedmowa jest bardzo zdatna dla tego celu. Po pierwsze najczęściej zaciekawia się nie o wszystkich możliwych analizach lecz jedynie o jednej analizie, która jest najprawdopodobniejsza. Po drugie chce się mieć wyniki analizy, nawet, jeśli pełna analiza korektowa nie jest możliwa. Dlatego {labormaniero} przedmowy, która jest “{top-down}” nie jest przyjazna. Myślę także, że definicja wyjątków naturalnego języka (także od Esperanto) może być bardzo trudna w przedmowie. Do teraz nie znalazłem oceny tego problemu i jestem bardzo nieprawdopodobnie, że to istniałoby dla Esperanto. Na pozór wszyscy obecni syntaktyczni analizatorzy więcej nie użytkują przedmowę albo logiczne programowanie. Przypuszczalnie główny przypadek jest prędkością tych programów. Inny zakres jest definicją semantycznych sieci. To na pozór jest silna cecha przedmowy. W przedmowie są przypuszczalnie definiować pytania na rekursywnych strukturach i oczywiście semantyczne sieci są rekursywne. To nie jest przypuszczalnie w normalnym SQL zwyczajnych baz danych. Semantyczne sieci są warunkiem dla lepszego tłumaczenia maszynowego.

Nie jest łatwe zadanie {tralabrori} rzęsistą teorię i {enprogrami} to we zdatnym programie, który daje użytkownikom jakiś zysk. Często są dla użytkowników bardzo użytecznych całkiem proste funkcje. Inne funkcje potrzebują dogłębną teorię i długi rezonans.
Na dobre jest, że z powodu sieci globalnej teraz nawet niefachowiec jako ja ma bezpłatny przystęp do nowoczesnych programów i dokumentacji.
Także dzisiejsze domowe komputery są dosyć szybkie dla takich zadań.

Artukuł został przetłumaczony w pełni maszynowo przez wolny program Esperantilo
Adres kontaktowy:
mail@xdobry.de