Archivi Categorie: Linguaggi

Linguaggi di programmazione

JavaScript – stringhe e numeri – 2

Continuo da qui e finisco il rapido esame di Node.js con la valutazione se sia possibile usarlo per scripts veloci in alternativa a Python. Do subito la soluzione: sì se lo scripter è un ninjia per JavaScript o se lo script deve finire (modificato) anche nel Web. I miei validi  collaboratori  grokers Edo e PLG fanno notare che è prassi comune modificare gli script in ogni nuova istanza, a volte per pigrizia (es. nomi di files codificati nel codice), a volte per motivi più sensati (es. formattazione dell’output). Aspetta, non è una novità nuova: πάντα ῥεῖ.

Oltre a quanto raccontato nel post precedente (qui) restano (per le mie elaborazioni tipiche) da esaminare l’acquisizione di dati provenienti da fogli di calcolo tipo Excel, di solito il modulo Calc di Libre Office.

Node ha un modulo per gestire i file .xls e .xlsx:

# [SheetJS js-xlsx](http://sheetjs.com)

Parser and writer for various spreadsheet formats. Pure-JS cleanroom implementation from official specifications, related documents, and test files.
Emphasis on parsing and writing robustness, cross-format feature compatibility with a unified JS representation, and ES3/ES5 browser compatibility back to IE6.

Il modulo è installabile con npm ma da noi non viene usato. È la stessa situazione già vista con il modulo xlrd di Python.

C’è, rispetto a quanto visto nel post indicato, solo un problema legato alla rappresentazione dei numeri floating point, in particolare i separatori decimale e delle migliaia. Quest’ultimo è in genere usato solo dai gestionali, na se del caso…

Il file di dati che uso come esempio contiene questi dati:

Come già visto in precedenza acquisisto il testo dal file salvato come .csv

* xls $ node
> var fs = require('fs')
undefined
> var dati = fs.readFileSync('prova.csv', 'utf8').split('\n')
undefined
> dati
[ 'test di lettura files excel-like,,,',
  '1,"2,3",quattro,5^6',
  '2,"3,4",,dopo campo vuoto',
  '' ]

OOPS! sbagliato: la virgola iene usata sia come separatore di campo che separatore decimale. Nella creazione del .csv occorre definire il separatore di campo a punto-virgola

e sarà tutto OK

> var dati = fs.readFileSync('prova-pv.csv', 'utf8').split('\n')
undefined
> dati
[ 'test di lettura files excel-like;;;',
  '1;2,3;quattro;5^6',
  '' ]

Adesso è giunto il momento di usare le regexs, ne basta una per riga, replace non funzione su più linee:

> var ok1 = dati[1].replace(/,/g, '.')
undefined
> ok1
'1;2.3;quattro;5^6'

JavaScript (quindi Node) è –come dire– poco rigoroso sul tipo di dato che sta elaborando. Per fortuna esiste la funzione typeof, una manna da usare tutte le volte che possono esserci ambiguità:

> i = 6 * 7
42
> r = 22 / 7
3.142857142857143
> st = 'ciao!'
'ciao!'
> ov = 1 / 0
Infinity
> e = Number('x')
NaN
> typeof(i)
'number'
> typeof(r)
'number'
> typeof(st)
'string'
> typeof(ov)
'number'
> typeof(e)
'number'

va quasi bene, non distingue gli errori; ma non disperare, ci sono isNan e isFinite:

> n = Number('x')
NaN
> isNaN(n)
true
> isNaN(42)
false
> i = 1 / 0
Infinity
> isFinite(i)
false
> isFinite(42)
true

OK 🤩 JavaScript (e Node) ha quel che mi serve; solo che è diverso da Python (e da tutti gli altri linguaggi di programmazione), dipende dal programmatore, un po come parlare olandese o, ancora più panicoso, finlandese; poi ci sarebbero il coreano e il vietnamese (ma ci sono bambini di due anni che li parlano); questioni d’ambiente. Come JavaScript.

JavaScript – stringhe e numeri – 1

E se invece di Python lo script lo faccio con Node.js? Continuo da qui.

OK, letta una riga come stringa come raccontato nel post precedente voglio ricavare i numeri che la compongono. In JavaScript c’è la funzione split(), quella che mi serve, verifico:

* conv $ node
> st = '1 2 3.1415 4 5'
'1 2 3.1415 4 5'
> st.split(' ')
[ '1', '2', '3.1415', '4', '5' ]

