Archivi Categorie: Linguaggi

Linguaggi di programmazione

mou: non funziona, non so il perché e allora la faccio funzionare; senza sapere il perché

art-ma-3

Tutto parte da questo tweet che rimanda a questo post.

Uh! semplice, non so se possa servire ma è sexy lo provo:

$ history | awk '{print $2}' | awk 'BEGIN {FS="|"}{print $1}' | sort | uniq -c | sort -n | tail | sort -nr
    103 spo
    100 mtw
     94 stw
     53 history
     47 _j
     46 _e
     42 sx
     39 cd
     33 bash
     29 gen
$

OK! ma mica devo scrivere ogni volta tutto questo; lo meto in uno script ed ecco:

$ cat m0
history | awk '{print $2}' | awk 'BEGIN {FS="|"}{print $1}' | sort | uniq -c | sort -n | tail | sort -nr
$ bash m0
$

OOPS! NO! non va.

Qui –no, non so spiegarmi il perché– ho dato la colpa a awk e l’ho sostituito con sed. sed è più semplice, o ci sono più abituato, anche se quando sono finito su Unix mi sono procurato subito le fotocopie della shell (di SisV), di vi (un articolo scritto da Bill Joy per Hewlett-Packard) e awk (sì perché sembrava intrigante, assay.

La mia versione:

$ history | sed 's/[ 0-9]*//' | sed 's/[ ].*//' | sort | uniq -c | sort -n | tail | sort -nr
    103 spo
     98 mtw
     93 stw
     54 history
     47 _j
     46 _e
     42 sx
     39 cd
     34 bash
     30 gen
$

OK! sì lo so che si potrebbe approfittare che history scrive il nome del programma sepre in colonna 7 ma già vengo spesso accusato di fortranismo residuo…

Provo a scriptarlo e

$ cat m1
history | sed 's/[ 0-9]*//' | sed 's/[ ].*//' | sort | uniq -c | sort -n | tail | sort -nr
$ bash m1
$

NO! come prima. Ho sentito Stack Overflow,  googlato  duck-duck-go-ato; visto che anche altri chiede a riguardo per operazioni simili riguardanti history ma senza risultati. Allora ho cancellato tutto, in fondo a chi interessa una cosa così.

Poi capita che di notte, avete presente (sensa comparision (pron. sensa cumparisiun) dicono qui in Piemonte) Srinivasa Ramanujan e la dea Lakshmi di Namagiri? Ecco a me lo stesso ma seguo la Vera Fede e allora vengo ispirato da Sua Pastosità il Prodigioso FSM –sempre sia condito, RAmen– subito nella prima notte utile.

Fortuna che quando cancelli il ‘puter sa che gli umani a volte non sanno quello che fanno o poi cambiano idea e allora si può ripristinare il cancellato.

Se il problema è history lo si può tener fuori dallo script e eseguire con una pipe. (Nota OT, qui: non so mai che genere usare, mi manca il neutro). Lo script (mou-r) diventa:

#!/bin/bash
sed 's/[ 0-9]*//' | sed 's/[ ].*//' | sort | uniq -c | sort -n | tail | sort -nr

che abilitato e copiato in ~/bin produce

$ history | mou-r
    102 spo
     98 mtw
     93 stw
     57 history
     46 _j
     46 _e
     42 sx
     39 cd
     35 bash
     30 gen
$

OK, ma è troppo lungo, creo un alias

$ alias mou='history | mou-r'
$ mou
    102 spo
     98 mtw
     92 stw
     57 history
     46 _j
     46 _e
     42 sx
     39 cd
     35 bash
     30 gen
$

Perfetto. L’alias ovviamente va messo in ~/.bash_aliases (o qualche altro file eseguito al boot. Ah! mou sta per most used. Resta il mistero di history ma frankly, my dear, I don’t give a damn (cit.) 🧐

Gestione di una coda in Python – 1

art-ma-2

Un post facile, didascalico, introduttivo alla valutazione per uno script che si comporta proprio come la fila allo sportello. Altamente sperimentale, nèh!

Si può fare con una lista. Ecco come crearla e inserire elementi:

>>> ls = []
>>> len(ls)
0
>>> ls.append(1)
>>> ls.append(2)
>>> ls.append('tre')
>>> ls
[1, 2, 'tre']
>>> len(ls)
3

Richiamare elementi:

>>> ls[2]
'tre'

Verificare se un elemento è presente:

>>> 2 in ls
True
>>> 8 in ls
False

Estrarre elementi:

>>> ls = [1, 2, 3, 4, 5, 6]
>>> ls.pop()
6
>>> ls
[1, 2, 3, 4, 5]

non come serve, funziona come una pila (stack). È però immediato rimediare, così:

>>> ls.pop(0)
1
>>> ls
[2, 3, 4, 5]
>>> ls.pop(0)
2
>>> ls
[3, 4, 5]

In previsione di un uso in qualche misura intensivo conviene costruire funzioni per semplificarne la gestione. Le funzioni che si costruiscono sono semplici e per quanto possibile conviene usare la sintassi lambda. Inoltre i nomi inizieranno tutti con q (come  coda  queue).

L’assegnazione è semplice, resta LS = [].

Inserimento

>>> qapp = lambda ls, dat : ls.append(dat)

la provo:

>>> LS = []
>>> qapp(LS, 'a')
>>> qapp(LS, 'b')
>>> qapp(LS, 'c')
>>> LS
['a', 'b', 'c']

Estrazione del primo dato, quello in testa il car del Lisp:

>>> qext = lambda ls : ls.pop(0)  # versione iniziale, migliorabile
>>> d = qext(LS)
>>> d
'a'
>>> e = qext(LS)
>>> e
'b'
>>> f = qext(LS)
>>> f
'c'
>>> g = qext(LS)
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 1, in 
IndexError: pop from empty list

OOPS! funziona se la lista non è vuota; da correggere testanto la lista con un if che in lambda ha una sintassi sua:

>>> qext = lambda ls : ls.pop(0) if len(ls) > 0 else None
>>> LS = ['1', 'b', 'ultimo']
>>> qex(LS)
'1'
>>> qext(LS)
'b'
>>> d = qext(LS)
>>> d
'ultimo'
>>> e = qext(LS)
>>> print(e)
None

OK; per estrarre un dato che non è il primo

>>> qexnth = lambda ls, n : ls.pop(n) if len(ls) >= n else None
>>> LS = [1, 2, 3, 4]
>>> qexnth(LS, 2)
3
>>> LS
[1, 2, 4]
>>> d = qexnth(LS, 5)
>>> LS
[1, 2, 4]
>>> print(d)
None

Ma è più utile estrarre un dato in base an suo valore, non per l’indice

>>> qexd = lambda ls, dat : ls.pop(dat in ls) if dat in ls else None
>>> LS = [1, 10, 100]
>>> qexd(LS, 10)
10
>>> LS
[1, 100]
>>> qexd(LS, 1000)
>>> LS
[1, 100]

OK 😁 Continua, forse 😁

Radici di espressioni e interattività

sl104

Continuo (in qualche misura) con quanto detto qui, integro, rispondo a osservazioni, varie e tutte sensate.

Per prima cosa si è visto che il calcolo simbolico non è (nel nostro caso) necessario (o quasi mai). E, ma lo sapevamo già, Maxima anche; anche se a me piace e continuerò a parlarne 🧐

In questi giorni Redmonk ha pubblicato la lista aggiornata dei linguaggi più usati. Cose interessanti, non nuove ma una sbirciatina è d’obbligo.

Sì, JavaScript (con altri, Java, C++) è in testa 🤩 Principalmente, credo, per il Web, dentro le pagine HTML, ma si può –e viene– usato anche fuori, vedi Node.

Io sono tifoso di Python, anche per via di NumPy, SciPy, Matplotlib &co. In alternativa c’è Octave (la versione FOSS di MATLAB). Ci sarebbe anche Julia ma è ancora poco usato, troppo nuovo e con modalità diverse da quella corrente (l’opposto di Node); per adesso vale quanto detto per Maxima, ma forse in futuro…

Per elaborazioni semplici e da aggiustare spesso sono da escludere i linguaggi compilati, C++, Java e altri ancora (Rust).

Sì, resta JavaScript, Node in questo caso, alternativo a Python. Per chi lo usa abitualmente va bene, per me dovrei impratichirmi con i nomi delle funzioni e dove trovarle nella galassia di packages (sbaglio o è labirintica?). Ma funziona, è OK anche Node.

Riporto due casi dal post citato, usando Python:

$ python3 -q
>>> expr = "(a + b)**2 - b**2 + a*b - c"
>>> a = 1; b = 2; c = 3
>>> eval(expr)
4
>>> from sympy import real_roots, Symbol, real_roots
>>> x = Symbol('x')
>>> expr = 8*x**3 + 4*x**2 + 2*x - 258
>>> real_roots(expr)
[3]
>>> len(real_roots(expr))
1
>>> real_roots(expr)[0]
3

In tanti ci dicono di usare eval() solo quando necessario per tanti motivi, p.es. è pesante per la macchina, ma nel nostro caso è quel che serve, evita di dover costruire una funzione ad hoc o usare lambda.

Naturalmente lo stesso vale per Node:

$ node
> expr = "(a + b)**2 - b**2 + a*b - c"
'(a + b)**2 - b**2 + a*b - c'
> a = 1; b = 2; c = 3
3
> eval(expr)
4

Per le radici delle equazioni occorre installare qualche package, ce ne sono diversi, a una rapida googlata ho trovato nerdamer, Algebrite, algebra.js, math.js, Zolmeister, … Da valutare e scegliere. Da vedere con npm search.

Gli esempi (Python e Node) visualizzano versioni elementari di REPL (read-eval-print-loop) cioè l’uso interattivo di linguaggi di programmazione. Ai vecchi come me viene da dire “ah, come il BASIC!“; altri (sì, anche me) invece direbbero “ah, il LISP!“. Per calcoli al volo è l’ideale. Quelle visualizzate sono le REPL minime, ne esistono di molto più evolute e potenti, p.es. l’ambiente di Octave. Secondo me REPL è la via.

Radici e espressioni simboliche

elea3

Un paio di idee, senza arrivare agli script, anche perché –adesso vi conto.

Non dico che sono in ritardo su quanto ho promesso; faccio quello che gli americani chiamano appellarsi al quarto emendamento, ecco.

Ci sono tanti linguaggi di programmazione, alcuni rendono più semplice fare determinate operazioni, altri sono più simpatici (non a tutti, ovvio), altri ancora non sono così conosciuti (almeno da me).

Ci sono argomenti che ho già trattato in passato ma che adesso salta fuori che sarebbe opportuno riprendere:

  • trovare le radici di un’espressione;
  • semplificare un’espressione simbolica.

Si può fare? ci provo.

Trovare le radici di un’espressione

0

Considero l’espressione in figura, equazione di terzo grado, il primo che è moderatamente ostico (o comunque non immediato). _c è uno script derivato da bc, ne semplifica in qualche misura l’uso (forse lo racconterò, non oggi che sono in ritardo).

La prima idea, quella giusta secondo me, è di usare Maxima:

(%i1) expr : 8*x^3 + 4*x^2 + 2*x - 258 = 0;
                             3      2
(%o1)                     8 x  + 4 x  + 2 x - 258 = 0
(%i2) solve (expr);
                    sqrt(123) %i + 7      sqrt(123) %i - 7
(%o2)        [x = - ----------------, x = ----------------, x = 3]
                           4                     4

OK, ma delle tre soluzioni due sono complesse; se voglio solo quelle reali opero così:

(%i3) realroots (expr);
(%o3)                               [x = 3]

OK, ma Maxima è vecchio, implementato come si usava una volta, poco conosciuto, …

Qualcosa di più mainstream, tipo JavaScript? Ahemmm… quello no, ma c’è Python.
Per le espressioni simboliche si può usare il package SymPy.

>>> from sympy import Symbol
>>> x = Symbol('x')
>>> expr = 8*x**3 + 4*x**2 + 2*x - 258
>>> from sympy.solvers import solve
>>> solve(expr)
[3, -7/4 - sqrt(123)*I/4, -7/4 + sqrt(123)*I/4]

OK, da tutte le soluzioni, anche quelle complesse; notare che SymPy usa anche simboli aggiuntivi rispetto a Python, qui I equivale a j di Python. Naturalemte se voglio solo le soluzioni reali posso:

>>> from sympy import real_roots
>>> real_roots(expr)
[3]

OK. JavaScript, in realtà Node resta da vedere, e personalmente m’interessa poco, anzi niente.

Semplificazione espressioni simboliche

Alle volte capitano; roba che si fa in prima media, e forse me la sono dimenticata.
Con Maxima ho:

(%i1) expr : (a + b)^2 - b^2 + a*b - c;
                                         2    2
(%o1)                     (- c) + (b + a)  - b  + a b
(%i2) ratsimp (expr);
                                               2
(%o2)                         (- c) + 3 a b + a
(%i3) a : 1; b : 2; c : 3;
(%o3)                                  1
(%o4)                                  2
(%o5)                                  3
(%i6) ev (expr);
(%o6)                                  4

Mentre com Python:

>>> from sympy import Symbol, simplify
>>> a = Symbol('a')
>>> b = Symbol('b')
>>> c = Symbol('c')
>>> expr = (a + b)**2 - b**2 + a*b - c
>>> simplify(expr)
a**2 + 3*a*b - c

Calcolare il valore numerico è un po’ più quixotico, serve lambda:

>>> expr
a**2 + 3*a*b - c
>>> f = lambda a, b, c : a**2 + 3*a*b - c
>>> f(1, 2, 3)
4

ma si può fare di meglio:

>>> a = 1; b = 2; c = 3
>>> expr = a**2 + 3*a*b - c
>>> eval(str(expr))
4

Ci sarebbe ancora un’alternativa (sexy) ma non adesso; prossimamente; forse.

Numeri troppo grandi o troppo piccoli – 2

sl100

Continuo (e concludo) su come tratto numeri troppo grandi o troppo piccoli, proseguendo da qui e con un chiarimento (solo per me) raccontato qui.

Il post precedente si concludeva con l’osservazione che a volte è meglio Excel (o equivalenti open source). E poi un conto è la programmazione ma alla fine nella relazione i numeri devo scriverli in italiano.

Conviene usare Python (o, in alternativa Node), qualcosa come questo:

$ python3 -q
>>> N = 123456789
>>> t = '{:,}'.format(N)
>>> t
'123,456,789'
>>> t = t.replace(',', '.')
>>> t
'123.456.789'

OK, facilmente leggibile; volendo si potrebbe anche rendere come piace a qualcuno, i mate per esempio, usando uno spazio al posto del punto, così:

>>> t.replace('.', ' ')
'123 456 789'

Questo va bene per gli interi ma deve funzionare anche per i numeri reali, quelli con la virgola (cioè  il punto  no la virgola).

>>> N = 123456789.369
>>> t = '{:,}'.format(N)
>>> t
'123,456,789.369'
>>> t = t.replace('.', '_')
>>> t
'123,456,789_369'
>>> t = t.replace(',', '.')
>>> t
'123.456.789_369'
>>> t = t.replace('_', ',')
>>> t
'123.456.789,369'

OK, un ultimo passo: la precisione cioè il numero di cifre dopo la virgola:

>>> round(N, 2)
123456789.37

OK, notare che round() funziona con i numeri; si può usare anche con le stringhe ma queste devono essere  ‘mericane  da programmatori:

>>> N = '123456789.125'
>>> round(float(N), 2)
123456789.12

altrimenti ottengo un errore

>>> N = '123456789,125'
>>> round(float(N), 2)
Traceback (most recent call last):
  File "", line 1, in 
ValueError: could not convert string to float: '123456789,125'

OK, l’arrotondamento va fatto prima dell’italianizzazione del numero. Raccogliere tutto quanto in unn script è immediato (fnum.py):

from sys import argv
if len(argv) == 1:
    prec = 2
else:
    prec = int(argv[1])

st = input('')
# '{:,}'.format()
st = st.replace(',', '.')
isfloat = '.' in st
try:
    if isfloat:
        n = round(float(st), prec)
        if n == int(n):
            n = int(n)
    else:
        n = int(st)

except:
    print('errore!')
    exit()

#italianizzo
t = '{:,}'.format(n)
t = t.replace('.', '_')
t = t.replace(',', '.')
t = t.replace('_', ',')

print(t)
$ echo 123456789.369 | py3 fnum.py 2
123.456.789,37
$ echo 123456789 | py3 fnum.py 2
123.456.789
$ echo 123456789.0002 | py3 fnum.py 2
123.456.789

OK, proprio come Excel (o meglio equivalenti open). E possiamo anche inserirlo automaticamente nella clipboard, come raccontato nel secondo dei post citati:

$ echo 123456789.0002 | py3 fnum.py 2 | _k
123.456.789
$ echo `_p`
123.456.789

Ovviamente potevo scrivere (meglio) anche:

$ echo $(_p)
123.456.789

Ma in fondo posso inglobare nello script anche _k, troppo semplice , lasciato come esercizio. Inoltre in quest’ultimo caso lo script fnum.py non risulta più Windows compatibile (dovesse servire).

Altro esercizio, allenarsi su quanto suggerito da Katie, qui.

Gestire la clipboard

top4

A me nessuno dice mai niente ma indagando ontehtoobz ogni tanto trovo cose…
Come xclip, fa proprio quello che volevo da tanto tempo. A dirla tutta ne ho già parlato ma non in modo organico, cerco di rimediare.

Niente di meglio che un esempio per vedere cosa fa, con la teoria non sono tanto buono, ecco:

c0

e nella clipboard ho, vediamo con Crtl-V

prima riga
seconda
terza.

cioè l’output del comando precedente. Comodo, molto più comodo di selezionare il testo e copiarlo con Maiusc-Ctrl-C.

Tanto comodo (come si vedrà in un prossimo post (forse)) che ho creato lo script _k, abilitato e messo in ~/bin

#!/bin/bash
xclip -f -selection clipboard

Da testare:

c2

e nella clipboard ho:

3.142857142857143
3.141592653589793

Come Ctrl-V fa coppia con Ctrl-C creo _p che fa coppia di _k:

#!/bin/bash
xclip -o  -selection clipboard

lo abilito e sposto in ~/bin ed ecco:

c4

Per sapere tutto di xclip aprire questo link. Prossimamente risulterà meno oscuro come intendo utilizzare _k &co (probabilmente… forse…) 😐

La precisione e la realtà

s-p

Ogni tanto qualcuno riporta una variante di come il ‘puter sbaglia clamorosamente su cose semplicissimissime. Per esempio ecco Vanessa (rockz! 💥, vorei essere come lei (anche se non sono invidioso per natura, nèh!)): […] the people that make fun of JS for 0.1 + 0.2 ≠ 0.3.

Uh! a me fa venire in mente due cose. Comincio con la verifica

$ python3 -q
>>> 0.1 + 0.2
0.30000000000000004
>>>
$ node
> 0.1 + 0.2
0.30000000000000004
$ racket
Welcome to Racket v6.11.
> (+ 0.1 0.2)
0.30000000000000004
> ;; ma anche
  (+ 1/10 2/10)
3/10
> ;; e
  (exact->inexact (+ 1/10 2/10))
0.3
$ echo "0.1 + 0.2" | bc
.3
$ calc 0.1 + 0.2
	0.3
$ maxima -q
(%i1) 0.1 + 0.2;
(%o1)                                 0.3

OK, Racket rockz! 💥, come già si sapeva. Ma gli interpretati dinamicamente come JavaScript, Python e altri ancora, da me non usati correntemente, fanno proprio come dice Vanessa (et al.).

Non è solo una stravaganza della REPL, è davvero vero:

$ python3 -q
>>> (0.1 + 0.2) == 0.3
False
>>> round((0.1 + 0.2), 3) == 0.3
True

Non è una cosa nuova, anzi –qui entro nel c’era una volta– io che sono vecchio ricordo che fin da quando ho cominciato c’erano due funzioni (nostre, cioè fatte da uno di noi o prese da qualche guru nostrano (o anche no)): LEQR e LEQD, rispettivamente per argomenti REAL (4 bytes) e DOUBLE PRECISION (8 bytes); da dichiarare LOGICAL. Con Python diventano una sola, qeq (sta per quasi-equal):

def qeq(a, b):
    return abs(a - b) < 1e-15

Da provare:

$ py3
>>> def qeq(a, b):
...     return abs(a - b) < 1e-15 
... 
>>> qeq(0.1 + 0.2, 0.3)
True
>>> from math import pi
>>> qeq(22/7, pi)
False
>>> #difatti
>>> 22/7 - pi
0.0012644892673496777

Ma se devo dire tutta la verità questa funzione (nelle due varianti) non veniva usata: era lunga una sola riga quindi niente risparmio di battute per contro l’uso comportava un carico maggiore per il sistema, la chiamata a funzione. Volendo potrei raccontare dello statement function, ecco l’inizio di pag. 7-7 del manuale del Fortran IV di Pr1me:

st-fun

Anch’io so cose lollose come Vanessa, solo di utilità assolutamente nulla (oggi, come passa il tempo!).

H/T a AP che mi ha corretto la bozza. E i suggerimenti.

Numeri troppo grandi o troppo piccoli – 1

Scrooge-McDuck-Money-Bin-1000x562

Alle volte capita di avere a che fare con numeri che sono non come quelli normali, immaginabili facilmente dagli umani (anche da me).

Escluderei subito Paperon de’ Paperoni che, secondo Don Rosa, possiede five multiplujillion, nine impossibidillion, seven fantasticatrillion dollars and sixteen cents e per Carl Banks il deposito (Bin) contiene three cubic acres of money e qui non è solo questione di ordini di grandezza ma peggio, roba che in confronto gli stringhisti sono sensati, per cui salto e ricomincio.

Prendiamo per esempio gli astronomi, loro trattano roba come:

Sono parenti stretti dei fisici, a occhio nudo non si riesce (almeno io non ne sono capace) a distinguere gli uni dagli altri. Ma anche loro hanno (danno) numeri che –per esempio:

  • Massa del protone: 1.67262171(29)e-27 kg ossia 0.938 GeV/c2;
  • L’elettrone è ancora più piccolo (ma siamo lì) invece il neutrino (uno a caso, non facciamo i difficili) andiamo su 0.05 eV/c2 o 8.913309e-38 kg.

 Senza senso  Per fortuna esistono i multipli e i sottomultipli delle unità di misura, almeno per SI, il Sistema internazionale di unità di misura.

Quindi usare SI, unità e multipli; poi quando servono se ne fanno altre, come già visto per il parsec. Per esempio i fisici usano il barn (b = 10e-28 m2). Umorismo qui: un fienile di quelle dimensioni non ha senso (tranne per i fisici, ovviamente, Marco lo usa a volte, anche inverso).

Senza arrivare a questi estremi anch’io (con l’aiuto di più amici, questi script sono nati come un gioco, facendo altro) sto tentando di addomesticarli per quanto possibile. Il lavoro non è concluso ma un paio di script consentono di visualizzare il numero normale in formato scientifico (2sc) e viceversa (2ns).

Rendere il numero in formato scientifico – 2sc

#!/bin/bash
N=$@
if [ -z $NFORM ]; then
    NFORM="%.4e"
fi
N=$(echo $N | sed 's/\./\,/g')
N=$(printf "$NFORM  " $N)
N=$(echo $N | sed 's/\,/\./g')
echo $N | xclip -f -selection clipboard

Il lavoro viene svolto da printf, on base al formato, %.ne dove n è un intero positivo non troppo grande; di deafault vale 4, cioè quattro cifre decimali ma è possibile cambiarlo facilmente senza modificare lo script (come vedremo tra breve).

C’è un problema, per alcuni di noi, quelli abituati a programmare: Bash vuole la virgola come simbolo di definizione per la parte decimale (per noi italiani) contrariamente all’abitudine e a (quasi) tutti i linguaggi di programmazione. Niente panico con sed si risolve prima di subito.

Visto che il risultato di queste trasformazioni è spesso da usare immediatamente entra in gioco xclip che inserisce nella clipboard il risultato visualizzato.

$ 2sc 9460700000000000
9.4607e+15
$ 2sc 30857000000000000
3.0857e+16
$ 2sc 149597870700
1.4960e+11

e, ovviamente

$ 2sc 9460700000000000 30857000000000000 49597870700
9.4607e+15 3.0857e+16 4.9598e+10

Il numero di cifre decimali è facilmente ridefinibile: se non va bene il alore 4 di default si può ridefinire la variabile d’ambiente NFORM, con

export NFORM='%.3e'

e si può verificare con

$ echo $NFORM
%.3e

e si avrà

$ 2sc 9460700000000000 30857000000000000 49597870700
9.461e+15 3.086e+16 4.960e+10

NFORM conserva il valore all’interno del terminale, chiudendolo diventa indefinita. È possibile annullarla (sdefinirla?, undefinirla?) con export NFORM=

Ma si può semplificare, con uno script (particolare, in qualche misura atipico), ecco nprec

#!/bin/bash
if [ -z $1 ]; then
    export NFORM=''
else
    export NFORM='%.'$1'e'
fi
echo $NFORM

e ottengo

$ source nprec 2
%.2e
$ echo $NFORM
%.2e
$ source nprec

$ echo $NFORM

$

La particolarità è che lo script va eseguito con source.

Da formato scientifico all’usuale – 2ns

#!/bin/bash
R=""
while [[ $# -gt 0 ]]; do
    N=$(echo $1 | sed -e 's/\./\,/')
    N=$(printf "%.30f" $N)
    ye=$(echo $N | grep -c "\,")
    if [[ $ye == 1 ]]; then
        N=$(echo $N | sed -e 's/[0]*$//')
        N=$(echo $N | sed -e 's/\,$//')
    fi
    N=$(echo $N | sed -e 's/\,/\./')
    R=$R' '$N
    shift
done
echo $R  | xclip -f -selection clipboard

La trasformazione della rappresentazione del numero è svolta da printf.

Notare l’if che controlla se il numero ha una parte decimale; se sì sed elimina gli 0 finali e se il piunto è finale anche questo viene eliminato. Queste operazioni richiedono di trattare un numero alla volta, sempre il primo ($1) perché l’ultima istruzione del loop while è shift che, appunto, elimina l’elemento appena trattato.

$ 2ns 9.4607e15
9460700000000000
$ 2ns 3.0857e16
30857000000000000
$ 2ns 1.49597870700e+11
149597870700

OK, funziona, con numeri non troppo grandi (sorry Mr Scrooge!)

$ 2ns 1.23e+25
12300000000000000000000000
$ 2ns 1.23e+29
123000000000000000002281701376
$ 2ns 1.23e+30
1229999999999999999988457275392
$ 2ns 1.23e+35
122999999999999999998102457678823424
$ 2ns 1.23e+40
12300000000000000000542639153683841941504

OOPS! la precisione, a un certo punto non sono più 0 tondi ma altre cifre 😡 Ma tanto questi numeri sono illeggibili (o meglio io non riesco a leggerli). Da fare (prossimamente?) per renderli più amichevoli: separare con spazi le cifre in gruppi di 3 come usano i commerciali, volendo anche con i punti proprio come fanno i Rag.

Linguaggi, quali?

dtkx

Marco Rogers rockz! 💥; sa di essere “pretty much the best guy you know“, ha tanti follower, conosce über-nerds che seguo anch’io e potrei continuare con tante altre positività che poi risulterebbe un post di pubblicità.

Invece no, qualcuno ha risposto a un suo tweet quasi sondaggio e, manco fossi Dirk Gently, sono arrivato a lui e al tweet: If I was learning a first programming language today, here are the ones I would consider.

La sua lista è in buona parte condivisibile rispetto al mercato. Tanti oggi sono sul Web e allora JavaScript è la risposta, con contorno di CSS. Altrimenti Python, è il suo momento, specie considerando NumPy, Matplotlib &co; e anche TensorFlow (sempre &co. ma ne so poco, anzi niente). Ruby non so se è un’impressione solo mia ma ultimamente non è più così gettonato. Java e C++ se invece si fanno cose più impegnative; non entrambi, scegliere, secondo me. Si potrebbe aggiungere Rust, in futuro diventerà obbligatorio almeno considerarlo. Il C liscio solo se si si hanno le mani nel kernel, o si è come dire? di gusti molto particolari.

 Manca qualcuno? Ecco io direi che 

Marco ha già, in pochissimo tempo, ottenuto tante risposte, provo a scorrerle…

  • Manca il C#: Marco non lo pratica, io nemmeno.
    JS da assuefazione: It was my first language and I found it difficult moving to other languages yrs later.
  • Aggiunge Looking back on my early CS edu, it feels important that my whole first semester was only pseudo-code e We wrote on paper mostly e The pseudo-code languages had rules. But they were all abstract. We weren’t learning by blind trial and error, but by discussion and feedback on every solid attempt.
    Ancora: I also attribute a lot of these early experiences with helping me be comfortable learning new languages. (Not that it was “easy”).
  • JavaScript is the most accessible and most easily distributed language in history. The web is the greatest invention in the last several decades and you have to use JavaScript to program a large portion of web clients. Purtroppo è così! (opinione personale). That may change if web assembly takes off.
  • If you wanna always be able to get a job. You should learn JavaScript and web development.
    Both Python and Ruby are super easy to get into. Pretty accessible to learn. Have big communities and lots of resources. Popular and we’ll supported frameworks and toolchains. Pick either one. You’ll be fine.
    ava is the other language for web development. It has all of the benefits of community, support, marketability, etc. It is just an entirely different learning experience as a language. And you’ll tend to gravitate towards completely different kinds of problems.

E altro ancora. E altro arriverà.

Ma è ora di svelare chi mi ha passato la dritta: In risposta a @polotek.

Peter Seibel, quello di Practical Common Lisp, uno dei libri della mia top ten, anzi 5.

Di Peter un cinguettio da quotare interamente: My radical position is that the first item should include both Scheme and Common Lisp. If you think they’re the same, you’re doing it wrong. And if you think one is better than the other, you’re doing it extra wrong.

Marco continua con condizioni di mercato, molto sensate imho. E niente Lisp, ovviamente.

Altro ancora e… Language wars in 5… 4… 3… 2… 1…

OK, non so se sono solo io che se entro in queste discussioni non riesco più a uscirne. Ma adesso smetto, ecco. Ah! ‘ncora ‘na roba: sono con Peter (100%) anche se appena nomino il Lisp perdo punti; Racket poi è anche peggio 👽

Il valore della funzione in Bash

PETSCIIBOTS-1

Gli script Bash sono comodi, versatili, utilissimi ma a volte… Esempio (puramente didascalico, non realistico, nèh!): il prodotto di due numeri, in Python posso scriverlo così (file per.py):

#:/usr/bin/python3

def per(na, nb):
    res = na * nb
    return res

# test

r1 = per(6, 7)
print(r1)

r2 = per(3.7, 7.3)
print(r2)

Un pythonista produrrebbe un codice più sintetico ma sono sul didattico. Eseguendolo ottengo

$ python3 per.py
42
27.01
$

OK, funziona sia con gli interi che con i reali. In Bash c’è l’istruzione return ma non è quello che serve: ritorna l’exit code della funzione, un intero compreso in [0..256]. In effetti in Bash l’equivalente di return degli usuali linguaggi non manca, bisogna ricorrere a una qualche rimedio, ce n’è almeno uno facilmente usabile: usare una variabile, viene vista globalmente, il codice diventa (file per.sh):

#/bin/bash

function per () {
    res=$(echo "$1 * $2" | bc -l)
}

# test

per 6 7
echo $res

per 3.7 7.3
echo $res

Eseguo:

$ bash per.sh
42
27.01
$

Credo serva qualche chiarimento.

  • la definizione do funzione inizia con function seguita da nome, coppia di parentesi () senza gli argomenti e graffa aperta {. Nel nostro caso function per () {. È però possibile omettere la parola function, la riga diventa: per () {;
  • gli argomenti all’interno della funzione vengono visti come $1, $2, ...;
  • nel caso in esame siamo costretti a usare bc perché vogliamo trattare (anche) numeri reali; inoltre il carattere * verrebbe interpretato da echo per cui occorre racchiudere l’espressione tra virgolette. Ma posso usare varianti, p.es: res=$(echo $1 "*" $2 | bc -l), res=$(echo $1 \* $2 | bc -l);
  • al posto di $(…) posso usare la vecchia sintassi, sconsigliata da POSIX: res=`echo $1 \* $2 | bc -l`;
  • la funzione viene chiamata con il nome seguito dai parametri separati da spazio, p.es: per 6 7;
  • la variabile usata per ritornare il valore della funzione, in questo caso res, è visibile al ritorno dalla funzione.

È possibile avere funzioni che ritornano più valori, ecco magmin.sh:

#/bin/bash

magmin () {
    st=$1
    res1=${st^^}
    res2=${st,,}
}

# test

magmin "Stringa MAG e min 💥"
echo $res1
echo $res2

che produce

$ bash magmin.sh
STRINGA MAG E MIN 💥
stringa mag e min 💥
$

Nota: si può evitare l’uso della variabile di supporto st, l’espressione res1=${1^^} funziona ma, secondo me, è meno chiara.

Quindi, in conclusione: è possibile usare funzioni in Bash come negli altri linguaggi di script (Python, JavaScript, …). Ma è pratico? Con tutte le alternative liberamente disponibili è da valutare caso per caso.