Archivi Categorie: Linguaggi

Linguaggi di programmazione

Maiuscolo|minuscolo – ultima puntata

tmm-finOggi concludo la telenovela serie sulla conversione maiuscolo|minuscolo all’interno della clipboard. Le puntate precedenti sono qui e qui.
Avevo pensato di poter utilizzare il comando echo con l’opzione dell’here document ma non ci sono riuscito. Non so se è una mia carenza o il metodo non è praticabile. Sono ricorso a cat, soluzione meno bella perché ho dovuto creare un file temporaneo in /tmp.

Il codice è commentato e semplicissimo (non cambia granché dalla puntata precedente) per cui eccolo, senza preamboli:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/newlisp

;; get state of CapsLock
(set 'clON (% (int (slice (string 
        (exec "xset q | grep LED")) -4 2)) 2))

;; get clipboard content
(set 'txt (join (exec "xsel -ob") "\n"))
;;  case translate
(if (= clON 1) 
    (set 'txt (upper-case txt))
    (set 'txt (lower-case txt)))

;; park in a temp file
(set 'tmpf "/tmp/Mm")
(write-file tmpf txt)

;; put the temp file it in the clipboard
(set 'cmd (join (list "cat " tmpf " | xsel -ib")))
(exec cmd)
(delete-file tmpf)

(exit)

Si potrebbe ancora modificarlo rendendolo più lispico ma poi si perderebbe la possibilità di confronto con la versione del passo successivo, questo che segue immediatamente.

Se a questo punto qualcuno ponesse la domanda “ma si deve fare con newLISP?” risponderei “certo che no! ecco la versione con Python”.

#!/usr/bin/python
# -*- coding: utf-8 -*-

import subprocess, os

# stato di capslock
cmd = "xset q | grep LED"
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
                      stderr=subprocess.STDOUT)
output = (ps.communicate()[0]).strip()
clON = int(output[-2:]) % 2

# contenuto della clipboard
cmd = "xsel -ob"
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
txt = (ps.communicate()[0])

# converto
if clON:
    txt = txt.upper()
else:
    txt = txt.lower()

# lo parcheggio in un file temporaneo
tmpf = "/tmp/Mm"
f = open(tmpf, 'w')
f.write(txt)
f.close()

# copio il file nella clipboard
#cmd = "cat"
cmd = "cat {0} | xsel -ib".format(tmpf)
os.system(cmd) #, "", shell = True)
os.remove(tmpf)

Sono quasi equivalenti. Ecco un dump del test effettuato.

MMSì newLISP converte anche le lettere non ASCII :-D
Ma siccome è un’inutility il discorso, per me, si chiude qua ;-)

Ruby – leggere da terminale e commenti

r5OK, c’è stata una lunga pausa nell’esame di Ruby. L’ultima puntata è stata questa, tanto tempo fa: Ruby – iniziamo con gli script.
Ora ripaprto. E continuiamo a fare pratica con gli script. Dopo “Hello World!” sarebbe bello che lo script salutasse proprio te. Così:

#!/usr/bin/ruby
puts "Ciao sono il 'puter"
print "come ti chiami:"
name = gets()
puts "Ciao #{name}Buona giornata!"

c1

gets fa l’opposto di puts: invece di scrivere la stringa sul terminale legge quella che scriviamo.

Naturalmente la quinta riga potevamo scriverla anche come: puts "Hello " + name + " Buona giornata!"

Due cose:

  • notare l’a-capo anomalo prima di "Buona giornata", sarà da indagare in futuro;
  • solo per chi usa Linux la prima riga è speciale, permette di rendere lo script eseguibile in questo modo:

c1-1

In Windows non esiste l’equivalente e la riga viene considerata come un commento, come in realtà è.

Già, i commenti. Tranne dentro le stringhe “” come nel caso precedente Ruby considera il carattere # e tutto quello che segue fino alla fine della riga come un commento, serve a noi ed è ignorato dall’interprete, come in questo caso:

#!/usr/bin/ruby

# esempio (scemo) di commenti
v1 = 5 # primo dato
v2 = 7 # secondo
res = v1 + v2 # somma
print "la somma di #{v1} + #{v2} vale #{res} #sapevatelo\n"

c2

Qui vediamo diversi tipi di commenti:

  • la prima riga, speciale di cui abbiamo già detto (è talmente speciale che ha un nome, shebang);
  • la terza riga, tutta di commento;
  • le righe 4, 5 e 6 con un’istruzione e poi un commento;

Notare che il carattere # nell’ultima riga non è un commento. Anche le righe vuote, come la seconda vengono ignorate dall’interprete, proprio come i commenti.

Alle volte il commento che vogliamo scrivere è troppo lungo per una riga sola. In questo caso possiamo fare così:

#!/usr/bin/ruby

=begin
    esempio (scemo) di commento multilinea
    è tutto un commento fino a quando lo
    chiudiamo con la keyword che segue
=end
v1 = 5 # primo dato
v2 = 7 # secondo
res = v1 * v2 # prodotto
print "il prodotto di #{v1} * #{v2} vale #{res} #sapevatelo\n"

c3

Quando la lunghezza dello script supera poche righe questo tipo di commenti diventano molto utili, non immaginate quanto. Notare che le keyword =begin e =end devono cominciare in prima colonna, niente spazi.

Compito a casa: cosa succede se metto un commento multilinea dentro a un commento multilinea? ;-)

Ancora su maiuscolo|minuscolo

tmm2OK, lo so che non serve a niente ma voglio relazionarvi. Si continua dal post precedente: Convertire testo in maiuscolo|minuscolo.
Mentre preparavo quel post mi era venuto in mente che…
Non avevo considerato la Legge di Hofstadter. Cosa imperdonabile per chiunque abbia avuto accesso a uno o più ‘puters per più di qualche anno. Ma tant’è, sembrava facile. E poi la soluzione precedente non funziona con i caratteri con numero ASCII/ANSI maggiore a 127.
Eppure guarda qua:

nl-ulahemmm, sì, ma posso smettere quando voglio, dai ;-)

Allora sono riuscito a fare questo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/newlisp

;; get state of CapsLock
(set 'clON (% (int (slice (string 
        (exec "xset q | grep LED")) -4 2)) 2))

