manipolazione di testi
Icon è un sofisticato linguaggio per il trattamento di stringhe, ed è considerato il successore di SNOBOL. Uno dei suoi ideatori, Ralph Griswold, partecipò alla realizzazione di SNOBOL e successivamente, nel 1977, allo sviluppo di SL 5 (SNOBOL Language 5). SL 5 suppliva ad alcune carenze sintattiche di SNOBOL, tuttavia nel 1977 si pensò ad un linguaggio che ereditasse il meglio di SNOBOL4 e di SL 5, linguaggio con un diverso sistema di pattern matching e basato sul concetto di generatore, vale a dire un meccanismo per produrre tutti i risultati di un’espressione, anzi ogni espressione tramite il comando every diventa un generatore; tramite i generatori si possono costruire le strutture LOOP (every i:= n to m do ...) , i risultati di una espressione, forniti interattivamente, permettono l’elaborazione del testo durante il riconoscimento. Un altro generatore, utilizzabile per stringhe e liste è l’operatore unario!, che fornisce i caratteri di una stringa o gli elementi di una lista.
I tipi di dato di Icon sono numeri,
caratteri, stringhe, procedure e i tipi di dati aggregati insiemi (set),
liste, matrici associative, strutture o record, i cui componenti possono essere
di qualsiasi tipo.
In Icon il risultato di una relazione non è vero o falso, ma rispettivamente il valore del relato più a destra o il fallimento della relazione, così l’istruzione:
every write(6 < (1 to 10 by 2))
produce:
7 9
poiché 6 è confrontato, per effetto dell’iteratore every, con 1 3 5 7 9 e l’istruzione writes è eseguita, solo per i valori che soddisfano la relazione; poiché in Icon tutto è espressione, è lecito un programma del genere (| è il simbolo per concatenare espressioni):
procedure main() every n:= 2 | ?100
| n+1 | n+26 do write(n,if (d:= primo(n)) = n then
" primo" else
" divisibile per " || d) end procedure primo(n) (n % 2) = 0 & return 2; every i:= (3 to n^.5 by 2) do (n % i) = 0
& return i; return n end |
2 primo 22 divisibile per 2 23 primo 49 divisibile per 7.0 |
Il cui risultato si può vedere qui accanto. Nell’esempio every esegue write(...) 4 volte: per n a cui è assegnato il valore 2, poi per n contenente un numero a caso fra 1 e 100, indi per n incrementato di 1 ed ancora di 26. write dopo aver stampato n, stampa il valore dell’espressione if. La funzione primo(n), se n è pari ritorna il valore 2, altrimenti fornisce il più piccolo divisore di n (diverso da 1 ovviamente).
Un esempio che chiarisce la potenza del meccanismo di ricerca e fallimento è il seguente frammento di codice[1] che trova tutte le triple di numeri interi, fra 1 e 20, che soddisfano il teorema di Pitagora:
fl :=
open("s.txt","w") every i:=1 to 20 & j:=i to
20 & m:=j+1 to
20 & m*m
= i*i+j*j & write(fl,i,char(9),j,char(9),m) |
3 4 5 5 12 13 6 8 10 8 15 17 9 12 15 12 16 20 |
Alcuni simboli sono usati in contesti unari e binari, con significati del tutto o quasi non correlati:
Simbolo |
simbolo espressione |
espressione simbolo espressione |
? |
Genera
numero a caso |
Scansione
di stringa |
^ |
Rigenera co-espressione |
Eleva
a potenza |
* |
Dimensione
|
Prodotto |
/ |
Test
per null |
Quoziente |
\ |
Test
per non null |
Limitazione |
Le funzioni o procedure possono avere più parametri, ciò
vale ad esempio anche per la funzione arcotangente atan(x,y), con y, se omesso, assunto pari ad 1, e la tangente è il rapporto
fra x ed y, ciò permette di trattare il
caso in cui la tangente vale ±∞.
Il comando suspend espressione trasforma una funzione in un generatore, ad ogni richiamo
della funzione espressione fornirà un nuovo valore.
In Icon sono presenti dei generatori infiniti:
·
seq() 1,2,3,
...
·
| k k,k,k, ... | è il ripetitore di
concatenazione di espressioni
Fortunatamente l’operatore (binario) \ limita la generazione
indefinita:
every writes(| (0|1) \ 8) produce 01010101
Le strutture di controllo, oltre a quelle ottenibili con
l’espressione n to m, sono if then [else], while, case, repeat e until.
Icon è un linguaggio per il trattamento di testi, ed è
quindi naturalmente ricco di funzioni per il trattamento di stringhe, sia
funzioni native sia presenti in librerie (mapstrs, strings); inoltre per facilitare le operazioni di scansione di
stringhe, esistono insiemi predefiniti quali &digits, &letters, &lcase, ecc… e ad ogni variabile stringa è associato un pointer
alla posizione interessata al processo di scansione. Le funzioni di scansione
hanno, in genere, la struttura:
funzione(modello,stringa,posizione_iniziale,posizione_finale)
in esse possono essere omesse le posizioni iniziali e
finali, se i valori di default sono accettabili. Qui di seguito un elenco di
alcune funzioni di scansione:
·
MANY()
verifica quanti carattere all’inizio della scansione appartengono ad un certo
insieme di caratteri.
·
UPTO()
restituisce le posizioni in cui la stringa contiene caratteri appartenenti ad
un certo insieme.
ANY(), MANY() e MATCH() restituiscono il
puntatore al carattere successivo il match.
L’operatore ?, il cui antecedente è una stringa, pone questa come default
nelle funzioni di scansione che seguono l’operatore: le istruzioni che seguono
sono pertanto equivalenti:
wd := "2*sin(37-a)"
match("sin",wd,find("*",wd)+1)
wd ? match("sin",,find("*")+1)
Sono disponibili molte librerie, utilizzabili mediante il comando link nomelibreria, che offrono varie funzioni, fra cui trattamento di stringhe, funzioni grafiche e per la programmazione ad oggetti (IDOL).
Il linguaggio è giunto alla versione 9;
Jicon è un generatore di classi Java. Le librerie per la grafica permettono di
realizzare facilmente applicazioni grafiche interattive, è tuttavia anche
disponibile uno strumento di programmazione visuale (VIB) nell’esempio che
segue illustra alcune capacità grafiche di Icon, e come Icon gestisce
l’interattività.
# Window Icon Version 9.3.2 December 4, 1998 link graphics global colori procedure main() colori :=
["white","black","white"] # per disegno in
rilevo Htxt := 16 # altezza testi bottoni := [["Fine",401, 360,
60,20,Fine],["Disegna",340, 360, 60,20, Disegna]] WOpen("size=500,400") |
stop("impossibile aprire la finestra") WAttrib("fg=light grey",
"linewidth=1","label=Prova eventi")
FillRectangle()
Fg("Black")
Font("typewriter,bold," || Htxt) # || op. concat. stringhe every
itm := !bottoni do { # generazione bottoni bottone(itm,"su") CenterString(itm[2] + itm[4] / 2, itm[3]+itm[5] / 2,
itm[1]) } # gestione eventi repeat case
Event() of { tst :=
(&lpress | &lrelease): SeBottone(bottoni,tst) } end procedure SeBottone(bottoni,tst) every itm := !bottoni do { if (itm[2]
< &x <itm[2]+itm[4]) & (itm[3] < &y <itm[3] + itm[5])
then { if tst = &lpress then
bottone(itm,"giu") else {bottone(itm,"su"); itm[6]();
} # eseguo metodo associato } } end procedure
bottone(itm,direz) FgColor := WAttrib("fg") # colore
precedente Fg(colori[indx:=
x[find(direz,x:="1su2giu")-1]])
# contorto vero? DrawLine(itm[2],itm[3],itm[2]+itm[4],itm[3],itm[2]+itm[4],itm[3]+itm[5]) Fg(colori[indx+1]) DrawLine(itm[2]+itm[4],itm[3]+itm[5],itm[2],itm[3]+itm[5],itm[2],itm[3])
Fg(FgColor) end procedure Fine()
WriteImage("graf.gif");
write("Fine") & exit() end procedure Disegna() rosso := ?65000; # un numero a caso fra 0 e
65000 verde := ?65000; # un numero a caso fra 0
e 65000 blu := ?65000; # un numero a caso fra 0 e 65000 Fg(rosso || "," || verde ||
"," || blu); FillCircle(?450,?300,?10); retun; end |
Il codice sorgente che segue è un traduttore, incompleto, in linguaggio
CUPL (v. CUPL) di programmi IT (v. IT).
# Window Icon Version 9.3.2 December 4, 1998
link strings global itsimboli,ckinstr,Istr,IndIstr,tabrif,word procedure main(args) itsimboli
:= &ucase ++ &digits ++ "\"+-*/().,'=" # unione di
insiemi itrapr
:= "LRJZUVWSMXDPKQ"; #
caratteri di icon itsymb
:= "().=UVW+-*/^ \""; # simboli IndIstr
:= table(0); # tabella indirizzi tabrif
:= table(0); # tabella riferimenti
goto e perform Istr
:= table(""); # tabella
istruzioni ckinstr
:= 0; # contaistruzioni itsrc
:= args[1]; if not args[1] then itsrc :=
"source.it"; fl := open(itsrc,"r") |
(write("non esiste: ",itsrc) & exit()); #clupsrc := "source.cupl"; itsrc
? clupsrc := tab(find(".",itsrc,1)) || ".cupl"; # elimino
dopo simbolo di fine flo := open(clupsrc,"w") write("Sorgente IT " ||
itsrc || " sorgente CUPL " || clupsrc); write(flo,"COMMENT
PROGRAMMA CUPL CHE INTERPRETA PROGRAMMA IT " || itsrc); while riga :=
map(read(fl),itrapr,itsymb) do {
riga ?
riga := tab(find(" F",riga,1)); # elimino dopo simbolo di fine write(riga)
ckinstr := ckinstr + 1;
word
:= getword(riga); # lista degli operandi # inizio scan indirizzo := get(word) IndIstr[ckinstr] := indirizzo; genistr() #
if \ tabrif[indirizzo] then { #
if tabrif[indirizzo] == indirizzo then Istr[ckinstr] ||:=
"\nLAB" || indirizzo || "\tEND";} } every k:=1 to ckinstr do {
if \
tabrif[IndIstr[k]] then { # esiste un riferimento per goto op
:= tabrif[IndIstr[k]]; if op > 0 then Istr[k] ||:= "\nLAB" || op
|| "\tEND"
else if op < 0 then Istr[k] := "LAB" || -op || Istr[k]
}
write(Istr[k]);write(flo,detab(Istr[k]));} end procedure genistr()
while *word > 0 do {op1 := get(word)
if op1 == "IF" then {
Istr[ckinstr] := "\tIF" || componi(word) || " THEN
" || Istr[ckinstr];
break;
}
op1 ? case ch := move(1) of {
# variabili
"Y"| "C" | "I": {Istr[ckinstr] ||:=
"\tLET " || op1 || componi(word);}
"H": Istr[ckinstr] ||:= "\tSTOP";
"T": if not find("WRITE",Istr[ckinstr]) then Istr[ckinstr] ||:= "\tWRITE " || get(word) else Istr[ckinstr] ||:= ", " || get(word);
"G": {op := get(word);Istr[ckinstr] ||:= "\tGO TO
LAB" || op; tabrif[op] := -op; # per label } #numerico è un loop default: {Istr[ckinstr] ||:=
"\tPERFORM LAB" || op1 || " FOR " || get(word) || " = "
|| get(word) ||
" BY " || get(word) || " TO " || get(word) || "\nLAB" ||
op1 || "\tBLOCK"; tabrif[op1] := op1; # per fine perform
}
} } end procedure getword(str) word := []; str ? while
tab(upto(itsimboli)) do { put(word,
tab(many(itsimboli))) } return word end procedure
componi(lista) #
compone formula, si ferma se finisce o trova IF wd := "";
while op := get(lista) do {
case op of {
"IF": {push(lista,op); return wd;}
"U": {op := "=";}
"V": {op := "GT";}
"W": {op := "GE";} }
wd := wd || " " || op;
} # risoluzione funzioni wd := replace(replace(wd,"\"42E
","SIN("),"\"",")") return wd; end |