sembra OK ma

> st = ' 1   2\t   3.1415 \t\t   4  \t5 '
' 1   2\t   3.1415 \t\t   4  \t5 '
> console.log(st)
 1   2	   3.1415 		   4  	5
undefined
> st.split(' ')
[ '',
  '1',
  '',
  '',
  '2\t',
  '',
  '',
  '3.1415',
  '\t\t',
  '',
  '',
  '4',
  '',
  '\t5',
  '' ]
> st.split(' \t')
[ ' 1   2\t   3.1415', '\t   4 ', '5 ' ]
> st.split('\t')
[ ' 1   2', '   3.1415 ', '', '   4  ', '5 ' ]

no; crea stringhe vuote e non toglie i tabs (\t).

Quando il gioco si fa duro s’invocano le regexs 💥 😁

> st
' 1   2\t   3.1415 \t\t   4  \t5 '
> st.match(/[^ \t]+/g)
[ '1', '2', '3.1415', '4', '5' ]

OK! è lei 🤩

Ci sono tante funzioni che convertono la stringa in numero. Mi riferisco agli interi ma salvo casi particolari vale tutto anche per i floating.

> sti = '123'
'123'
> +sti
123
> 1 * sti
123
> sti / 1
123
> sti - 0
123

ma attenzione:

> sti + 0
'1230'
> 0 + sti
'0123'

e poi ci sono stringhe malfatte

> stierr = '123a'
'123a'
> stierr * 1
NaN

Ci sono parseInt() e parseFloat() come nel C (quasi):

> sti = '123'
'123'
> parseInt(sti)
123
> parseInt(sti, 16)
291
> parseInt(sti, 8)
83
> stierr = '123z'
'123z'
> parseInt(stierr)
123
> parseInt(stierr, 16)
291
> parseInt(stierr, 8)
83
> parseInt(stierr, 2)
1

Niente errore, interrompe la valutazione della stringa appena trova un carattere non rappresentante una cifra.

Le stringhe possono essere in formato scientifico, (non con parseInt())

> '12e3' * 1
12000
> '12e-3' * 1
0.012
> '12E3' * 1
12000
> '4.2e1'
'4.2e1'
> '4.2e1' * 1
42
> '4.2e-1' * 1
0.42
> parseInt('56e3')
56
> // errore qui ^
undefined
> parseFloat('5.6e3')
5600

Esiste anche la funzione generica Number() che accetta per gli interi il formato esadecimale ma non l’ottale (d’altronde chi lo usa ancora nel 2018?)

> Number('12')
12
> Number('12.5')
12.5
> Number('12.5z')
NaN
> Number('0x12.5')
NaN
> Number('0x12')
18
> Number('012')
12
> Number('12e3')
12000
> Number('12.3e2')
1230

Quindi? Per me è importante che gli errori vengano segnalati, quindi niente parseX(). Per base 10 mi sa che Number() è OK altrimenti (a malincuore) la moltiplicazione per uno o la sottrazione di zero.

Uh! ht @ E&G 🤩

E se invece JavaScript (Node.js) – 2

Seconda parte dell’esame di Node.js per valutarlo in alternativa a Python. Continuo da qui.

Ho scoperto che i giovani HTMLers la sanno molto più lunga di me. Ci sono anche corposi manuali cartacei che, come da prassi, non verranno mai letti, permettendomi di ricorrere al solito tormentone: RTFM!

C’è online tutto quello che serve, per esempio:

Probabilmente anche altre, ma credo mi basti.

Il codice minimo del post precedente può essere usato come template per gli scripts da costruire. Mancano solo due aggiustamenti (in realtà di più ma almeno questi): formattare i numeri e controllare la visibilità/vita delle variabili.

Per i numeri uso le funzioni round(), toFixed(), toExponential() e forse altre ancora.

Lo script del post precedente viene aggiornato così (file stuff_2.js):

#!/usr/bin/node

my_elab = function(st) {
  var v = Number(st);
  var s = Math.sin(v);
  var e = Math.exp(v);
  var bl = " "
  var r1 = v + bl + s.toFixed(4) + bl + e.toExponential(4);
  if (v > 0) {
    var r2 = bl + Math.log(v).toFixed(4);
  }else{
    var r2 = " -----";
  }
  return r1 + r2;
}

