Archivi Categorie: Python

Un quiz di Annarita

Header

Vi voglio raccontare di una mia disavventura programmatica multipla. Però non dovete ridere troppo di me, anche perché comunque c’è l’happy end finale.

Sto studiando mx, ahemmm Mathics e quando ho visto il post della prof. Annarita Ruberto: Trova Il Numero Misterioso mi son detto: “ecco l’occasione per testare la mia conoscenza di mx!“. Disastro, devo ancora impratichirmi, parecchio.

Ma, quando il gioco etc… perché non provare con Python? OK, disastro anche qui.
Perché non avevo capito il senso del testo. E cominciavo a disperarmi. Anzi panico proprio. Non sono più quello di una volta! OK, basta frasi fatte e commiserazioni. Ieri Annarita ha pubblicato (con un giorno di ritardo su quanto aveva minacciato) la soluzione, spiegando i passaggi in modo che capissi anch’io (sigh!): Trova Il Numero Misterioso: La Soluzione E Altre Curiosità.

Insomma questo è il problema:

1. la somma delle cifre di x è un numero y, che, moltiplicato per il numero ottenuto invertendo le sue cifre, dà come prodotto x.
2. Trovando i fattori primi del numero ottenuto invertendo le cifre di x, poi calcolando la semisomma dei quadrati di questi fattori primi, e, infine, rimuovendo la cifra 0 dal nuovo numero, si otterrà il numero x.

E questa la soluzione:

Il numero 1729 soddisfa la condizione 1 del puzzle perché:
la somma delle sue cifre è 19 e 19 ∙ 91 = 1729.
1729 soddisfa anche la condizione 2 perché:
9271 = 73 ∙ 127
e
(73^2 + 127^2) / 2 = 10729. Rimuovendo lo zero da questo numero, si ottiene proprio 1729.

Ah! ma allora, presto, anzi prima di subito, con Python e in futuro (forse) con mx Mathics!

numbers
Solo un paio di osservazioni prima del listato:

  • C’è bisogno di trovare i fattori di un numero, ho trovato una soluzione che mi piace sul solito Stack Overflow: Prime factorization – list. Non l’ho analizzata, funziona, forse in futuro… L’autore ha un blog interessante, su argomenti affini, avendo tempo…;
  • Visto che continuavo a ripetere sempre le stesse istruzioni ho deciso di trasformarle in funzioni, anche quando sono cortissime. Tutto in previsione della futura versione mx (davvero conto di farla, non subito, devo impratichirmi);
  • Queste funzioni sono lispiche, potrebbero essere chiamate in sequenza come usano i funzionali; non l’ho fatto per tenere il codice leggibile (e inserire una marea di print in fase di debug);
  • Inoltre mi sono convertito alla setta degli utilizzatori della list comprehension: è leggibile, comoda, si risparmia una riga. Peggio per quei linguaggi che non ce l’hanno;
  • Per adesso è un abbozzo ma queste funzioni le ho raggruppate in un file che forse evolverà in modulo come si deve.

OK, ecco il codice, addirittura commentato, cosa rara per me.

Il modulo con le funzioni utilizzate:

# -*- coding: utf-8 -*-

# jutils: qui dentro ci metto le funzioni che mi trovo
#         a ricopiare sovente; chissà se funziona

def strlist(st):
    ''' ritorna la lista dei caratteri della stringa st'''
    return list(st)

def ls2str(ls):
    ''' ritorna la stinga unione della lista ls'''
    st = ''
    return st.join(ls)

def lsstr2dig(ls):
    ''' ritorna le cifre della stringa ls'''
    return [int(c) for c in ls]

def strpopc(st, c):
    ''' se la stringa st contiene il carattere
        c questo viene eliminato; una volta sola
    '''
    p = st.find(c)
    if p >= 0:
        st = st[:p] + st[p+1:]
    return st

def factors(n):
    ''' copiato da qui:
        http://stackoverflow.com/questions/ +++
             16996217/prime-factorization-list
    '''
    gaps = [1, 2, 2, 4, 2, 4, 2, 4, 6, 2, 6]
    length, cycle = 11, 3
    f, fs, next = 2, [], 0
    while f * f <= n:
        while n % f == 0:
            fs.append(f)
            n /= f
        f += gaps[next]
        next += 1
        if next == length:
            next = cycle
    if n > 1: fs.append(n)
    return fs

Lo script:

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