;; get clipboard content
(set 'txt (join (exec "xsel -ob") "§"))

;;  case translate
(if (= clON 1) 
    (set 'txt (upper-case txt))
    (set 'txt (lower-case txt)))

;; set it in the clipboard
(set 'cmd (join (list "echo " txt " | xsel -ib")))
(exec cmd)

(exit)

Che non è ancora perfetto, non gestisce correttamente la selezione multilinea. Il workaround attuale è di sostituire l’a-capo con il carattere § (galassia a spirale?). E c’è ancora da vedere quelli che la shell considera caratteri speciali, che pal :-(

Insomma ci sarebbe ancora da fare, chissà forse, anche perché sembra semplice. Per esempio per la selezione multilinea c’è l’here document.

E anche i caratteri speciali possono essere escapati. Noioso ma fattibile. E per questo lo script usa parecchie variabili, ridondanti ma utili per il debug. Poi quando sarà a posto sarà molto più elegante, forse.

Chissà… Comunque posso smettere quando voglio. E c’è Ruby che aspetta. Anche se sembra tutto davvero semplice.
Anche tenendo conto della Legge di Hofstadter ;-)

Convertire testo in maiuscolo|minuscolo

OK, una cosa completamente inutile. Ma siccome ci ho lavorato per qualche ora ve la voglio raccontare. Caso mai non leggetelo.

togc

Io sono diversamente smart –è risaputo anche se non mi va che si dica in giro– e con l’installazione di Ubuntu RR mi sono trovato parecchie cose che non funzionavano più come prima. Anche Gedit, colpa mia, certo, ma era sparita l’opzione Modifica maiuscole/minuscole dal menu Modifica. E prima di scoprire che dovevo attivare i plugin mi sono messo a googlare per Python + clipboard + capslock e allora ecco qui.

Allora c’è da dire che si trova tutto già fatto, solo da assemblare.
La prima cosa da fare è scaricare il modulo pyperclip, da qui: Pyperclip – A cross-platform clipboard module for Python.

Per determinale lo stato del tasto Bloc Maiusc è bastata un’altra googlata, qui: Python – How to get current keylock status?

È poi richiesta l’installazione di due utilities: xclip e xset.

OK, abbiamo tutto, ecco lo script Python (togc.py):

#!/usr/bin/python
# -*- coding: utf-8 -*-

import pyperclip, commands

t = pyperclip.paste()
capson = (int (commands.getoutput("xset q | grep LED")[65])) % 2
if capson:
    t = t.upper()
else:
    t = t.lower()
pyperclip.copy(t)

Basta renderlo eseguibile con chmod +x togc.py e funziona.

Il passo successivo è di creare un pulsante sul desktop o sulla barra dei lanciatori.

unity

Facilissimo, eccolo (togc.desktop):

[Desktop Entry]
Name=Lanciatore Semplice
GenericName=Lanciatore Semplice
Comment=Lanciatore Semplice
Exec=/home/juhan/lab/APPs/togc/togc.py
Icon=/home/juhan/lab/APPs/togc/togc.png
Terminal=false
Type=Application
NotShowIn=GNOME;
X-Ubuntu-Gettext-Domain=synaptic

Name[it_IT]=togc

La solita avvertenza: modificare come richiesto le righe 5 e 6, prima di renderlo eseguibile.
Come icona ho usato l’immagine messa in testa al post.

Poi si può provare, ma è OK, almeno per me:

adesso provo togc -- vediamo 123 Привет мир!
ADESSO PROVO TOGC -- VEDIAMO 123 Привет мир!
adesso provo togc -- vediamo 123 привет мир!
ADESSO PROVO TOGC -- VEDIAMO 123 привет мир!

I caratteri non latini non vengono convertiti, ma neanche Gedit lo fa.
E, sì, è completamente inutile.
E, sì, lo so, ci sarebbe Emacs o almeno vim; vi lo usavo prima che parecchi di voi nascessero, e prima ancora usavo ed e roba simile.

Però ho trovato un altro paio di cose che voglio condividere con voi, prossimamente… forse… ;-)

Ruby – iniziamo con gli script

r3Finora abbiamo usato Ruby nella versione interattiva:

s0
anzi, per non avere roba inutile nella versione:

s1
e per non dover scrivere ogni volta irb --simple-prompt abbiamo visto come fare per creare un comando personalizzato:

s2
procedura che dipende dal sistema operativo, come spiegato nelle prime lezioni, andate a quei post se necessario.

Ma oggi si cambia tutto, iniziamo con gli script, i programmi, come fanno i veri programmatoriTM.

Apriamo un documento di testo che salveremo con il nome hw.rb (con Blocco note, Notepad2 o qualcosa di simile con Windows, con Gedit, Nano, Geany su Linux) e scriviamoci dentro le nostre istruzioni, così

puts "Hello World!"

Potremo eseguire lo script nel terminale in questo modo:

hw

Questo è un passo obbligatorio quando si inizia a raccontare di un nuovo linguaggio. Fin dai tempi di K&R (Kernighan e Ritchie, tanto tempo fa quando ero ggiovane).

Vediamo qualcosa di più articolato:

# qualche calcolo semplice
# per impratichirci

val1 = 17
val2 = 5
somma = val1 + val2
print val1, ' + ', val2, ' = ', somma
puts ''

prodotto = val1 * val2
print val1, ' * ', val2, ' = '
puts prodotto

quoz = val1 / val2
resto = val1 % val2

print val1, ' / ', val2, ' = ', quoz, ' con resto ', resto, "\n"

sc0

OK?

Ci sono parecchie cose nuove, vediamone qualcuna.
Il carattere # e tutto quello che segue fino alla fine della riga indica un commento e viene ignorato da Ruby. Ma serve a noi, non immaginate quanto! Ogni script potrà o dovrà essere modificato in futuro, urgentemente, vi sarete certo dimenticati di com’è fatto e se è più lungo di poche righe sarà tedioso doverlo rileggere tutto.
Avremmo potuto inserire commenti come # definizione dei dati, # calcolo della somma, etc..
È possibile inserire righe vuote, vengono ignorate dall’interprete ma servono a noi.

Sempre in previsione di quando dovremo aggiornare lo script usiamo dei nomi significativi per le variabili, come somma, prodotto, quoz (sarebbe quoziente ma non sono stato abbastanza paziente) e resto.

Di puts e print ne abbiamo parlato nei post precedenti, entrambi scrivono sul terminale. La differenza è che puts va a capo dopo ogni dato che scrive, print no.
Per cui si può far scrivere tutto a print e mettere un puts vuoto, come nelle righe 7 e 8 oppure dare tutto in pasto a print tranne l’ultimo dato, come nelle righe 11 e 12.
Se poi uno vuole fa come in riga 17, se si ricorda delle differenze tra apici e virgolette e delle sequenze di escape.

Adesso una cosa che vale solo per Linux, chi usa Windows chiuda gli occhi per le prossime righe.
Scrivere ogni volta ruby nome.rb è faticoso! Ma c’è un rimedio, questo:

#!/usr/bin/ruby
print "Hello World!\n"

La prima riga sembra un commento (e lo è) ma in realtà è anche la shebang che ci consente di rendere eseguibile lo script così:

sb

Il comando chmod dobbiamo darlo solo una volta, possiamo anche verificare se l’abbiamo già dato:

ls

Notare la x (sta per execute) presente nei permessi, solo per sb.rb.

OK, fine particolarità solo per Linux.

Un’ultima cosa importante, guardate questo script:

prima = "Hello "
seconda = "world!"

print prima seconda
puts

err

OPS! errore alla riga 4, correggere così

prima = "Hello "
seconda = "world!"

print prima, seconda
puts

err-c

Queste cose a me capitano spesso. tanto spesso che penso che il ‘puter ce l’abbia con me ;-)

Eple per chi vuole cominciare da piccolo

It stands for “apple” in norwegian and it is an acronym for
EPLE Programming Learning Environment

unica

Quando io dovevo iscrivermi al secondo anno del Poli (in realtà era già tardi ma prima non lo sapevo ancora, e poi tutti i miei amici di Poli…) ho dichiarato che volevo andare su ingegneria informatica.
“Cos’è quella roba lì?” chiese mio padre.
“Quella che si occupa dei calcolatori” risposi, me l’ero preparata. In realtà dissi “cervelli elettronici” perché allora si chiamavano così.
Sapevo che mio padre sarebbe stato molto contrario e mi stupì che non dicesse niente. Ma, forse, era solo stato preceduto dalla mamma che mi convinse con “è una moda passeggera, tempo un paio d’anni e non ne sentiremo più parlare”.
Continuai con ingegneria civile, credo che oggi si chiami edile. Ma ho sempre trafficato con i ‘puter. Di diversi tipi e dimensioni, sempre più piccole, fino a quando non c’è più stato spazio per la tastiera.

E, naturalmente, per me calcolatore = cosa che serve a fare calcoli, quelli che farli a mano sarebbe troppo lungo. Anche se devi saperli fare, per controllare le istruzioni che gli dai, lo chiamano debug.
Quando ho cominciato a lavorare io c’erano ormai i TTY, sì a volte ancora le schede ma era roba del passato. Un’altra cosa che sarebbe dovuta essere del passato e invece no erano le due generazioni precedenti di operatori: quella che lavorava in assembler (si dovrebbe dire assembly, lo so e lo so che lo sapete) e quella che lavorava in FORTRAN IV, proprio come se fosse assembler (assembly). E siccome erano i capi comandavano loro.

Poi a un certo punto sono usciti i personal, cosine costose e inguardabili se viste adesso. Ma con loro è nata una generazione di giovani (non si chiamavano digitali ma lo erano) che dopo un po’ smettevano di giocare e facevano cose in Basic. Programmi orrendi per come erano scritti, tipo tante istruzioni sulla stessa riga separate da due-punti fino a quando la riga stessa diventava troppo lunga. C’era chi iniziava il FOR con dichiarazione del ciclo, poi ci metteva tutto il corpo e il NEXT finiva da solo su una nuova riga. Ma il mondo sarebbe stato loro.
Poi anche il Basic è passato di moda: oggi con il SO (operating system) non te lo danno, e è anche difficile trovarlo. E allora se una ragazza o un ragazzo come si deve vuole imparare fin da subito?
C’è qualcosa in giro, da parecchio tempo. E ultimamente c’è una novità: la tesi di Pier Giuliano Nioi, piergiu.

eple

È quasi un peccato che io sappia già trafficare con il ‘puter (anche se in realtà non mi capisce quasi mai) perché il piergiu (io lo pronuncio con l’accento sulla u) ha sviluppato un’app decisamente bella. Ho tentato per qualche giorno di farne un riassunto, una recensione ma vi conviene vederla da lui. Trovate il PDF che vi dice tutto e l’applicazione completa qui: An Interactive Environment for the Didactical Manipulation of Programs.

Ditegli che vi mando io (“Juhan send me“) se volete, ma anche no.

education

Adesso non so a se piergiu piaccia questo post messo così ma dovevo farlo, guarda oggi che cinguettio ho visto: Localized (Visual) Programming Language For Kids?

Quando si dice la combinazione ;-) :-D

Interprete Lisp in Python

Un post un po’ particolare, insolito. Ma ultimamente sono attivi due autori über-bravi, Robitex e Dikiyvolk e allora vado con questa cosa difficile da definire.lambdaRecentemente sono finito invischiato in una cosa che non so bene come raccontare. E che continuava a venirmi in mente anche quando pensavo a altro, insomma ne sono stato completamente invischiato. E vorrei coinvolgere anche voi. Comincio con il presentare l’autore: Michael Nielsen un australiano che sta negli USA e si definisce a writer, scientist, and programmer.

Allora c’è questo post Lisp as the Maxwell’s equations of software (code repository) che parte con un paio di reminescenze personali per finire in un progetto inutile in ultima analisi ma fantastico: costruire un interprete Lisp minimale, funzionante e completo e farlo in Python.

C’è riuscito e molto bene. Per onestà occorre dire che si è basato su una cosa simile di Peter Norvig di cui ho già accennato in passato ma conto di tornarci (dipende ovviamente dalle reazioni a questo post) e c’è riuscito benissimo.setIntanto parte con un minicorso del suo interprete Tiddlylisp (tiddlywinks è un vecchio gioco, ormai in disuso, conosciuto da noi come pulci). Tiddlylisp è piccolo ma completo. Certo mancano delle cose che in un interprete serio professionale si danno per scontate; un’istruzione dev’essere contenuta in una riga, per esempio e mancano costrutti usuali. Ma funziona e il corso di Michael è preciso e esauriente. Chi non conosce il Lisp può farsi un’idea precisa, tanto poi se passa a una versione vera scopre che ci sono i dialetti e ogni implementazione è una cosa a se e che ogni utente personalizza la sua copia fino a diventare incompatibile con il resto del mondo.

Se invece di usare interattivamente l’interprete gli si da un file di codice da eseguire la limitazione dell’istruzione monoriga cade e abbiamo (come Michael ci dimostra) la possibilità di risolvere compiti più complessi.pythonQuesta è la prima parte del post (sì è lungo, molto più lungo di quello che si trova di solito nei blog). Ma adesso viene il bello, cioè no, si continua ma adesso con Python. E c’è il codice e la descrizione che è non banale ma, in fondo, molto più semplice di quello che mi sarei immaginato.

Per finire si parla di costruire un interprete Lisp in (esatto) Lisp. E i commenti dei lettori ampliano ancora il discorso. Tutti più bravi di me, ovviamente.

Ecco, che ne pensate? Forse sono solo io a stupirmi di questo modo di intendere la programmazione, o è il mantra “Lisp is a programmable programming language“.

Poi, certo, si usano altre cose, C++, Java, Python, …
Io per diversi anni avevo solo il Fortran. Poi le cose sono peggiorate: è arrivato il Basic (non c’è mai limite al peggio).

Ancora una nota: lo cita anche Michael ma è troppo bello devo farlo anch’io: The Roots of Lisp di Paul Graham.

Sei un programmatore Ruby principiante se…

Rieccomi!!! Butto giusto uno spunto di riflessione sui post di Juhan riguardo Ruby. Un po’ di tempo fa su linkedin mi arrivò questo argomento di discussione:

“Sei un programmatore Ruby principiante se pensi che la seguente espressione:

ruby -e 'a=(true and false) or (true and true); puts a'

restituisca true.”

Leggendola così su due piedi… ho pensato perché non dovrebbe restituire true… la risposta è semplice, in ruby esistono degli operatori “ripetuti” (chiamiamoli così, gli esperti non mi insultino) che hanno lo stesso funzionamento, ma differente priorità. Per cui abbiamo che “or” può essere scritto come “or” o come “||” e l’”and” come “and” o come “&&”. Solo che gli opertori “and” e “or” hanno minore priorità dell’operatore “=” per cui nel nostro esempio avremo che l’assegnazione alla variabile “a” avverrà prima che venga eseguito l’or. A varrà quindi il risultato di (true and false).

Questa è una tabella che ho copiato da “Programming Ruby 1.9 & 2.0″ di “The Pragmatic Bookshelf” con l’elenco degli operatori e della loro priorità:

Elenco degli operatori Ruby con le loro priorità

Elenco degli operatori Ruby con le loro priorità

Se si fosse scritto:

ruby -e 'a=(true && false) || (true && true); puts a'

o

ruby -e 'a=(true and false) || (true and true); puts a'

Avremmo ottenuto “true” come da copione :) o da abitudinario come me :)