if (process.argv.length < 3) {
  console.log("nome-script file-dei-dati");
  process.exit(2);
}

var fdati = process.argv[2];
var fs = require("fs");
var dati = fs.readFileSync(fdati, "utf-8").split("\n");

var ndati = dati.length;
if (dati[ndati - 1] == "") { ndati -= 1};

var fout = fdati + "-js.out"
fs.writeFileSync(fout, dati[0] + "\n\n", 'utf8');

for (let n = 1; n < ndati; n ++) {
  fs.appendFileSync(fout, my_elab(dati[n]) + "\n", 'utf8');
}

Lo rendo eseguibile e lo lancio

* py-js-2 $ cat dati
Test minimo per confronto tra Python e Node.js
1
0
-1
42
* py-js-2 $ chmod +x stuff_2.js
* py-js-2 $ ./stuff_2.js dati
* py-js-2 $ cat dati-js.out
Test minimo per confronto tra Python e Node.js

1 0.8415 2.7183e+0 0.0000
0 0.0000 1.0000e+0 -----
-1 -0.8415 3.6788e-1 -----
42 -0.9165 1.7393e+18 3.7377

A me la cosa che colpisce è la selva di var. È che serve per rendere la variabile locale. Lo so che sono banale ma per me lo faccio lo stesso un esempio (file loc-glo.js):

#!/usr/bin/node

my_elab = function() {
  var n = 42;
  console.log('in my_elab\ng = ' + g + '   n = ' + n);
}

n = 0;
g = 8;
my_elab();
console.log('in main\ng = ' + g + '   n = ' + n);

eseguendolo ottengo

* py-js-2 $ node loc-glo.js
in my_elab
g = 8   n = 42
in main
g = 8   n = 0

Modifico lo script togliendo l’istruzione var (file no-var.js):

#!/usr/bin/node

my_elab = function() {
  n = 42;  //questa è l'istruzione modificata
  console.log('in my_elab\ng = ' + g + '   n = ' + n);
}

n = 0;
g = 8;
my_elab();
console.log('in main\ng = ' + g + '   n = ' + n);

e ottengo:

* py-js-2 $ node no-var.js
in my_elab
g = 8   n = 42
in main
g = 8   n = 42

Convertendo programmi “vecchi” capita che (come si usava) si trovano variabili globali, funzioni non pure o che non fanno una sola cosa; il problema non è specifico per Node/JavaScript ma questo linguaggio lo esalta. Alle volte, se si ha un progetto chiaro, è da valutare se non codificarlo ex-novo. Con Node? solo in casi particolari, p.es. deve girare sia nel Web che in locale; o il coder conosce bene (o solo) JS. Altrimenti per questioni di standardizzazione io sono per Python e Octave e Rust e Bash (ma non per Windows, meglio convertire in .py o .js) e altri (visto che non ho citato Racket?). Poi, certo, ci sono anche C++, Java e –forse– una spruzzata di Ruby e Perl. Ah! multipiattaforma, Linux e Windows. Anche se…

E se invece JavaScript (Node.js) – 1

Il mio linguaggio di default (che raccomando) è Python, versione 3.x. Non sempre, ci sono casi in cui è meglio ricorrere ad altri (già detto che Python è lento?) ma considerando che ha i moduli NumPy, matplotlib &co, almeno 2 REPL, GUIs (che non uso), una buona documentazione, è usatissimo da una comunità molto attiva, è facile (ahemmm) e conosciuto (ahemmm).

Ovvio che ci sono casi per cui possono tornare migliori Octave, C++, Rust, Racket, Java e altri ancora.

In ogni caso –per me– cosa si usa dev’essere open e disponibile su Linux e Windows (per mio cuggino, non per me).

Adesso mi viene chiesto “E JavaScript, Node.js in particolare, offline?”. Uh! ci devo pensare, forse un revival di “E Ruby” che una volta si sentiva spesso; a proposito cosa si dice di Ruby?

JavaScript è popolare, come Python, anzi in certi ambienti molto di più. E conosciuto, come e, forse, più di Python. Si può usare per riscrivere piccoli programmi obsoleti? Per esempio quelli vecchi in Visual Basic. Forse anche le macro di Excel (detesto la parola “macro” usata in questo senso). Uh! ci devo pensare, anzi provo, ne avrò per un paio di posts, questo è il primo.