from jutils import *

def passo_1(numero):
    ls = strlist(str(numero)) # lista di caratteri
    n = lsstr2dig(ls)         # lista di cifre
    somma = sum(n)            # somma delle cifre
    ls = strlist(str(somma)) # idem per la somma
    ls.reverse()
    rst = ls2str(ls)
    r = int(rst)              # e quindi in numero
    return (somma * r) == numero

def passo_2(numero):
    ls = strlist(str(numero)) # lista di caratteri
    ls.reverse()              # inverto
    rinv = ls2str(ls)         # trasformo la lista in stringa
    ninv = int(rinv)          # questo è il numero inverso
    fattori = factors(ninv)
    lquad = [i**2 for i in fattori] # lista dei quadrati dei fattori
    nquad = sum(lquad) / 2
    strquad = str(nquad)
    strquad = strpopc(strquad, '0')
    cont = int(strquad)
    return cont == numero

#main
trovato = False
numero = 999
numerofinale = 9999
while not trovato:
    numero += 1
    ok1 = passo_1(numero)
    if numero == numerofinale:
        print "Mi arrendo"
        exit()
    if ok1:
        trovato = passo_2(numero)
        if trovato:
            print "Trovato!", numero

arquiz

Ancora PI, poi basta per un po’

AthenaPeiraeusStampAllora … ultima puntata (per ora, ci sarebbe altro materiale ma non vorrei sembrare monomaniaco) sulle cifre di π, o PI, o pi, o –lui insomma.

Questa volta ricavato e memorizzato su file formattato usando sympy.
Non ci sono arrivato da solo anche perché c’è Stack Overflow. A volte mi chiedo come facevo una volta prima del Web! OK, <mode nostalgia OFF>. Trovato qui: 1000 digits of pi in python, in particolare la risposta di Garret Hyde. Che cita Python Adventures, lo stesso post di cui parlavo la volta scorsa (il mondo è piccolo, anzi no, è enorme ma se ti riesci a orientare :wink:)
Ecco il codice:

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

def riga_numeri(bl, n):
    f.write(bl + ' ')
    for c in range(n / 5):
        f.write('{:>5} '.format((c + 1) * 5))
    f.write('\n')

def num_col(n):
    f.write('{:>8} '.format(n))

from sympy.mpmath import mp

mp.dps = 1000001  # number of digits
PI = mp.pi
# print(PI)	  # print pi

lunriga = 100 #per TR

f = open("pag-sy", "w")
bl = " " * 8
riga_numeri(bl, lunriga)
n = 0
st = str(PI)

for c in st[2:]:
    if (n % lunriga) == 0:
        num_col(n)
    f.write(c)
    n += 1
    if (n % lunriga) == 0:
        f.write("\n")
    elif (n % 5) == 0:
        f.write(" ")
f.write("\n")
f.close()

pi1Msy

Undici minuti sul mio ‘puter non recentissimo, anzi… Al solito utilizza pienamente una delle CPU presenti:

cpu

E, ovviamente, produce lo stesso risultato di pi, componente della libreria CLN, utilizzato qui: Contare le cifre di PI.

diff

Ops! diff mi dice che c’è un newline di differenza, verificabile anche con ls:

ls

A qualcuno interessa un file di 1.3 MB, abbondante, questo:

dump

Dai con la prossima settimana si cambia argomento :roll:

Tanti decimali, il caso di PI

1986_stamp_83d40m_Athena_Recentemente Ok, panico ha partecipato al Carnevale della Matematica tenuto dall’ottimo GLF :-)
Uno scoop: il carnevale porta pochissime visite, appena visibili se si vanno a cercare ma devi metterci molta cura. Forse perché l’argomento era difficile, troppo teorico? Ma io ho partecipato anche con l’altro blog, quello più generalista, il Tamburo Riparato. Di là era per pubblicizzare Linux, risultati molto scarsi, un flop.
Ma l’argomento m’interessa, anzi lo script originale lo rivelo solo oggi.
E non finisce qui: ho altra roba già in bozza, mica posso buttarla :roll:

Ecco la versione naïf per calcolare PI con tante cifre decimali quanto volete. Usa sympy e non è ottimale, anzi: è lentissimissima:

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

import sys, sympy

ndec = int(sys.argv[1]) + 2
# evalf restituisce il numero di cifre,
#compreso il 3 e il punto  iniziale

pi_str = str(sympy.pi.evalf(ndec))
print pi_str