Ho faticato un po’ a capire il perché degli operatori “duplicati”, ovviamente la mia prima risposta è stata:

“Ruby è sviluppato da un giapponese e i giapponesi ogni tanto fanno delle scelte che a noi non giapponesi sembrano strane” :) se non mi credete fatevi un giro per Tokyo (ci sono stato 2 volte) e vi ricrederete… :D
La risposta invece logica è che gli operatori “&&” e “||” si usano per operazioni booleane (i nostri amici if e famiglia), mentre “and” e “or” si usano per il controllo del flusso (come in perl).
Per dettagli e approfondimenti qui è spiegato tutto benissimo.

Post brevissimo… diciamo un aperitivo per i prossimi post di Juhan :)

La varietà di variabili in Go

Scarica l’articolo per la stampa nel formato pdf: golang_tipi_variabili1.pdf

Sommario

Faremo un excursus sul significato delle variabili chiarendone il loro funzionamento nei moderni linguaggi di programmazione, in particolare con riferimento al Go. Ci soffermeremo sul concetto di variabile valore e su quello di variabile riferimento.
Per la comprensione e l’esecuzione degli esperimenti proposti, è necessario solo un po’ di concentrazione e la lettura dei post sul linguaggio Go apparsi su questo stesso blog.

Variabili