In passato avevo esaminato l’ottimo Eloquent JavaScript di Marijn Haverbeke, adesso c’è la versione 3. Fondamentale per i nuovi.

Ma per me niente online, niente Web, piuttosto elaborazioni numeriche –vengo dal Fortran, me number cruncher.

Un test minimo, leggere un file di dati e eseguire su questi qualche calcolo. Sia dati il file dei dati:

Test minimo per confronto tra Python e Node.js
1
0
-1
42

La prima riga è una stringa, un titolo, le successive sono valori numerici di cui voglio calcolare il seno, l’esponenziale e il logaritmo (naturale). È un primo approccio, minimo, mancano controlli, ma lo voglio semplice. Salta fuori che per un niubbo come me i due scripts risultano molto simili:

Python

#!/usr/bin/python3
from sys import argv
from math import sin, exp, log

def my_elab(st):
  v = float(st)
  s = sin(v)
  e = exp(v)
  r1 = f'{st} {s} {e}'
  if v > 0:
    r2 = ' ' + str(log(v))
  else:
    r2 = ''
  return r1 + r2

if len(argv) < 2:
  print("file dei dati non indicato")
  exit(2)

fdati = argv[1]
with open(fdati, 'r') as f:
  dati = f.readlines()

fout = fdati + '-py.out'
g = open(fout, 'w')
print(dati[0], file = g)

ndati = len(dati)
for n in range(1, ndati):
  print(my_elab(dati[n].rstrip()), file = g)
* py-js-1 $ chmod +x stuff.py
* py-js-1 $ ./stuff.py dati
* py-js-1 $ cat dati-py.out
Test minimo per confronto tra Python e Node.js

1 0.8414709848078965 2.718281828459045 0.0
0 0.0 1.0
-1 -0.8414709848078965 0.36787944117144233
42 -0.9165215479156338 1.739274941520501e+18 3.7376696182833684

Node.js

#!/usr/bin/node

my_elab = function(st) {
  v = Number(st);
  s = Math.sin(v);
  e = Math.exp(v);
  bl = " "
  r1 = v + bl + s + bl + e;
  if (v > 0) {
    r2 = bl + Math.log(v);
  }else{
    r2 = "";
  }
  return r1 + r2;
}

if (process.argv.length < 3) {
  console.log("file dei dati non indicato");
  process.exit(2);
}

var fdati = process.argv[2];
var fs = require("fs");
var dati = fs.readFileSync(fdati, "utf-8").split("\n");

var ndati = dati.length;
if (dati[ndati - 1] == "") { ndati -= 1};

var fout = fdati + "-js.out"
fs.writeFileSync(fout, dati[0] + "\n\n", 'utf8');

for (let n = 1; n < ndati; n ++) {
  fs.appendFileSync(fout, my_elab(dati[n]) + "\n", 'utf8');
}
* py-js-1 $ chmod +x stuff.js
* py-js-1 $ ./stuff.js dati
* py-js-1 $ cat dati-js.out
Test minimo per confronto tra Python e Node.js

1 0.8414709848078965 2.718281828459045 0
0 0 1
-1 -0.8414709848078965 0.36787944117144233
42 -0.9165215479156338 1739274941520501000 3.7376696182833684

OK, sì, richiede affinamenti OK 😁

Il caso di case

Ogni tanto capita di dover aggiornare un programma vecchio, adeguarlo, ampliarlo. Capita anche che deve funzionare anche con Linux e allora il dilemma se modificarlo o rifarlo ex-novo non si pone.

Usiamo cosa? Python, ovvio (ahemmm… quasi ma questa volta sì). E salta fuori la solita storia che adesso provo ancora una volta a dire che secondo me non ha ragione d’essere.

Il programma vecchio, in Visual Basic, quindi solo Windows contiene (semplifico, riduco all’essenziale) questo codice:

Select Case N
  case 0 To 1
    Print "piccolo"
  Case 2
    Print "giusto"
  Case 3, 4
    Print "grosso"
  Case Else
    Print "valori tra 0 e 4"
End Select

Select/Case è la versione Basic di switch/case del C e linguaggi da esso derivati. Per esempio JavaScript:

