What are SWI-Prolog operators?

SWI-Prolog: Operatorer og Kontrolstrukturer

30/03/2024

Rating: 4.97 (7744 votes)

Forestil dig at skulle skrive matematiske udtryk som +(*(2,3),*(4,5)) i stedet for det velkendte og letlæselige 2*3+4*5. Det er netop den slags syntaktisk kompleksitet, som operatorer i SWI-Prolog er designet til at løse. De er et fundamentalt værktøj, der forbedrer kildekodens læsbarhed markant. Sammen med Prolog's kraftfulde kontrolstrukturer giver de udviklere mulighed for at skrive elegant og effektiv logisk kode. Denne artikel er en dybdegående guide til, hvordan du forstår og anvender disse centrale elementer i SWI-Prolog, fra de grundlæggende principper til de mere avancerede anvendelser.

What are SWI-Prolog operators?
In SWI-Prolog, operators are local to a module (see also section 6.8). Keeping operators in modules and using controlled import/export of operators as described with the module/2 directive keep the issues manageable. The module system provides the operators from table 5 and these operators cannot be modified.
Indholdsfortegnelse

Hvad er Operatorer i SWI-Prolog?

I Prolog er en operator i bund og grund et prædikat (en regel eller et faktum), der kan skrives på en mere bekvem måde. Formålet er udelukkende syntaktisk – det ændrer ikke på, hvordan Prolog-programmet eksekverer, men det ændrer, hvordan du skriver og læser det. Uden operatorer ville Prolog-kode hurtigt blive en uoverskuelig samling af indlejrede prædikater, især ved matematiske og logiske operationer.

SWI-Prolog kommer med en lang række foruddefinerede operatorer, såsom +, -, *, / for aritmetik og , (konjunktion), ; (disjunktion) for logik. En vigtig pointe er, at næsten alle disse operatorer kan omdefineres af brugeren, med undtagelse af komma-operatoren (,).

Definition og Modularitet af Operatorer

Når du definerer dine egne operatorer, skal du være forsigtig. Overdreven brug kan gøre din kode 'naturlig' at se på, men samtidig sløre syntaksens grænser og gøre den svær at forstå for andre. For at imødekomme dette problem har SWI-Prolog siden version 3.3.0 indført modul-lokale operatorer. Det betyder, at en operator, du definerer i et modul, kun eksisterer inden for det specifikke modul, medmindre du eksplicit eksporterer den.

Du definerer en operator ved hjælp af prædikatet op/3:

op(Præcedens, Type, Navn)

  • Præcedens: Et heltal (typisk mellem 0 og 1200), der bestemmer rækkefølgen af operationer. Lavere tal binder stærkere.
  • Type: En atom, der definerer operatorens associativitet og position (f.eks. xfx for en infix-operator som +, eller fx for en prefix-operator).
  • Navn: Navnet på operatoren (f.eks. '==>').

Hvis du ønsker at dele en operator mellem moduler, kan du inkludere den i modulets eksportliste. For eksempel:

:- module(mit_modul, [ min_funktion/1, op(900, xfx, ==>) ]).

user-modulet fungerer som en standardtabel for alle andre moduler. Hvis du har brug for at opnå kompatibilitet med ældre Prolog-systemer uden modul-lokale operatorer, kan du definere operatoren direkte i user-modulet, selv indefra et andet modul:

:- op(900, xfx, user:(=>)).

En vigtig regel i SWI-Prolog er, at et citeret atom (f.eks. 'minop') aldrig fungerer som en operator. Den mest portable måde at forhindre et atom i at blive tolket som en operator er at omkranse det med parenteser, f.eks. (minop).

Kontrolstrukturer: Styring af Programflow

Kontrolstrukturer er de mekanismer, der styrer, hvordan et Prolog-program eksekverer. De bestemmer rækkefølgen af kald, håndterer valg og fiasko, og muliggør komplekse logiske betingelser. De fleste af disse strukturer oversættes direkte af compileren for maksimal ydeevne.

Grundlæggende Kontrolprædikater

Disse er de mest basale byggesten til at styre flowet.

PrædikatBeskrivelse
fail/0Fejler altid. Tvinger Prolog til at backtracke for at finde alternative løsninger.
false/0Synonym for fail/0, men med en mere deklarativ klang.
true/0Succederer altid. Gør ingenting, men tillader eksekveringen at fortsætte.
repeat/0Succederer altid og skaber et uendeligt antal valgpunkter. Bruges ofte i loops, der skal køre, indtil en bestemt betingelse er opfyldt.

Cut-operatoren (!): Forpligtelse og Beskæring

Cut-operatoren, skrevet som !, er en af de mest kraftfulde og potentielt farlige kontrolstrukturer i Prolog. Dens funktion er at "beskære" valgpunkter. Når et cut eksekveres, sker der to ting:

  1. Det forpligter Prolog til alle de valg, der er truffet siden den klausul, cut'et er i, blev valgt.
  2. Det fjerner alle valgpunkter, der er oprettet af målene til venstre for cut'et i den aktuelle klausul.

Kort sagt: "Jeg er sikker på, at dette er den rigtige vej. Gå ikke tilbage og prøv andre alternativer." Dette kan forbedre ydeevnen ved at forhindre unødvendig backtracking, men kan også ændre den deklarative betydning af et program. Metakald som call/1 er "ugennemsigtige" for cut, hvilket betyder, at et cut inde i et metakald kun påvirker valgpunkter skabt af det kaldte mål.

Does SWI-Prolog implement +/1 as a control structure?
In contrast to the ISO standard, but compatible with several other Prolog systems, SWI-Prolog implements \+/1 as a control structure. This implies that its argument is compiled as part of the enclosing clause and possible variables in goal positions are translated to call/1.