pisym

OK, le ultime cifre non sono attendibili, come si vedrà in seguito. Inoltre è lentissimo; vediamo cosa capita per un caso più sostanzioso, 1 milione di decimali:

pitime

No, non può andare 10 minuti e 32 secondi! Ma quando il gioco si fa :oops: già detto, cancello.

Un sito da prendere in considerazione in questi casi è Rosetta. Sì, c’è il nostro caso, qui.

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

def calcPi():
    q, r, t, k, n, l = 1, 0, 1, 1, 3, 3
    while True:
        if 4*q+r-t < n*t:
            yield n
            nr = 10*(r-n*t)
            n  = ((10*(3*q+r))//t)-10*n
            q  *= 10
            r  = nr
        else:
            nr = (2*q+r)*l
            nn = (q*(7*k)+2+(r*l))//(t*l)
            q  *= k
            t  *= l
            l  += 2
            k += 1
            n  = nn
            r  = nr

import sys

ndec = int(sys.argv[1]) + 1
pi_digits = calcPi()

i = 0
for d in pi_digits:
    sys.stdout.write(str(d))
    i += 1
    if i == ndec: print; sys.exit(0)

rosetta1

scrive il 3 iniziale, problema minore. Per quanto riguarda la velocità c’è sempre di mezzo il print, per velocizzare si potrebbe mettere in una stringa (problema aggiuntivoo: in Python le stringhe sono immutabili). In ogni caso ecco:

rotime

Niente, l’ho interrotto, dopo 200 minuti. Cosa succede se tolgo l’output commentando la riga 30? Continua a essere molto lento. Mistero, per adesso meglio la mia versione naïf :shock:
Però, che razza di codice! Chissà da dove viene? Ecco, entrano in gioco la Wiki e Google e prima di subito la soluzione:

OK, ma: 1) non è in Python e 2) Haskell? di nuovo?

Beh, Haskell forse in futuro, per Python c’è Laszlo Szathmary: Digits of PI (Part 2).

Non so se l’argomento interessa solo me; in ogni caso conto di parlarne ancora.
Ah! intanto ho scoperto che c’è un clone FOSS di Mathematica, scritto in Python; interessa? a me sì ovviamente.
Prossimamente… forse… :roll:

IPython

While it does not provide any computational or data analytic tools by itself, IPython is designed from the ground up to maximize your productivity in both interactive computing and software development. It encourages an execute-explore workflow instead of the typical edit-compile-run workflow of many other programming languages. It also provides very tight integration with the operating system’s shell and file system. Since much of data analysis coding involves exploration, trial and error, and iteration, IPython will, in almost all cases, help you get the job done faster.

IPy_header

Me ne avevano già parlato in tanti (sul Web) ma l’ho installato solo oggi. E pensare che… OK, ricomincio dall’inizio.

L’ho installato da terminale sudo apt-get install ipython, scarica ben 3MB sull’hard disk ed è pronto:

ip0

Da provare prima di subito:

ip1

OK. Il sito di IPython dice che la versione corrente (stabile) è la 1.2.1 – February 2014. Con Ubuntu viene installata la 0.13.2, anche se si usa sudo apt-get install ipython-notebook come consigliato dalla docmentazione ufficiale (pagina Installing IPython del sito, in realtà installa la versione per il browser) o da Software Center. Chissà se ad aprile, con Trusty Tahr (che sarebbe poi la Fidata Capra dell’Himalaya (pensa te che i francesi non sono ancora riusciti a cambiargli il nome)) si aggiornerà, vedremo.
Anche il manuale in versione PDF non è aggiornato e io ormai li leggo sul Kindle, scollegato.

OK, vediamo:

ip2

Comodo l’autocompletamento!

def f(x, y, z):
    return (x + y) / z

a = 5
b = 6
c = 7.0

result = f(a, b, c)

ip3

Non ci sono ancora arrivato del tutto ma se si ha uno script (t.py nel mio caso) si può valutare (si capisce valutare o è da lisperato perso) con %run. Con %who si ha l’elenco delle variabili, non so se sia possbile modificarle, tipo debug.

Però…
Sto barando. Finora ho copiato. Devo studiare. Io intanto mi leggo il manuale, l’ho appena caricato sul Kindle poi ne riparlo.
Prossimamente :shock: Forse :roll:

Python vs. Perl