n = parseInt(process.argv[2]);
switch(n) {
  case 0 : case 1:
    console.log("troppo piccolo");
    break;
  case 2:
    console.log("giusto!");
    break;
  case 3: case 4:
    console.log("troppo grande");
    break;
  default:
    console.log("valori validi da 0 a 4");
}
* case $ node case.js 0
troppo piccolo
* case $ node case.js 1
troppo piccolo
* case $ node case.js 2
giusto!
* case $ node case.js 4
troppo grande
* case $ node case.js 100
valori validi da 0 a 4
* case $ node case.js -100
valori validi da 0 a 4

Con Bash è anche più sinttetico, ma non gira su Windows (cioè non su tutti i ‘puters, non su quello del Boss).

N=$1
echo "caso" $N
case $N in
  0 | 1) echo "piccolo" ;;
  2) echo "giusto" ;;
  3 | 4) echo "grande" ;;
  *) echo "NO! valori [0 .. 4]" ;;
esac

Python non ha un’istruzione simile, perché non serve:

from sys import argv
n = int(argv[1])

if n in [0, 1]   : print("troppo piccolo")
elif n == 2      : print("giusto!")
elif n in [3, 4] : print("troppo grande")
else             : print("valori validi da 0 a 4")

if/elif/else. Secondo me è anche molto leggibile (OK, dipende dai gusti). Una soluzione simile è immediata con JavaScript:

n = parseInt(process.argv[2]);

if ([0, 1].indexOf(n) >= 0)      { console.log("troppo piccolo"); }
else if (n == 2)                 { console.log("giusto!"); }
else if ([3, 4].indexOf(n) >= 0) { console.log("troppo grande"); }
else                             { console.log("valori validi da 0 a 4"); }

Vero che è più bello della versione case?

Le graffe …, direbbe un C-ista o un programmatore Java e/o JS. Ma anche un pythonista potrebbe andare a capo dopo il :. E il codice vero sarebbe più complesso, su più righe. Certo; questo è solo un esempio per dire che **case non serve quasi mai.

Ma sai che il Lisp… OK, ma il Lisp è diverso.

Un po’ di quirks con Python

Recentemente un tweet intercettato via un retweet di qualche tweep mi ha coinvolto in discussioni senza troppo senso ma –credo– da riportare.

KDnuggets visualizza una serie di tricks dichiarando che Most of the tricks and tips are taken from PyTricks and a few blogs.

Ecco ‏PyTricks di Max, brennerm. L’unico Max Brenner trovato su Twitter è una catena di bar, non credo sia chi sto cercando.

Ma mai disperare, una rapida googlata ed eccolo, qui. C’è –c’era– anche su Twitter ma sembra sia stata solo una prova, 42 tweets, l’ultimo vecchio di un anno.

OK, torno ai suggerimenti Python. L’inizio è stato l’inversione della stringa:

>>> st = '0123456789'
>>> rst = st[::-1]
>>> st
'0123456789'
>>> rst
'9876543210'

Con due giovani promesse della CS (ragazzi dovreste bloggare, imho) abbiamo cercato di mettere i valori appropriati nei camp i(start e stop) lasciati ai valori di default senza riuscirci. Perché pare che davvero non si può, vedi qui.

In Python c’è il metodo reverse() per le sequenze mutabili (ma le stringhe sono immutabili).

>>> ls = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> sl = ls.reverse()
>>> sl
>>> ls
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

uh! non quello che mi aspettavo (RTFM!): The reverse() method modifies the sequence in place for economy of space when reversing a large sequence. To remind users that it operates by side effect, it does not return the reversed sequence.

Altro trick intrigante è la trasposizione di una matrice. Max riporta:

>>> original = [['a', 'b'], ['c', 'd'], ['e', 'f']]
>>> transposed = zip(*original)
>>> print(list(transposed))
[('a', 'c', 'e'), ('b', 'd', 'f')]
>>> transposed
<zip object at 0x7f150fbd4448>

che, ovviamente, funziona. Il guaio è che che il codice non dice tutto quel che vorrei.

>>> orig = [['a', 'b'], ['c', 'd'], ['e', 'f']]
>>> trans = [*zip(*orig)]
>>> trans
[('a', 'c', 'e'), ('b', 'd', 'f')]

Ecco mancava un *: An asterisk * denotes iterable unpacking. Its operand must be an iterable. The iterable is expanded into a sequence of items, which are included in the new tuple, list, or set, at the site of the unpacking. [qui]. Conviene anche ripassare qui.