Eksempler på Cut's Rækkevidde

KodeEffekt af !
t0 :- (a, !, b).Beskærer valgpunkter fra a/0 og forhindrer backtracking til andre klausuler af t0/0.
t1 :- (a, !, fail ; b).Beskærer valgpunkter fra a/0 og t1/0. Da fail følger, vil t1 altid fejle, hvis a lykkes. b vil aldrig blive nået.
t2 :- (a -> b, ! ; c).Beskærer valgpunkter fra b/0 og forpligter til den første gren af disjunktionen (;).
t3 :- (a, !, b -> c ; d).Beskærer kun valgpunkter fra a/0. Valgpunkter i b/0 eller d/0 påvirkes ikke.
t4 :- call((a, !, fail ; b)).Beskærer kun valgpunkter fra a/0 inde i call/1. Valgpunkter for t4/0 påvirkes ikke.
t5 :- \+ (a, !, fail).Beskærer valgpunkter fra a/0. Dette er en almindelig måde at implementere negation-as-failure på.

Logiske Konnektorer: Konjunktion og Disjunktion

  • Konjunktion (,/2 - "og"):Goal1, Goal2 er sandt, hvis både Goal1 og Goal2 er sande.
  • Disjunktion (;/2 - "eller"):Goal1 ; Goal2 er sandt, hvis enten Goal1 eller Goal2 er sandt. Prolog vil først forsøge at bevise Goal1. Hvis det lykkes, skabes et valgpunkt, og hvis der senere backtrackes, vil Prolog forsøge at bevise Goal2.

Det anbefales kraftigt altid at bruge parenteser omkring disjunktioner for at undgå tvetydighed. Den foretrukne stil i SWI-Prolog er:

p :- a, ( b, c ; d ).

Betingede Konstruktioner: If-Then-Else

Prolog tilbyder en if-then-else-konstruktion, men dens semantik er anderledes end i de fleste andre programmeringssprog og er tæt knyttet til cut.

  • If-Then (->/2): Skrives som Betingelse -> Handling. Hvis Betingelse lykkes, forpligter Prolog sig til den første løsning af Betingelse (som om et cut var placeret efter den) og fortsætter med at eksekvere Handling. Hvis Betingelse fejler, fejler hele konstruktionen. En (If -> Then) opfører sig som (If -> Then ; fail).
  • If-Then-Else: Skrives som Betingelse -> Handling1 ; Handling2. Dette læses som (Betingelse -> Handling1) ; Handling2. Hvis Betingelse lykkes, eksekveres Handling1, og Handling2 ignoreres fuldstændigt. Hvis Betingelse fejler, eksekveres Handling2.

Soft-Cut (*->/2): Et Fleksibelt Alternativ

Soft-cut er en mindre aggressiv version af if-then-else. Den skrives som Betingelse *-> Handling1 ; Handling2.

Forskellen er, hvordan den håndterer valgpunkter i Betingelse:

  • Hvis Betingelse lykkes mindst én gang, opfører den sig som (call(Betingelse), Handling1). Det betyder, at den vil udforske alle løsninger af Betingelse, i modsætning til den almindelige ->/2, der kun tager den første.
  • Hvis Betingelse fejler fuldstændigt, opfører den sig som (\+ Betingelse, Handling2).

Et godt eksempel er at implementere et `optional` prædikat, der skal bevare alle løsninger, hvis målet lykkes, men stadig lykkes, hvis målet fejler:

optional(Goal) :- ( Goal *-> true ; true ).

Her vil optional(member(X, [a,b])) give løsningerne X=a og X=b, mens optional(member(X,[])) blot vil lykkes én gang uden at binde X.

Ofte Stillede Spørgsmål (FAQ)

Kan jeg definere mine egne operatorer globalt i SWI-Prolog?

Ja, du kan opnå global adfærd ved at definere operatoren i user-modulet (f.eks. :- op(900, xfx, user:my_op)). Den moderne og anbefalede praksis er dog at bruge modul-lokale operatorer for at skabe mere robust og vedligeholdelsesvenlig kode, der undgår navnekonflikter mellem forskellige biblioteker.

Hvad er den største forskel mellem cut (!) og soft-cut (*->)?

Den primære forskel ligger i, hvordan de håndterer løsninger. Et almindeligt cut (som er implicit i ->/2) forpligter sig til den første løsning af betingelsen og kasserer alle andre. Et soft-cut (*->/2) forpligter sig kun, hvis betingelsen lykkes mindst én gang, men det tillader backtracking for at finde alle løsninger til betingelsen, før handlingen udføres.

Hvorfor er det vigtigt at bruge parenteser omkring disjunktioner (;/2)?

Prolog's operatorpræcedens kan være forvirrende. Uden parenteser kan et udtryk som a, b ; c blive tolket anderledes, end du forventer. Parenteser fjerner al tvetydighed. a, (b ; c) betyder "a og (b eller c)", mens (a, b) ; c betyder "(a og b) eller c". Ved altid at bruge parenteser gør du din kode mere læsbar og undgår logiske fejl.

Hvordan forhindrer jeg et atom i at fungere som en operator?

Den mest pålidelige metode, der virker på tværs af Prolog-implementeringer, er at sætte atomet i parentes. For eksempel, hvis + er defineret som en operator, vil (+) blive behandlet som et almindeligt atom. I SWI-Prolog vil et citeret atom (f.eks. '+') heller ikke blive behandlet som en operator i de fleste sammenhænge.

Hvis du vil læse andre artikler, der ligner SWI-Prolog: Operatorer og Kontrolstrukturer, kan du besøge kategorien Sundhed.

Go up