Programming-republic-of-perlSono in ritardo su tutto; devo ancora finire due cose per domani ma devo proprio dire una cosa. Anche perché riguarda in qualche misura quello che devo fare per domani.
Perl, devo essere stato uno dei primi a affrontarlo, è nato nel 1987, controllato anche sulla Wiki, non è un errore, credevo prima. No, non ho prove, anche i libri non li ho più. OK, nostalgia mode OFF :shock:

Oggi ho trovato questo post: The Fall Of Perl, The Web’s Most Promising Language.
L’autore, Conor Myhrvold, è –come dire– eclettico. Ma conosce bene la materia il post è informato e esaustivo.

camelMa il motivo vero di queste note è un altro, anzi più di uno.

Python
Oggi le cose in cui Perl eccelleva si fanno con Python; anche le espressioni regolari. E Python ha un grosso vantaggio: la sintassi è molto più amichevole, sembra il Basic di una volta. E anche la storia dell’indentazione (non è che devo dire rientri, vero?) ho visto che è facile da digerire. E Python è molto usato, ci sono librerie (OK, non si chiamano così ma sono quello che una volta si sarebbero chiamate librerie). Metti in conto che non devi compilare e anzi puoi modificare al volo lo script e Python vince alla grande, ad esclusione dei progetti più impegnativi dove si usa Java o il C++. A proposito non so se è una cosa locale ma il C++ ha fama di essere ostico e impegnativo; io trovo tale anche Java, ma chissà :oops:

Octave (Matlab)
Conor dice che Python “even expanding at Matlab’s expense“. Però, per quel che ne so, Matlab è usato parecchio per calcoli di matrici e roba simile; e ha gli stessi vantaggi di scripting di Python. Resta da verificare se serva la versione proprietaria o sia sufficiente Octave, FOSS.

awk
Ci sono però dei casi in cui forse Python ha dei rivali. Per esempio gli script di awk di cui ho parlato nei post precedenti. Funzionano, anzi cresceranno in futuro. Anche se c’è il problema di Windows.

OK, a proposito per domani devo vedere se usare awk (o Perl) o Python. Scelta difficile io sono pythonista convinto ma vengo da sh, sed, awk e Perl.
E una delle cose di cui vado orgoglioso tra quelle recenti è fatta con Gnuplot :roll:

pass in Python

geek

Il post precedente su continue ha generato un commento che mi sembra il caso di sviluppare. Anche perché voglio enunciare esplicitamente una cosa del blog –dei miei post– che è sempre rimasta tra le cose non dette.

Anche se sono fuori dal mondo del lavoro ogni tanto vedo un vecchio (ma molto più giovane di me) amico con cui si parla anche di programmazione. Niente di eccezionale, anzi l’opposto: non si usa il C/C++, troppo complesso o almeno intimidatorio. Per le cose grosse si usa Java, che secondo me è incasinato come il C++ (o quasi). Io ho proposto Go che mi sembra abbordabile ma forse è troppo nuovo. Poi adesso si è iniziato a usare Octave (e forse Matlab) come fanno al Poli. Un linguaggio, anzi tutto un ambiente, il Visual Basic, usatissimo in passato mi sembra un po’ abbandonato, anche perché limitato a Windows. Per le cose normali adesso si usa Python, anche se ci sono cose che ancora non vanno come si vorrebbe. Ma è comodissimo per i piccoli script, mai più di un centinaio di righe. OK, a volte ci sono moduli importati, e poi il numero di righe non conta, vale la bontà dell’algoritmo.

La programmazione è in ogni caso vista come un mezzo, quello che interessa è trovare il risultato di una procedura, di una simulazione. Caso limite ma reale è quello di ottenere un fattore di sicurezza (pare si chiami davvero così) partendo da una massa di dati enorme ed eterogenea. Dove –importante– conta molto più l’operatore che non la macchina e il programma che c’è dentro.
Inoltre un programma (nel nostro caso si dovrebbe parlare di script) è sempre in beta, si modifica al volo quando si rende necessario. E spesso diventa ingestibile, ma questo sarebbe un altro discorso.
C’è un responsabile ma non è detto che sia l’autore dello script. E nessuno legge come dovrebbe i manuali. Anche perché non è necessario sapere tutto e poi manca il tempo.
Le soluzioni eleganti, classi e metodi razionali, come insegnano la scuola e i libri sono spesso sconosciute. Avete presente gli if kilometrici e le variabili globali? OK, se funziona va bene.