Tutti o quasi tutti i linguaggi di programmazione comprendono tra i concetti di base quello di variabile. Il nome stesso ci dice che è un qualcosa che può cambiare durante l’esecuzione del programma e quel qualcosa è il dato che la variabile rappresenta.

Per fare un esempio di codice in un linguaggio generico, possiamo in modo molto naturale assegnare il valore numerico 10 alla variabile v e sottrarre poi 5:

-- in un linguaggio generico...
v = 10
print( v )  --> stampa 10

v = v - 5
print( v )  --> stampa 5

Se provo a creare una seconda variabile q con il valore della prima mi aspetto che riceva il valore che v conteneva al momento dell’assegnazione. Ma se poi la prima variabile cambia che ne è della seconda?
Il codice seguente chiarisce il dubbio sul comportamento delle variabili:

-- sempre scrivendo il codice
-- in un linguaggio generico
v = 10
q = v

print(q) --> stampa 10

-- poi v cambia...
v = v - 5
print(q) --> stampa 10 o 5?

Ci si può attendere due soli risultati:

  1. la seconda variabile non cambia mantenendo il valore 10;
  2. la seconda variabile cambia cambiando a sua volta in 5 come la prima.

Dal C, al C++, a Java

Siamo partiti da un concetto piuttosto semplice: dare un nome al contenitore di un dato che può cambiare durante l’esecuzione del programma — ovvero a “run-time” — ma ben presto ci siamo resi conto che è necessario scegliere come effettivamente rappresentare le informazioni, e queste scelte influenzeranno nel bene e nel male le applicazioni.