Mi sa che è da vedere anche zip(). Qui, qui, qui e qui.
Ovviamente dopo la documentazione ufficiale (già detto RTFM?), qui.

Ci sono altri suggerimenti, interessanti quelli sui dizionari; ma anche altri. Tutti.

Julia 1.0

The much anticipated 1.0 release of Julia is the culmination of nearly a decade of work to build a language for greedy programmers. JuliaCon2018 celebrated the event with a reception where the community officially set the version to 1.0.0 together.

Come si diceva all’inizio, nel 2012: We want a language that’s open source, with a liberal license. We want the speed of C with the dynamism of Ruby. We want a language that’s homoiconic, with true macros like Lisp, but with obvious, familiar mathematical notation like Matlab. We want something as usable for general programming as Python, as easy for statistics as R, as natural for string processing as Perl, as powerful for linear algebra as Matlab, as good at gluing programs together as the shell. Something that is dirt simple to learn, yet keeps the most serious hackers happy. We want it interactive and we want it compiled.

Ora c’è finalmente la versione 1-punto. Io ho perlustrato una versione precedente, già buona ma non ancora come questa. E ripeto la mia previsione che Julia sarà uno dei linguaggi del fururo. Anzi, fin da subito è da considerare in alternativa o complementariamente a Python, Octave (Matlab per i ricchi o quelli che devono fare cose davvero speciali), C++, altri (tanti altri).

Julia è speciale, dall’intro: Scientific computing has traditionally required the highest performance, yet domain experts have largely moved to slower dynamic languages for daily work. We believe there are many good reasons to prefer dynamic languages for these applications, and we do not expect their use to diminish. Fortunately, modern language design and compiler techniques make it possible to mostly eliminate the performance trade-off and provide a single environment productive enough for prototyping and efficient enough for deploying performance-intensive applications. The Julia programming language fills this role: it is a flexible dynamic language, appropriate for scientific and numerical computing, with performance comparable to traditional statically-typed languages.

Il team di sviluppo rockz, davvero, anzi über 💥, guarda qui la Wiki. E, ovviamente, il sito di Julia.

Ah! adesso qualcosa di completamente differente, in qualche misura complementare: ci sono altre novità, per altri linguaggi:

Python non si ferma mai, c’è la versione 3.7, con novità (l’evoluzione) che personalmente devo ancora approfondire.


Racket ha fatto un grande balzo, c’è la versione 7.0 ancora migliorata (mica facile ma sì, davvero).


Aggiornamento
Ahemmm… Julia non è ancora così a posto come vorrebbe (vorrei, vorremmo tutti). guarda qua: Think Julia: How to Think Like a Computer Scientist.
Ma crescerà, ricordiamoci sempre che Ankh-Morpork non è stata costruita in un giorno.

Un quiz & considerazioni personali relative

Cosa non si trova ontehtoobz! (auto-cit.) 😯 Ieri questo: I was just shown this. Guess what this does in Python? powers = [lambda x: x**i for i in range(10)] powers[3](2).

Sukant è conosciuto, in diversi socials, rockz. E, confesso, ho provato a risolvere il suo quiz, nel senso di correggere la funzione, senza riuscirci. Poi ho letto i commenti, già David A Roberts 💥 doveva mettermi sulla retta via:

* quiz $ py3
>>> def f(i): return lambda x: x**i
...
>>> powers = [f(i) for i in range(10)]
>>> powers[3](2)
8

ma ci sono altri come me che non sono così pronti: How is it Alex Jones got banned, and yet Sukant still has a twitter account after posting this?? dice Warner e Leftmost Grief Node: Haha the Pythonistas responding to this in my org are like, “Well that code is not Pythonic so…”.

das portal ci spiega il perché, Dan P ci dice che Scala…

Ma la risposta migliore è quella di jeeger che si potrebbe tradurre con *RTFM!*, in questo caso The Hitchhiker’s Guide to Python, in particolare –chiaro come il sole– Late Binding Closures.

Non copio è là.
Da leggere attentamente, anzi studiare che poi interrogo, nèh! 🧐

Ci ho pensato su. Anzi ci ho dormito su. E ho maturato una mia opinione che potrò cambiare se e solo se fatti o circostanze me lo consiglieranno.

In questo caso –e tanti altri casi simili– il problema non esiste:

>>> pow(2, 3)
8
>>> pow(2, 0.5)
1.4142135623730951
>>> pow(-2, 0.5)
(8.659560562354934e-17+1.4142135623730951j)