Poi, e vengo al dunque, Python ci mette del suo. Come fa notare Roberto nel commento al post citato si potrebbe usare pass:

pass is a null operation — when it is executed, nothing happens. It is useful as a placeholder when a statement is required syntactically, but no code needs to be executed.

Lo script di ieri diventa così:

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

n = 0
while n < 6:
	n += 1
	c = n % 3
	if c == 0:
		pass
	elif c == 1:
		print n, c, "*"
	else:
		print n, "**"
print "OK!"

pass

Risultato identico a quello dello script di ieri. In realtà l’unica differenza e la sostituzione di continue con pass.

Ci sono differenze tra le due istruzioni? Ho googlato, cercato in Stack Overflow ma non ho trovato niente; forse devo cercare meglio, o forse qualcuno che ne sa di più ci dirà.
Io personalmente ho sempre usato continue (e il suo fratello break) all’interno dei cicli e pass nella gestione delle eccezioni.
A proposito: con gli ing. di cui sopra mai parlare di errori, è una parolaccia :mrgreen:


Poi ogni tanto faccio anche dei post diversi, per esempio ne sto preparando uno che…
prossimamente :cool:

Aggiornamento

al3hex nel commento ha risolto il mistero. Che mistero non era ovviamente :roll:

continue in Python

Una storiella ma forse può essere utile per qualcuno. Questa è capitata per via della standardizzazione: “d’ora in poi tutto in Python (e Java e Octave e …)“. Inoltre a peggiorare le cose c’era il capo che continuava a girare nei paraggi, in questi casi sapete come ci si deve comportare, vero?

geek

Il codice era più complesso, questo è solo un esempio semplificato al massimo.

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

n = 0
while n < 6:
	n += 1
	c = n % 3
	if c == 0:
		print n, "###"
	elif c == 1:
		print n, c, "*"
	else:
		print n, "**"
print "OK!"

0

In Python non esiste l’istruzione switch o case o cond, solo l’if. Quindi l’esempio è corretto, e funziona.

Però salta fuori che bisogna fare una modifica: nel caso in cui c vale 0 non bisogna far nulla.
Il geek ha modificato il codice così (prudentemente ha commentato anziché cancellare l’istruzione divenuta inutile):

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

n = 0
while n < 6:
	n += 1
	c = n % 3
	if c == 0:
		#print n, "###"
	elif c == 1:
		print n, c, "*"
	else:
		print n, "**"
print "OK!"

1

Ops! non funziona, Python vuole qualcosa da fare, anche quando vogliamo non faccia nulla.

C’era il capo che girava e allora ecco la soluzione:

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

n = 0
while n < 6:
	n += 1
	c = n % 3
	if c == 0:
		#print n, "###"
		k = 1
	elif c == 1:
		print n, c, "*"
	else:
		print n, "**"
print "OK!"

2

Sì, inserire un’istruzione qualunque dove Python la vuole, funziona, il capo queste cose non le guarda, caso risolto.

Però, secondo me, non vale. C’è il manuale online, dai, prova a cercare “continue“, c’è in altri linguaggi e fa proprio quello che vogliamo:

continue may only occur syntactically nested in a for or while loop, but not nested in a function or class definition or finally clause within that loop. It continues with the next cycle of the nearest enclosing loop.

Verifica:

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

n = 0
while n < 6:
	n += 1
	c = n % 3
	if c == 0:
		continue
	elif c == 1:
		print n, c, "*"
	else:
		print n, "**"
print "OK!"

3

OK! Questa può essere intesa come la versione 2014 di “e leggere quel *bip* di manuale!:roll:

Il conto torna

Alla TV francese danno un programma TV che si intitola Des chiffres et des lettres in cui i concorrenti si sfidano a risolvere sia giochi di parole che di aritmetica. Uno dei giochini si intitola Le compte est bon: vengono dati alcuni numeri, e un numero obiettivo, e lo scopo è di ottenere l’obiettivo (o avvicinarcisi il più possibile) applicando le quattro operazioni aritmetiche alla serie di iniziale numeri.

Per capire di cosa si tratta, facciamo subito un piccolo esempio: dati i numeri 5, 100, 6, 3, 10 e 1, bisogna cercare di ottenere il numero 635 utilizzando soltanto le quattro operazioni +, -, * e /.

Una possibile sequenza che porta alla soluzione è:

5+100 = 105; 6*105 = 630; 3-1 = 2; 10/2 = 5; 630+5 = 635;

Come vedete, non è poi molto difficile, ma bisogna perderci un attimo di tempo a provare un po’ di combinazioni.

Viene fuori che la maestra di mio figlio, che frequenta la CM2 (quinta elementare) qui in Francia, propone questo tipo di esercizi per allenare i ragazzini a fare i conti. Divertente, no? Non l’avevo mai visto fare in Italia, ma può darsi che mi sbagli, se seguite questo blog e ne sapete di più sugli esercizi che vengono dati in quinta elementare in Italia, scrivetelo nei commenti! A questo sito trovare parecchi esercizi simili per tutte le classi delle elementari.

Fatto sta che qualche giorno fa, mio figlio si presenta con questa sequenza: 1, 2, 5, 10, 25, 50, e il numero da ottenere era 668. Non riusciva a trovare la soluzione, e voleva sapere se potevo trovarla io.

Dopo averci pensato qualche secondo, gli ho detto che secondo me non c’era soluzione. “Forse ho sbagliato a copiare qualche numero”, mi risponde lui, “ma mi sembrava di aver ricopiato attentamente dalla lavagna”.

Come sapere se c’è o no soluzione al problema? Beh, da buon ingegnere, ho pensato che il modo migliore era di scrivere un programmino che provasse tutte le combinazioni. Con 5 numeri non dovrebbero essere tantissime. Ovviamente, essendo un problema semplice, il modo veloce di risolverlo era usare il Python, il mio lignuaggio di fast-prototyping preferito ormai.

Volete provarci da soli? Allora non guardate il programma seguente.

import copy
import argparse

numeri = [5, 100, 6, 3, 10, 1]
soluzione = 635

def elimina(i, lista):
    l = []
    l = copy.copy(lista[:i])
    l = l + copy.copy(lista[(i+1):])
    return l


def operazione(lista, sol, ops, ris, operazione, a, b):
    l3 = copy.copy(lista)
    l3.append(ris)
    op = ops + str(a) + operazione + str(b) + ' = ' + str(ris) + '; '
    calcola(l3, sol, op)


def calcola(lista, sol, ops):
    if len(lista) == 1:
        if lista[0] == sol:
            print "TROVATO! ", ops
    else:
        n = lista[0]
        l = elimina(0, lista)
        for i, x in enumerate(l):
            l2 = elimina(i, l)

            n1 = n + x
            operazione(l2, sol, ops, n1, '+', n, x)

            n1 = n - x
            operazione(l2, sol, ops, n1, '-', n, x)

            n1 = x - n
            operazione(l2, sol, ops, n1, '-', x, n)

            n1 = n * x
            operazione(l2, sol, ops, n1, '*', n, x)

            if x != 0 and n % x == 0:
                n1 = n / x
                operazione(l2, sol, ops, n1, '/', n, x)

            if n != 0 and x % n == 0:
                n1 = x / n
                operazione(l2, sol, ops, n1, '/', x, n)


def main():
    parser = argparse.ArgumentParser(description='Inserisci i numeri')
    parser.add_argument('nums', nargs='+', default=numeri)
    parser.add_argument('--s', default=soluzione)

    args = parser.parse_args()
    num = [int(x) for x in args.nums]
    sol = int(args.s)

    print num, ' ??? ', sol
    calcola(num, sol, "")


if __name__ == "__main__":
    main()

E lanciando il programma si ottengono… ben 2 risultati!

[1, 2, 5, 10, 25, 50]  ???  668
TROVATO!  10-1 = 9; 2+5 = 7; 25+50 = 75; 9*75 = 675; 675-7 = 668; 
TROVATO!  1-25 = -24; 2+5 = 7; 10*50 = 500; -24*7 = -168; 500--168 = 668; 
TROVATO!  25-1 = 24; 2+5 = 7; 10*50 = 500; 24*7 = 168; 500+168 = 668; 

La seconda in realtà coinvolge dei numeri negativi, quindi andrebbe eliminata. E quindi aveva ragione mio figlio e torto io. Mai contare troppo sulle proprie doti matematiche!

Il programma l’ho scritto un po’ di fretta e non è il massimo dello stile, e non sono neanche completamente sicuro che sia corretto. E quindi, per chi volesse esercitarsi, ho tre domande a diverso grado di difficoltà:

  • Sapete modificare il programma per evitare che stampi soluzioni negative?
  • E se voleste memorizzarle tutte le soluzioni in un vettore per riutilizzarle dopo?
  • Infine, sapete dimostrarne la correttezza?