Ogni linguaggio dunque affronta problemi di progettazione complessi, con esigenze spesso opposte, come ben testimonia la loro evoluzione storica, per esempio dal C, al C++, a Java. Ad ogni generazione l’ingegneria del software mette a frutto anni di lavoro e l’esperienza di milioni di righe di codice in un nuovo linguaggio.

Tipi di variabili

Eravamo rimasti al dubbio se nel nostro ipotetico linguaggio sia opportuno o meno agganciare il valore della seconda variabile a quello della variabile da cui era stata costruita. Verifichiamo subito qual è stata la scelta dei progettisti del Go :-)

Per una variabile di tipo intero la verifica è:

package main

import "fmt"

func main() {
    v := 10
    q := v
    // modo compatto di scrivere v = v - 5
    v -= 5

    // stampa 10 o stampa 5?
    fmt.Println(q)
}

invece per una variabile di tipo []int, ovvero uno slice la verifica è:

package main

import "fmt"

func main() {
    // uno slice:
    v := []int{10}
    q := v
    v[0] -= 5
    // stampa [10] o stampa [5]?
    fmt.Println(q)
}

Otteniamo tutti e due i comportamenti! Verificate per esercizio a quale dei due esempi corrispondono i casi (non vi farà male e vi ruberà solo un minuto se utilizzate Go Playground).