Non ce l’ho con lambda e le funzioni e quant’altro ma con il loro (ab)uso. E usare solo quello che si conosce davvero. Detto in altri termini non ho ancora digerito il fatto che non ho trovato il bug 👿

Comunque…

Siate semplici (cit.). Anzi, qui. È talmente autoevidente che non si sa di chi è; è Patrimonio dell’Umanità (se non lo è dovrebbe esserlo).

Formattare le stringhe in Python

Python lo uso da sempre (cioè no, ho solo dimenticato quando l’ho scoperto (o meglio il numero della versione che mi sembra di ricordar è troppo basso per essere credibile) e capita che ogni tanto scopro di non essere aggiornato, sapete l’evilussione 🧐 Per fortuna c’è Twitter che mi suggerisce, via prof Corno ecco: Python 3’s f-Strings: An Improved String Formatting Syntax (Guide).

La documentazione ufficiale corrente di Python (3.6 o mi sto perdendo qualcosa anche qui? sì. c’è la 3.7 nuova di pakka, non ancora installata) è aggiornata e dice tutto, anche se in modo un po’ troppo sintetico per quelli come me.

A formatted string literal or f-string is a string literal that is prefixed with ‘f‘ or ‘F‘.

Conviene quindi seguire la guida di RealPython, anzi RealPython è da followare su Twitter, fatto.

Anzi, la guida è talmente OK che non continuo a scrivere, c’è tutto là. A me resterebbe di aggiornare gazillioni di script che usano non immaginate cosa (l’evilussione) ma poi mi viene da pensare che (cit.)

anche perché capita troppo spesso che parto con l’idea di aggiornare e mi scopro a riscrivere ⭕

La funzione è …

Un thread su Twitter mi fa monta la voglia di dire la mia. E so resistere a tante cose ma non alle tentazioni (quasi-cit.) e allora ecco questo post senza pretese. Anzi forse nemmeno da leggere.

Tutto parte da qui: a function is sort of a container for bugs.

Siccome il post è mio dico subito la mia risposta: la funzione è un valore, esempio:

Ma anticamente la risposta sarebbe stata diversa. Qualcosa come: una funzione è un sottoprogramma che ritorna un valore; nella pratica si preferisce usare le subroutines, forse solo per convenzione.

Sì, erano i tempi in cui si usava il Fortran (l’unico (quasi) linguaggio disponibile su quel ‘puter). Volevo riportare un esempio di allora, ho lanciato bigG ed ecco:

sono soddisfazioni, se lo fate voi che avete interessi migliori probabilmente la risposta è diversa; in ogni caso mi dice solo che è una memoria indelebile per me. E forse per altri. Forse non solo la T$AMLC (se interessa: gestione comunicazioni con periferiche seriali). Esiste un sito storico, Bitsavers, con tanti documenti storici ma non ho trovato quel che cercavo.

Erano una cosa molto diversa da cosa si scrive oggi; invece di raccontarlo vi rimando a questa pagina: Functions and Subroutines.

Bella ma incompleta. Segue le prescrizioni del Fortran IV (1966). Non dice chiaramente che in Fortran tutti i dati sono passati per indirizzo e se vengono (o meglio venivano, oggi le cose sono migliorate, vedi p.es. qui) modificati all’interno del sottoprogramma (funzione o subroutine) risulteranno modificati al ritorno dalla chiamata. E manca di ricordarci del COMMON, allora pervasivo per quello che sarebbero diventate le variabili globali, anzi più specifico, migliore (anche nella creazione di bugs difficili da stanare).
Nella pratica poi le variabili venivano dichiarate solo se di tipo diverso da quello implicito (era anzi considerata una prassi da evitare non seguire la convenzione implicita).

OK, fine della premessa; torno al tweet. Ron dice “ma il BASIC…”. Vero. Anzi anche peggio. Il GOSUB era facoltativo, sostituibile con GOTO, ma dovevi accordarti con RETURN. Poi il linguaggio è migliorato notevolmente, solo i vecchi si ricordano di questo e ci twittano.

Algol fin da allora distingueva valori e indirizzi nei parametri. Poi è arrivato il C, dove ci sono solo le funzioni. Ma possono restituire null. E sono puntatori.

Haskell invece –uh! post troppo lungo ⭕