Dimenticavo: ovviamente, un programma analogo è disponibile in linea qui.

Ah, questi francesi!

Modificare il testo – III

re3Non so voi ma io resto del parere che le RE sono una brutta bestia. Da trattare con le dovute precauzioni, come detto nel post precedente.
A proposito ho provato a rifare lo script pdx.py usando replace(), metodo delle stringhe, ma dopo un po’ ho deciso che non è una via percorribile, in particolare per sostituire la virgola all’interno dei numeri reali scritti in italiano. Mi sono arreso (forse troppo presto, magari ci riproverò) anche a rifare lo script in newLISP, il mio linguaggio di scripting preferito. Ma non ho capito cosa non va nella RE (\d)[,](\d) eppure sembra tanto semplice. Mistero (almeno per ora).

Lo script pdx.py l’ho testato e sembra tutto OK. Cosa succede se il file di dati fosse grande (decisamente più grande di quelli per cui è stato scritto)? Di seguito procedo a un test con un file finto, testuale anziché numerico.
Il file dei dati contiene i primi due capitoli di Pinocchio, in formato testuale, scaricato da Liber Liber.

p0

Lo script viene modificato per il test in questo modo:

  • cerca le v e w e le sostituisce con *, in luogo di punto-virgola e tab;
  • cerca le u all’interno di una parola e le sostituisce con #, in luogo della virgola all’interno del numero.
#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys, os, re

if len(sys.argv) < 2:
	print "uso: ", sys.argv[0], "file-dei-dati"
	sys.exit(2)
if not os.path.isfile(sys.argv[1]):
	print "manca il file ", sys.argv[1]
	sys.exit(2)

txt = open(sys.argv[1], 'r').read()

txt = re.sub(r'(\S)[u](\S)', r'\1*\2', re.sub('[vw]', '#', txt))
print txt

p1

Verifico, per la coppia v e w si ha:

p2

mentre per la u ottengo:

p3

Chiaramente il conteggio a righe non è precisissimo, ma c’è meld:

click per ingrandire

click per ingrandire

Sì sembra OK.

Consumo di risorse:

p5

decisamente trascurabile, quasi invisibile.

p6

e istantaneo.

OK, fatto :roll:

Modificare il testo – II

regexOK, mi è stato fatto notare che lo script precedente, quello che sostituisce il punto-virgola e il tab con uno spazio manca ancora di una funzione indispensabile per le vecchie versioni di M$ Office: sostituire la virgola con il punto nei numeri reali o float (o come li chiamate voi, quelli non interi, insomma).

loPerò, forse c’è un modo semplice e veloce: io uso Libre Office e quando salvo come CSV mi chiede come:

csv0

e

csv1

Comunque sì, si può modificare lo script. Notare che la virgola dev’essere sostituita solo all’interno di un espressione numerica, anche se questi file di dati in genere le hanno solo con quella funzione. Ho costruito e utilizzo un nuovo file di dati (finti): d2.xls:

d2

La riga 4 si riferisce a date, usando non so bene che formato che non bisogna usare.
Ed ecco lo script aggiornato (pdx.py):

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

import sys, os, re

if len(sys.argv) < 2:
	print "uso: ", sys.argv[0], "file-dei-dati"
	sys.exit(2)
if not os.path.isfile(sys.argv[1]):
	print "manca il file ", sys.argv[1]
	sys.exit(2)

txt = open(sys.argv[1], 'r').read()

txt = re.sub(r'(\d)[,](\d)', r'\1.\2', re.sub('[;\t]', ' ', txt))
print txt

pdx

Altra cosa dimenticata: esiste un’ampia letteratura sulle RE, io ho usato principalmente Regular Expression HOWTO, c’è quasi tutto quello che serve. Inoltre mi trovo sempre più spesso a vagare dalle parti di Stack Overflow, a mia insaputa :roll:

In questo periodo mi sto occupando di altre cose (non trascuro il blog anche se qualcuno potrebbe avere quell’impressione) ma prossimamente voglio provare una soluzione alternativa, usando replace() e non le RE. E confrontarle in termini di efficienza.
Prossimamente, forse… :mrgreen:

Iscriviti

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

Unisciti agli altri 63 follower