Variabili valore

Se la variabile è intesa come il contenitore stessso in cui si trova il dato, creandone una per mezzo di un’assegnazione di un’altra variabile verrà creato semplicemente un nuovo contenitore con il dato in quel momento contenuto in quest’ultima.
Stiamo parlando delle variabili valore che forniscono l’accesso diretto al dato.

Quello che abbiamo definito contenitore è in sostanza il segmento di memoria che contiene la rappresentazione binaria del dato. Nella figura di seguito è rappresenta la dinamica della creazione e della modifica delle variabili valore v e q del codice di esempio.

Schema di funzionamento delle variabili valore

Schema di funzionamento delle variabili valore

…e le strutture?

Questo ve lo posso dire: le strutture in Go sono memorizzate in variabili valore, lascio a voi scrivere per utile esercizio il breve codice che lo verifica…

Variabili riferimento

Se la variabile è intesa come il nome del contenitore in cui si trova il dato, allora creandone una per mezzo di un’assegnazione da un’altra variabile, verrà copiato il nome del contenitore nella nuova. Si tratta delle variabili riferimento che forniscono un accesso indiretto al dato tramite informazioni riguardanti il contenitore.

Anche le variabili riferimento sono di tipo valore nel senso che dopotutto contengono direttamente le informazioni solo che non rappresentano il dato ma solo quello che serve al compilatore per raggiungerlo.

