Nella memoria le istruzioni sono in “linguaggio macchina”, qui sotto c'è un frammento di programma su PC di 15 bytes.
Indirizzo contenuto Istruzione in Assembler della memoria
1329:0100 4D DEC BP 1329:0101 5A POP DX 1329:0102 9F LAHF 1329:0103 01AB0000 ADD [BP+DI+0000],BP 1329:0107 0004 ADD [SI],AL 1329:0109 0000 ADD [BX+SI],AL 1329:010B 00FF ADD BH,BH 1329:010D FF00 INC WORD PTR [BX+SI]
I programmi, cioè le istruzioni fornite al computer sono dei file di testo, detti sorgenti, contenenti frasi di un linguaggio con regole sintattiche precise. A differenza dei linguaggi naturali, la maggior parte dei linguaggi di programmazione contengono solo frasi imperative, e dichiarative; alcuni linguaggi si basano solamente su frasi dichiarative.
Le frasi dichiarative descrivono i dati che il programma elabora, sia quelli non modificabili (costanti), che quelli modificabili durante l’elaborazione (variabili), quali contatori, aree di totalizzazione, ecc... I dati sono individuati da un nome, cui corrisponde una opportuna porzione di memoria .
Le frasi imperative sono istruzioni che elaborano i dati (ADD, MOVE, IF, ...).
Ad esempio: ADD IMPORTO TO TOTALE_IMPORTO
IL primo vantaggio di un linguaggio di programmazione è che le informazioni nella memoria sono accessibili per nome (variabile o campo, in inglese field), e non pìù per indirizzo numerico (è il programma che traduce in linguaggio macchina che ci pensa), il secondo vantaggio è che è più facile creare un programma perché ci si può concentrare su ciò che si vuole ottenere, piuttosto che su come ottenerlo.
Qui di seguito sono esaminati alcuni dei più significativi, e più usati linguaggi di programmazione.
Si possono classificare i più comuni linguaggi utilizzati in ordine di complessità ed efficacia decrescente:
Linguaggio |
Livello del linguaggio |
Note |
Macchina |
Solo un disperato scrive in linguaggio macchina |
|
Assemblatori |
Basso |
Per scrivere OS e programmi che lavorano velocemente |
C, C++, JAVA |
Medio |
Per scrivere OS e programmi che lavorano velocemente |
COBOL |
Alto |
Applicazioni commerciali |
FORTRAN |
Medio-alto |
Applicazioni scientifiche |
BASIC |
Alto |
Versatile |
LISP, PROLOG |
Medio |
Per applicazioni di Intelligenza Artificiale |
Tabella 6‑1
L'utilizzo dell'uno, piuttosto che dell'altro dipende da ciò che si vuol ottenere: un programma che gestisce una scheda audio, sarà sviluppato in Assembler o C, perché deve essere efficiente e compatto. Una procedura commerciale su grandi sistemi (Mainframe) sarà sviluppata in COBOL . Un programma di simulazione scientifica probabilmente sarà scritto in FORTRAN. VISUAL BASIC è ideale per creare programmi che interagiscono con l'utilizzatore, non hanno particolari esigenze di performance e soprattutto devono essere messi in esercizio in fretta.
I programmi, per essere eseguiti, devono essere convertire in linguaggio macchina, ciò avviene tramite programmi, detti compilatori, che trasformano le istruzioni in istruzioni di macchina. Questa operazione può dare origine ad un programma cosiddetto eseguibile (come lo sono Word, Excel, ecc.), oppure le istruzioni sono interpretate, cioè tradotte in linguaggio macchina, prima di essere eseguite, come nel caso dei VBscript e Javascript presenti in certe pagine INTERNET. Alcuni compilatori possono produrre, invece di istruzioni in linguaggio macchina, un programma in codice particolare detto p-code . Il p-code, sviluppato dalla University of California, San Diego (UCSD) agli inizi del 1970, è il codice di una "macchina virtuale", e come tale può girare su qualsiasi calcolatore, a patto che sia disponibile un interprete p-code. Il vantaggio è la portabilità dei programmi, l'efficienza di questi, pur essendo inferiore a quella dei programmi compilati, è superiore a quella dei programmi interpretati.
Sono i più vicini alla struttura delle istruzioni del calcolatore.
Il sorgente di un programma in Assembler è formati da linee di testo contenenti:
· Direttive al programma traduttore
· Dichiarazioni di variabile e di costanti
· Istruzioni
Ogni riga è generalmente formata da più parti separate da uno o più spazi:
etichetta OP Operando1,…,Operandon Commento
Dove etichetta è un eventuale nome utilizzabile nelle istruzioni di salto o il nome di una variabile o di una costante. OP è il nome di un’istruzione, di una direttiva all’assemblatore o il codice di una dichiarazione di variabile o costante. OP di regola è l’unica parte obbligatoria.
Operando1,…,Operandon dipendono dalla struttura di OP, ci sono istruzioni con 1, 2 o nessun operando. Infine commento è facoltativo, ma è un costituente importante ai fini della documentazione di un programma.
In genere i compilatori Assembler permettono di scrivere delle macro, cioè sequenze di istruzioni, in cui gli operandi possono essere parametrizzati. Lo scopo delle macro, il cui nome è usato come un'istruzione, è di evitare la scrittura di sequenze simili di istruzioni.
Esempio:MACRO &LAB SOMMA &ADD1,&ADD2 * &ADD1 = &ADD1 + &ADD2 L R1,&ADD1 * CARICO TOTALIZZATORE IN REG. 1 A R1,&ADD2 * SOMMO ADDENDO AL REG. 1 ST R1,&ADD1 * DA REG. 1 A TOTALIZZATORE MEND .... SOMMA TOT,IMPORTO ....
Gli pseudooperandi MACRO e MEND racchiudono il prototipo della macro SOMMA, il compilatore espande l'istruzione SOMMA sostituendo nel prototipo gli operandi parametrizzati &ADD1 e &ADD2 con TOT e IMPORTO rispettivamente.
Con riferimento ad un linguaggio assemblatore con solo due istruzioni, un salto su condizione e la sottrazione, la somma di due campi A e B, con l'ausilio di un campo C per i passaggi intermedi, è la seguente (tenuto conto che dopo l’istruzioione SUB V1,V2, il campo V1 conterrà V1 - V2):
Istruzione |
Contenuto di A |
Contenuto di B |
Contenuto di C |
37 |
13 |
● |
|
SUB C,C |
37 |
13 |
0 |
SUB C,B |
37 |
13 |
-13 |
SUB A,C |
50 |
13 |
-13 |
Figura 6‑2
Il COBOL ( COmmon Business Oriented Language ) è stato sviluppato nel 1959. Nonostante la sua età, è ancora uno dei linguaggi più usati per le applicazioni commerciali. COBOL è un linguaggio standardizzato disponibile su molti computer: lo stesso sorgente, con minime variazioni può essere compilato ed eseguito su diversi computer, dal mainframes al personal computers. Le istruzioni del COBOL sono in linguaggio similinglese. Questa caratteristica permette di imparare e scrivere programmi in modo piuttosto semplice.
Tutti i programmi COBOL hanno obbligatoriamente quattro parti (divisions), ognuna con uno specifico compito e nell'ordine in cui compaiono qui di seguito:
· Identification Division Identifica il programma; può anche contenere una descrizione del programma.
· Environment Division Definisce i file utilizzati dal programma, il calcolatore su cui è compilato ed il calcolatore su cui sarà eseguito.
· Data Division Contiene la descrizione dei dati di input e output.
· Procedure Division Contiene le istruzioni del programma.
All'interno delle DIVISION ci possono essere SECTION e paragrafi.
I programmi COBOL sono scritti su linee con tracciato fisso:
· Colonne 1 - 6: sono utilizzate, facoltativamente, per numerare le righe.
· Colonna 7: se presente *, è un commento. Il simbolo / indica alla stampante di andare a pagina nuova. Altri caratteri sono interpretati come continuazione di una costante della riga precedente.
· Colonne 8 - 11: è detta "Area A"; in essa devono iniziare DIVISION, SECTION, e i paragrafi.
· Colonne 12 - 72: è detta "Area B"; è l'area riservata alle istruzioni.
· Colonne 73 - 80: zona facoltativa utilizzabile per identificare il programma
In questa divisione sono fornite informazioni sul programma. L'unico paragrafo necessario è il PROGRAM-ID. Opzionalmente si può indicare l'AUTHOR, l'INSTALLATION; la DATE-WRITTEN, la DATE-COMPILED e la SECURITY.
E' la parte del COBOL dipendente dalla macchina. Contiene due sezioni: la configuration section e la input-output section. La configuration section indica tramite il paragrafo source computer il computer usato per compilare il programma e col paragrafo object computer il computer che sarà utilizzato per eseguire il programma.
ENVIRONMENT DIVISION.CONFIGURATION SECTION. SOURCE-COMPUTER. IBM-ES900. OBJECT-COMPUTER. VAX-8800.
Nell'input-output section sono fornite informazioni sui dispositivi di trattamento dei dati utilizzati nel programma. Qui di seguito un esempio.
INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT EMPLOYEE-FILE ASSIGN TO C:\EMPFILE. SELECT TRANS-FILE ASSIGN TO PRINTER. ORGANIZATION IS LINE SEQUENTIAL.
Nella DATA DIVISION sono descritti i dati utilizzati nel programma. Nella FILE SECTION sono descritti i formati dei record contenuti nei files di input e di output. La WORKING STORAGE SECTION è riservata alla descrizione delle aree di lavoro del programma e delle costanti. La LINKAGE SECTION è presente quando il programma riceve o produce dei dati non tramite file, ma da un altro programma, tramite la memoria del calcolatore, e descrive il formato di questi dati.
La definizione dei dati ha la struttura (fra parentesi [ ] clausole opzionali):
lv nome [REDEFINES nome]. lv nome [REDEFINES nome] PICTURE schema [VALUE 'valore'].
Dove lv è un numero che indica il livello del campo. Il livello 01 inizia nell'area A, i livello da da 02 a 49 indicano una sottodivisione del soprastante campo a livello 01, ed iniziano nell'area B.
PICTURE é la parola chiave che precede schema ossia ampiezza e tipo di campo; esso può essere di tipo alfabetico, alfanumerico o numerico, rispettivamente indicati con A, X, e 9. La lunghezza è data dal numero di A, X, o 9 presenti. Per i numeri, inoltre è possibile indicare i decimali ed il segno.
La prima forma è relativa a campi che sono sottolivellati, il suo tipo è X (alfanumerico) e la lunghezza è la somma delle lunghezze dei suoi sottolivelli. Esempio:
01 VERSAMENTO. 05 VERSATORE PICTURE X(30). 05 DATA_VERSAMENTO. 10 ANNO PICTURE 9999. 10 MESE PICTURE 99. 10 GIORNO PICTURE 99. 05 IMPORTO PICTURE 999V99.
E' la parte in cui è eseguita l'elaborazione dei dati. L'istruzione OPEN prepara ad accedere ai file riservandone l'utilizzo al programma; tramite l'istruzione READ i dati di input vengono portati in memoria e con l'istruzione WRITE dalla memoria sono mandati su file. L'istruzione CLOSE termina le operazioni sul file, di fatto questo è disponibile ad altre elaborazioni o altri programmi.
Le istruzioni condizionali sono PERFORM UNTIL e IF ... THEN ... ELSE, le istruzioni di assegnazione sono:
MOVE nome_di_dato/costante TO nome_di_dato ADD nome_di_dato/costante TO nome_di_dato SUBTRACT nome_di_dato/costante FROM nome_di_dato MULTIPLY nome_di_dato/costante BY nome_di_dato DIVIDE nome_di_dato/costante INTO nome_di_dato DIVIDE nome_di_dato/costante INTO nome_di_dato
Infine i comandi STOP-RUN ed EXIT rispettivamente terminano il programma o il sottoprogramma.
Il Fortran (FORmula TRANslation) è un linguaggio general purpose adatto cioè per qualsiasi tipo di problema, ma è particolarmente adatto per i calcoli matematici. Il Fortran è nato nel 1950 alla IBM ed ha avuto da allora molte versioni.
Un programma Fortran è una sequenza di linee di of testo con una sintassi fissa [1] :
Col. 1 Spazio, o "c" o "*" per commenti Col. 2-5 Etichetta opzionale Col. 6 Se diverso da blank indica continuazione della linea precedente Col. 7-72 Istruzioni Col. 73-80 Numerazione
Un semplice esempio:
program circle real r, area c Questo programma legge un numero reale e stampa c l’area del circolo di raggio r. write (*,*) 'Give radius r:' read (*,*) r area = 3.14159*r*r write (*,*) 'Area = ', area stop end
Un programma Fortran generalmente consiste di una parte principale ed eventuali sottoprogrammi. La struttura del programma principale è:
Nome del programma es. program circle Dichiarazioni es. real r, area Istruzioni es. write (*,*) 'Give radius r:' stop end
I nomi di variabile sono da 1 a 6 caratteri scelti fra {ab…z01…9}. Il primo carattere deve essere una lettera. Ogni variable deve essere dichiarata esplicitamente e se ne deve indicare il tipo; i tipi più comuni sono: integer, real, double precision, complex, logical, character.
L'istruzione condizionale può assumere diverse forme, la piu' generale è:
if (logical expression) then statements elseif (logical expression) then istruzioni : else istruzioni : endif
Nelle forme più semplici possono mancare i costrutti elseif ed else.
La più semplice, che deve essere scritta su una sola linea, è:
if (espressione logica) istruzione
Esempio (valore assoluto di x):
if (x .LT. 0) x = -x
Nell'esempio che segue viene effettuata la somma dei numeri interi da 1 ad n (si presume che ad n sia stato assegnato un valore in precedenza):
integer i, n, sum sum = 0 do 10 i = 1, n sum = sum + i write(*,*) 'i =', i write(*,*) 'sum =', sum 10 continue
Il numero 10 è una etichetta label. Le istruzioni comprese fra do 10 … e 10 continue sono ripetute fino a quando i, che è incrementato di 1 ad ogni ciclo, diventa uguale ad n.
VISUAL BASIC è un linguaggio di programmazione di alto livello, evolutosi dalle prime versioni per DOS chiamate semplicemente BASIC (Beginners' All purpose Symbolic Instruction Code). E' un linguaggio molto semplice da apprendere, in quanto le istruzioni sono parole della lingua inglese. VISUAL BASIC permette una programmazione guidata dagli eventi, lo sviluppo dei programmi si effettua in ambiente grafico.
Il linguaggio C è stato definito nel testo, ormai classico, di Kernigan e Ritchie: "The C Programming language" e sviluppato da Dennis Ritchie dei laboratori Bell inizialmente per l'elaboratore Digital PDP-11 su sistema operativo UNIX. Nel dicembre 1989 ne è stato dato lo standard ANSI.
C++ è il tradizionale linguaggio con qualche costrutto addizionale orientato alla programmazione ad oggetti. Fra le maggiori caratteristiche del linguaggio C ci sono la possibilità di definire tipi di dati, l'efficacia del codice macchina ottenuto e la capacità di sviluppare software di grandi dimensioni.
In C i blocchi di istruzione sono racchiusi fra parentesi graffe: {}, in particolare delimitano l'ambito delle istruzioni if, for e while; la singola istruzione è delimitata da ";".
Alcuni costrutti sono criptici, ad esempio:
i++, i-- invece dei più leggibili i=i+1 e i=i-1,
j -= h invece di j = j-h
Molte estensioni del linguaggio, quali le istruzioni di input/output, il trattamento delle stringhe di caratteri, ed altro, sono fornite in librerie da includere nel sorgente del programma.
La vasta diffusione e popolarità del linguaggio C è dovuta in buona parte alla sua gratuita cessione alle università.
Gli oggetti sono una evoluzione delle librerie, ma sono "attivi", cioè possono memorizzare e variare al loro interno delle informazioni e possono attivare delle funzioni su sollecitazione di particolari eventi (event driven). Nella terminologia degli oggetti le funzioni sono dette metodi, le informazioni proprietà, ed eventi le sollecitazioni a cui essi reagiscono.
La classe di un oggetto è la sua definizione, l'istanza dell'oggetto è l'oggetto utilizzato dal programma. Di un oggetto se ne possono creare più istanze.
Esempio:
class automobile proprietario string marca string endclass Actual instance 1 proprietario Beatrice marca Morris Mascot End Actual instance 2 proprietario John marca Rolls Royce End
Un linguaggio può essere considerato orientato agli oggetti (OOP Object Oriented Programming) se ha le seguenti caratteristiche: incapsulazione, ereditarietà, e identità.
§ Incapsulazione: è la caratteristica per cui si conosce dell'oggetto i metodi, le proprietà, e gli eventi, ma non come questi sono realizzati.
§ Ereditarietà: un Oggetto può essere strutturato in forma gerarchica, un oggetto eredita proprietà e metodi dai predecessori.
§ Identità: l'dentità dell'oggetto rimane anche al variare dei suoi attributi.
Le proprietà di un Oggetto sono molteplici e dipendono dal tipo di oggetto. Un oggetto grafico avrà dimensioni, posizionamento sul form, colore, bordi, sequenza di attivazione, visibilità, abilitazione, ecc... Le proprietà possono essere determinate al momento della preparazione dell'interfaccia utente, oppure variate dal programma.
Gli eventi sono le azioni dell'utilizzatore sull'oggetto, ad esempio la pressione del tasto del mouse. La "reazione" del programma all'evento è una porzione di programma, associata all'evento, che effettua qualche particolare azione.
I metodi sono delle azioni che l'oggetto compie se attivato da appositi comandi del programma. Alcuni metodi sono nativi, altri, quelli legati agli eventi, sono scritti dal programmatore.
Il Prolog (PROgramming in LOGic) è stato sviluppato all’Università di Marsiglia da Alain Colmerauer all’inizio degli anni 1970, come uno strumento di programmazione basato sulla logica, più precisamente su un sottoinsieme del calcolo dei predicati. Prolog è un linguaggio dichiarativo, in esso non vi è una sequenza di azioni, ma una raccolta di predicati (o fatti) con delle regole: in terminologia logica (v. 1.3.1 e 1.3.3 ) i predicati sono proposizioni, proposizioni con variabili ed enunciati [2] (con valore di verità "vero") e le regole sono implicazioni; l'esempio del paragrafo 1.3.3 :
Φ = {" piove" , "piove > strada bagnata"}
β = "strada bagnata"
tradotto nella sintassi Prolog ciò diventa:
piove.
bagnato:-piove.
L'esecuzione del programma è una richiesta a Prolog di dedurre dei fatti dall'insieme di informazioni che Prolog conosce, cioè l'insieme di predicati e di regole comunicate al programma; nell'esempio precedente vi è un predicato "piove" ed una regola "è bagnato se piove"; una possibile richiesta è:
goal bagnato.
La risposta di Prolog è:
yes
Mentre nel calcolo proposizionale si può dedurre unicamente il valore di verità di una proposizione, come nell'esempio visto, Prolog , invece, è in grado di dedurre, se la domanda è un predicato contenente variabili, i valori che soddisfano il predicato. Il cuore di Prolog è un motore di inferenza, cioè un (sotto)programma per ragionare logicamente sulle informazione. Prolog cerca di inferire che un'ipotesi è vera (la domanda che è stata posta), interrogando la collezione di informazione già note per essere vere. La risposta comprende tutte le soluzioni possibili.
I predicati possono esprimere sia proprietà degli oggetti che relazioni; in linguaggio naturale proprietà sono "l'erba è verde" e "Maria è una ragazza.", mentre sono relazioni, ad esempio: "a Mario piace guidare", "Parigi è la capitale della Francia".
L'assunzione basilare è che i predicati sono ciò che è conosciuto, implicitamente ciò che è conosciuto è vero.
Nella sintassi di Prolog un predicato si scrive con un nome di predicato seguito fra parentesi dall'oggetto o dagli oggetti su cui agisce; il tutto chiuso con un punto:
tipo(ferrari,sportiva). tipo(maserati,sportiva). tipo(fiat,famigliare). tipo(renault,utilitaria). piace(marco,auto). piace(elena,auto). piace(anna,bici).
Le regole sono predicati in cui un fatto è "vero" se uno o più altri fatti sono veri. La regola che definisce quando "piace guidare a Mario" potrebbe essere la seguente: "a Mario piace guidare se l'automobile è sportiva". Quindi le regole sono formate da due parti, la prima (testa) è un predicato che è reso vero dalla seconda (corpo), separata dalla prima tramite i simboli ":-" (due punti meno col significato di se), è:
piace(anna,auto):-tipo(Automobile,famigliare);tipo(Automobile,utilitaria). piace(mario,auto):-tipo(Automobile,sportiva),Automobile
La prima regola afferma che ad anna piace l'auto se e' famigliare o utilitaria, l'alternativa è indicata in Prolog col segno ";". Per la seconda regola, a mario piacciono le auto sportive purchè non siano ferrari; purchè in questo caso è tradotto con l'operatore logico e, che in Prolg è indicato con il segno ",". Non è per poco rispetto verso Anna, Mario che in Prolog compaiono in minuscolo: in minuscolo si indicano i fatti conosciuti, mentre le variabili hanno un nome che inizia con una lettera maiuscola, come la variabile Automobile che compare nelle regole.
Le regole permettono la deduzione di fatti da altri fatti, ma esse possono essere pensate come procedure. per chiedere a Prolog di compiere azioni diverse dal provare dei fatti, quali la scrittura di qualcosa o la creazione di un archivio, e in genere, ciò che un linguaggio di programmazione può fare.
Una volta fornito a Prolog un insieme di predicati, gli si possono porre domande che riguardano tali predicati; dall'esempio del paragrafo precedente, alcune domande potrebbero essere:
Domanda in linguaggio naturale |
Domanda Prolog |
Risposta |
Cosa piace ad Anna |
piace(anna,Cosa) |
Cosa=bici Cosa=auto Cosa=auto 3 Solutions |
Cosa piace a Chi |
piace(Chi,Cosa) |
Chi=antonio, Cosa=auto Chi=carla, Cosa=auto Chi=anna, Cosa=bici Chi=anna, Cosa=auto Chi=anna, Cosa=auto Chi=mario, Cosa=auto 6 Solutions |
Piacciono a Mario le auto |
piace(mario,auto) |
yes |
Piacciono a Mario le biciclette |
piace(mario,bici) |
no |
C'è qualcuno a cui piace la bici |
piace(_,bici) |
yes |
Esiste un catorcio di macchina |
tipo(Marca,catorcio) |
No Solution |
Figura 6‑3
Prolog per rispondere ad una domanda esamina i predicati che conosce: il caso più semplice è quello in cui la domanda non contiene variabili, come in piace(mario,auto), in questo caso Prolog cerca se esiste il predicato piace(mario,auto) o una regola la cui testa sia il predicato. Se non trova nulla la risposta è no, altrimenti, se esiste il predicato la risposta è yes, se esiste la regola e la coda di essa risulta vera, la risposta è yes, altrimenti è no.
Se la domanda contiene variabili, Prolog sostituisce alla variabile, via via il valore che ricava dai fatti, la risposta invece di essere yes o no è l'elencazione degli attributi che soddisfano la variabile oppure: No Solution. Alla domanda piace(anna,Cosa), Prolog ha risposto due volte Cosa=auto, ciò deriva dalla caratteristica di Prolog di cercare tutte le soluzioni, infatti la regola:
piace(anna,auto):-tipo(Automobile,famigliare);tipo(Automobile,utilitaria).
é soddisfatta due volte, poiché fra i fatti ci sono una automobile famigliare ed una automobile utilitaria.
Il segno "_" indica una variabile anonima per rispondere a domande generiche.
Prolog esamina tutti i fatti per rispondere, tranne quando la domanda è senza variabili, perciò al primo predicato verificato è in grado di rispondere yes, e quindi può interrompere la ricerca.
In Prolog le variabili sono gestite dal motore di inferenza, esse nascono libere (free), e nel corso dell'elaborazione Prolog le lega (bound) per stabilire se soddisfano la richiesta, finita l'elaborazione esse ridiventano libere. La cosa che più si avvicina al concetto di variabile dei linguaggi tradizionali, è il fatto, ed i fatti si possono aggiungere alla collezione di fatti e regole tramite un apposito predicato (assert).
La caratteristica di Prolog di cercare regole o fatti che soddisfino la domanda, è la base del suo utilizzo procedurale, in particolare se la domanda è relativa ad una regola plurima, cioè stessa testa e corpi diversi, Prolog verificherà la prima regola (quindi l'ordine delle regole è talvolta essenziale) se essa non è verificata, procederà con la regola successiva, ad esempio:
ciclo(10):-!. /* variabile = 10 termina */ ciclo(Counter):-NewCounter=Counter + 1, /* aggiorno contatore di ciclo */ random(Rnd), /* genero un numero a caso */ write(NewCounter,"\t",Rnd),nl, ciclo(NewCounter). /* riciclo */ trova:-write("Inizio della generazione di numeri casuali\n"),fail. trova:-ciclo(0),fail. trova:-write("Fine della generazione di numeri casuali\n"),exit. goal trova.
La domanda è trova, Prolog verifica la prima regola trova, in questa il predicato write produce una scritta sul video e risulta non verificato, a causa del predicato fail. Il predicato fail ha lo scopo forzare Prolog a proseguire e verifica quindi la seconda regola trova e poi finalmente la terza.
La seconda trova verifica il predicato ciclo che genera 10 numeri casuali, infatti la prima regola ciclo termina il ciclo, la seconda genera un numero a caso, tramite il predicato random, incrementa il contatore del ciclo, scrive il numero casuale ed infine ripete il ciclo.
La regola ciclo(10):-!. termina il ciclo tramite il predicato ! (cut), il cui scopo è di terminare la verifica della regola. Il predicato ! è utilizzato per lo scopo appena visto e quando interessa conoscere se c'è una soluzione, evitando così la perdita di tempo per trovarle tutte.
Il predicato exit evita la visualizzazione della risposta di Prolog . Infine il predicato nl permette l'avanzamento di una riga (in alternativa ai caratteri "\n" nel predicato write).
Nelle regole e nei fatti talvolta è presente della "conoscenza implicita": essa è o nel significato del predicato, ad esempio dati i fatti prima(a,b) e prima(b,c), la conoscenza implicita è prima(a,c) perché prima è una relazione di ordine, o nelle relazioni fra fatti diversi, ad esempio da fatti e regole del paragrafo 6.2.8.1 si capisce che a Mario piacciono le maserati, e a Elena piacciono tutte le automobili. Ma non c'è una regola o un fatto che dica qualcosa del genere: se a Tizio piacciono le Automobili, la Marca dell'automobile preferita è … Prolog permette di generare dei fatti, tramite il predicato assert(...), quindi potrebbe generare il fatto marca_preferita(persona,marca), e poi elencarlo.
Le regole relative ad Anna e Carlo fanno riferimento al tipo di auto, quindi se la regola è verificata, Prolog ha trovato la Marca dell'automobile, si possono modificare le due precedenti regole, come segue:
piace(anna,auto):- tipo(Automobile,famigliare), assert(marca_preferita(anna,Automobile)); tipo(Automobile,utilitaria), assert(marca_preferita(anna,Automobile)). piace(carlo,auto):- tipo(Automobile,sportiva),Automobile <> "ferrari", assert(marca_preferita(carlo,Automobile)).Questo vale solamente per Anna e Carlo, per tutti gli altri occorre aggiungere (in coda) un'ulteriore regola:
piace(X,auto):-tipo(Auto,_),X <> "anna",X <> "carlo", assert(marca_preferita(X,Auto)).Infine occorre scrivere le regole "procedurali" per eseguire il tutto, qualcosa di simile a ciò che segue:
elenco_marche(X):- piace(X,auto),fail. elenco_marche(_):- marca_preferita(X,Y), write(X," gradisce la ",Y),nl,fail. elenco_marche(_):- write("--------------"),nl,exit. goal elenco_marche(anna).
La risposta di Prolog è:
anna gradisce la fiat
anna gradisce la renault
--------------
La prima regola elenco_marche genera tutte le preferenze (di Anna), e la seconda le stampa.
La potenza di Prolog è nella capacità di estrarre la "conoscenza implicita" che è presente nei fatti, mediante opportuni predicati; questi in sostanza formalizzano le relazioni esistenti fra i fatti, ad esempio le relazioni d'ordine che il predicato prima (A è prima di B) sottende. L'esempio che segue vuole è un programma che verifica l'esistenza di cicli in un insieme di attività, di cui sono date le precedenze tramite il predicato prima(). I fatti sono:
prima(ideazione,pianificazione). prima(pianificazione,pubblicità). prima(ideazione,"ricerca materiale"). prima("ricerca materiale",stesura). prima(pubblicità,ideazione). % introduce un ciclo prima(pianificazione,stesura). prima(stesura,correzione). prima(correzione,stampa). prima(pubblicità,distribuzione). prima(stampa,distribuzione).
I predicati di ricerca dei cicli sono:
trovacicli(X):-attivita(X,Y,[]). attivita(Prima,Dopo,ListaLavori):-prima(Prima,Dopo), not(member(Dopo,ListaLavori)), prima(Dopo,NextDopo), attivita(Dopo,NextDopo,[Dopo|ListaLavori]). attivita(_,Dopo,ListaLavori):-member(Dopo,ListaLavori), write("Ciclo: "),nl, writelist([Dopo|ListaLavori]),fail. attivita(_,_,[]):-write("--- Fine ---"),nl, exit.
Il predicato trovacicli è utilizzato per la ricerca, il suo oggetto è una attività di partenza; il primo dei predicati attivita "scorre" la sequenza di attività, se una attività è già stata trovata (predicato member), fallisce, altrimenti prosegue la ricerca inserendo l'attività in una lista di attività.
Il secondo predicato attivita agisce dopo il primo, ed ha come oggetti le variabili Dopo e ListaLavori ereditate dal primo predicato attivita, in particolare se questo è fallito per il predicato not(member(Dopo,ListaLavori)), Dopo conterrà l'attività che provoca il ciclo, e ListaLavori l'elenco delle attività che compongono il ciclo, e queste verranno stampate.
Alla domanda:
GOAL trovacicli(ideazione).
Prolog risponde:Ciclo:
pianificazione
ideazione
pubblicità
pianificazione
--- Fine ---
Alla domanda:
GOAL trovacicli(correzione).
Prolog, non trovand cicli dopo l'attività "correzione",
risponde:
--- Fine ---
In questo programma si è utilizzata una lista per elencare le attività. Le liste sono una struttura di dati che può essere usata ricorsivamente sfruttando la definizione:
Lista = [Testa o primo elemento della lista | Coda o resto della lista]
L' operatore | permette sia la costruire delle liste, che il loro trattamento, un esempio del primo caso è la clausola writelist([Dopo|ListaLavori]): l'oggetto di writelist è la lista formata dall'elemento Dopo, seguita dalla lista ListaLavori. Sfortunatamente ci sono pochi predicati nativi per trattare le liste, è necessario scriverli, o utilizzare delle definizioni da inserire tramite il comando Prolog include, come è stato fatto nell'esempio precedente con i due predicati:
member(Testa, [Testa|_]). member(Testa, [_|Coda]):-member(Testa,Coda). writelist([]). writelist([Testa|Coda]):-write(Testa),nl,writelist(Coda).
Questi chiariscono come trattare ricorsivamente le liste, ad esempio il predicato member verifica la presenza di un elemento in una lista: se l'elemento è il primo, viene verificato il primo predicato member, altrimenti la ricerca prosegue, ricorsivamente, sul resto della lista.
[1] FORTRAN 90 ammette una sintassi meno vincolante.
[2] Si ricorda che una proposizione è anche detta relazione, e enunciato è una proposizione con valore di verità conosciuto.