Ecco una rappresentazione schematica dello stesso codice precedente che coinvolge le variabili v e q ma stavolta di classe reference.

Schema di funzionamento delle variabili riferimento

Schema di funzionamento delle variabili riferimento

Il terzo tipo: i puntatori

Non dovrebbe esistere un terzo tipo di variabili perché all’inizio del post abbiamo riscontrato che ci possono essere solo due situazioni che poi abbiamo associato alle variabili valore ed alle variabili riferimento.

In Go sono disponibili, come in C ed in C++, i puntatori ed in effetti non costituiscono un terzo tipo di variabile perché (almeno in Go) sono contenuti in normali variabili valore. Per convincerci eseguiamo il solito programma di prova:

package main

import "fmt"

func main() {
    n, m := 10, 5 // tipi int

    p1 := &n // tipo *int
    p2 := p1

    p1 = &m
    fmt.Println(*p1) // stampa 5
    fmt.Println(*p2) // stampa 10
}

I puntatori quindi sono variabili valore che contengono l’indirizzo grezzo di memoria dove si trova un dato. Dal punto di vista del linguaggio possiamo considerare i puntatori come delle variabili riferimento primitive. Con i puntatori infatti, gestiamo esplicitamente indirizzi di memoria ottenendoli con l’operatore & come abbiamo appena visto nel listato precedente, ed indichiamo esplicitamente il valore puntato deferenziando il puntatore con l’operatore *.

Se affermiamo questo, possiamo anche dire in modo speculare che le variabili riferimento sono un tipo evoluto di puntatore perché nel codice le scriviamo come normali variabili. È il compilatore che svolge il lavoro “primitivo” per noi elaborando nel modo opportuno le informazioni effettive celate nella variabile riferimento.

Possiamo a questo punto rispondere a queste domande:

  1. due variabili riferimento di uno stesso oggetto avranno lo stesso indirizzo di memoria?
  2. sapendo che in Go alle funzioni vengono passate le copie degli argomenti, cosa accade se modifichiamo una variabile riferimento all’interno di una funzione?
  3. in Go, conviene passare ad una funzione un puntatore a slice o lo slice stesso?

Risposta uno

Due variabili riferimento di uno stesso oggetto avranno lo stesso indirizzo di memoria?

Se una variabile riferimento è veramente una variabile valore che contiene un riferimento ad un dato in memoria, ne segue che avrà un proprio indirizzo di memoria diverso da quello di qualsiasi altra variabile dello stesso tipo anche se si riferisce allo stesso oggetto.
Verifichiamo per prima cosa se possiamo conoscere facilmente l’indirizzo di memoria di una variabile valore e di una variabile riferimento con questo minicodice:

package main

import "fmt"

func main(){
    val := 10
    // stampa --> 'val' address: 0x10d50038
    fmt.Println("'val' address:", &val)

    ref := []int{1, 2, 3}
    // stampa --> 'ref' address: &[1 2 3]
    fmt.Println("'ref' address:", &ref)
}

L’operatore & restituisce il puntatore con l’indirizzo di memoria di una variabile qualsiasi, ma la funzione fmt.Println() esegue giustamente una stampa ad alto livello del puntatore alla variabile riferimento rispettandone la natura.
Anziché far decidere alla funzione tuttofare fmt.Println() il formato di stampa possiamo farlo esplicitamente usando il segnaposto %p (consulta la documentazione del pacchetto fmt):

package main

import "fmt"

func main() {
    // una slice, due variabili reference
    s1 := []int{1, 2, 3, 4 ,5}
    s2 := s1

    fmt.Printf("address di s1: %p\n", &s1)
    fmt.Printf("address di s2: %p\n", &s2)
    // stampano -->
    // address di s1: 0x10d6f100
    // address di s2: 0x10d6f0f0

    // verifichiamo che le due variabili
    // si riferiscono allo stesso slice
    s1[0]++
    s1[1]++
    s1[2]--
    s2[2]--
    s2[3]++
    s2[4]++
    fmt.Println(s1,s2)
    // stampa --> [2 3 1 5 6] [2 3 1 5 6]
}

La risposta iniziale è corretta: due variabili riferimento non sono puntatori non contenendo un riferimento diretto alla memoria ma contengono dati nascosti al programmatore ciascuna in un’area di memoria propria.

Risposta due

Sapendo che in Go alle funzioni vengono passate le copie degli argomenti, cosa accade se modifichiamo una variabile riferimento all’interno di una funzione?

Dunque, se l’argomento viene copiato all’interno della funzione la variabile riferimento sarà si una copia dell’originale, e conterrà quindi le stesse informazioni nascoste per l’accesso ai dati, ma un istruzione di modifica all’interno della funzione modificherà l’oggetto, l’unico oggetto, a cui sia l’argomento passato che l’argomento di funzione si riferiscono.

Utilizzando il tipo map[string]int lascio al lettore la scrittura del minicodice di verifica…

Risposta tre

In Go, conviene passare ad una funzione un puntatore a slice o lo slice stesso?

La variabile che contiene uno slice è reference, quindi passare un puntatore a slice è solo più complicato: non ci si guadagna nemmeno con la dimensione in memoria degli argomenti.

Una domanda per il lettore curioso

In Go le funzioni sono tipi di prima classe (come in Lua, copioni!). Questo significa che possiamo passare una funzione come argomento di un’altra e creare funzioni anonime.
La domanda è questa:

Le variabili che contengono una funzione in Go sono di tipo valore o di tipo riferimento?

Conclusione

Abbiamo studiato insieme le due classi di variabili in linguaggio Go, variabili valore e variabili riferimento, lasciando al lettore un po’ di utile lavoro da fare su alcuni esercizi.

Come regola generale i moderni linguaggi di programmazione fanno si che ai tipi di dato semplici come i numeri ed i booleani sia associato il tipo di variabile valore, mentre ai dati complessi come gli oggetti sia associato il tipo di variabile riferimento.

Un saluto.
R.

Ruby, le stringhe II

r4

Continua l’esame del trattamento delle stringhe con Ruby, iniziato qui.
Oggi si parla di sequenze di escape: vediamo un primo caso, semplice:

e0Le sequenze \n e \r sono sequenze di escape; la prima corrisponde all’a-capo Unix/Linux e funziona come ci si aspetta, la seconda al ritorno carrello, vale a dire “Hello” viene scritto, il cursore ritorna all’inizio della riga e sovrascrive “Word!” coprendo la scritta precedente. C’è da dire che per rendere la vita più interessante su Mac l’a-capo è \r e su Windows la coppia \r\n. Fino a un po’ di tempo fa l’editor testuale di Windows Blocco note o Notepad non interpretava correttamente \n; mi dicono che le cose sono cambiate anche se io continuo a consigliare sostituti freeware come Notepad2.

Ci sono poi altre sequenze di escape, ecco il tab:

e1

E se uno vuole mettere il carattere \ come deve fare?

e2

Chiaro no? backslash escappa (i fighi dicono quota) il carattere che segue immediatamente, se questo è speciale. Fare attenzione che i normali possono diventare speciali, cioè sequenze di escape.

e3

Domanda difficile: perché non ho usato \ per ogni carattere?

e4

Ci ricordiamo, vero, la differenza tra le stringhe delimitate da " rispetto quelle con ', come da esempio. E si può fare anche così:

e5

OK?
C’è ancora una cosa da dire sulle stringhe, caratteristico di Ruby (ma c’è qualcosa di simile nel mio linguaggio di scripting preferito, quello che mi fa perdere punti ogni volta che lo nomino): le quotature alternative (alternate quotes).

e6

Come si vede possiamo usare %q + un carattere a scelta al posto dell’apice e terminare la stringa con il carattere che abbiamo usato dopo q. E per le parentesi usiamo la coppia aperta all’inizio e chiusa alla fine.

Se %q + carattere sostituisce l’apice come faremo per le virgolette?

e7

Ci ricordiamo inoltre che possiamo mettere il valore di una variabile in una stringa quotata con le virgolette, cosa che abbiamo già visto ma forse serve un ripasso veloce:

e8

OK? E adesso basta con le stringhe! :-D

Iscriviti

Ricevi al tuo indirizzo email tutti i nuovi post del sito.

Unisciti agli altri 37 follower