Robitex's Blog

Ideas in the web

Archivi Mensili: dicembre 2011

Numeri in lettere con Python


Scarica l’articolo nel formato PDF per la stampa

Sommario

L’articolo presenta il codice nel linguaggio Python per convertire un numero in parole nel comune formato ‘finanziario’.

Il codice

Avevamo già risolto questo problema prima con Java e poi con Lua. Lua è un linguaggio di scripting assai simile a Python e non è stato difficile convertire il listato. Ho dapprima effettuato su una copia del listato in Lua, una serie di sostituzioni: then con :, function con def per esempio. Come secondo passo ho poi sistemato con precisione l’identazione del codice poiché in Python è il mezzo per individuare i blocchi di istruzioni.

Terminate queste due operazioni, mi sono ritrovato con un codice Python quasi del tutto completo. Mancava trovare il sostituto per alcune funzioni di libreria Lua, risolte con lo slicing e con alcune funzioni di base matematiche. Infine ho migliorato la funzione di wrapper della funzione ricorsiva in una versione più completa e razionale rispetto all’originale Lua. Ecco il risultato:

#!/usr/bin/python

# script di traduzione di numeri in lettere
# nella lingua italiana
# traduce un numero in lettere

# funzione ricorsiva
def NumberToTextInteger(n):
    if n == 0: 
        return ""
        
    elif n <= 19:
        return ("uno", "due", "tre", "quattro", "cinque", 
                "sei", "sette", "otto", "nove", "dieci", 
                "undici", "dodici", "tredici", 
                "quattordici", "quindici", "sedici", 
                "diciassette", "diciotto", "diciannove")[n-1]
                
    elif n <= 99:
        decine = ("venti", "trenta", "quaranta",
                  "cinquanta", "sessanta", 
                  "settanta", "ottanta", "novanta")
        letter = decine[int(n/10)-2]
        t = n%10
        if t == 1 or t == 8:
            letter = letter[:-1]
        return letter + NumberToTextInteger(n%10)
        
    elif n <= 199:
        return "cento" + NumberToTextInteger(n%100)
        
    elif n <= 999:
        m = n%100
        m = int(m/10)
        letter = "cent"
        if m != 8:
            letter = letter + "o"
        return NumberToTextInteger( int(n/100)) + \
               letter + \
               NumberToTextInteger(n%100)
        
    elif n<= 1999 :
        return "mille" + NumberToTextInteger(n%1000)
    
    elif n<= 999999:
        return NumberToTextInteger(int(n/1000)) + \
               "mila" + \
               NumberToTextInteger(n%1000)
        
    elif n <= 1999999:
        return "unmilione" + NumberToTextInteger(n%1000000)
        
    elif n <= 999999999:
        return NumberToTextInteger(int(n/1000000))+ \
               "milioni" + \
               NumberToTextInteger(n%1000000)
    elif n <= 1999999999:
        return "unmiliardo" + NumberToTextInteger(n%1000000000)
        
    else:
        return NumberToTextInteger(int(n/1000000000)) + \
               "miliardi" + \
               NumberToTextInteger(n%1000000000)

# funzione wrapper
def NumberToText(x, pos=2):
   """ Ritorna un numero tradotto in lettere
       secondo un formato 'finanziario'
   """
   sign = ""
   if x<0:
      sign = "meno"
      x = abs(x)
   x = round(x, pos)
   n = int(x)
   frmt = "{0:0."+"{0:d}".format(pos)+"f}"
   spic = frmt.format(x-n)[2:]
   if n == 0:
      num = "zero"
   else:
      num = NumberToTextInteger(n)
   return sign+num+"/"+spic

# prelevo il valore numerico
value = input("Valore: ")

print("il numero {0} in lettere =".format(value))
print("{0}".format(NumberToText(value)))

Test (note tecniche)

Per testare il codice occorre aver installato un interprete Python. Avviate una finestra di console e scrivete il nome che avete dato al file contenente il codice (confrontate l’immagine sottostante presa da un sistema Ubuntu 10.04).

Esecuzione del programma nel terminale di Linux Ubuntu

Esecuzione del programma nel terminale di Linux Ubuntu

Auguri

Un Augurio di Buon Natale e per uno splendido nuovo anno 2012!

Il computo metrico con Lua


Scarica l’articolo in formato PDF per la stampa

Sommario

Certi tipi di opere edili possono essere così articolate da mettere in difficoltà l’utente che intendesse gestirle con un tradizionale software di computazione.
In questo post si svilupperà un’idea che può risolvere il problema di efficienza, secondo la quale i dati sono descritti da un opportuno linguaggio così da esprimere in modo diretto e concettuale le particolarità dell’opera edilizia.

Computi metrici

Un computo metrico di un opera consiste in una serie di voci che definiscono i singoli componenti edilizi quantificate secondo una specifica unità di misura. Inserire le misure è una di quelle attività leggermente noiose e poco attraenti…

Con un linguaggio specifico è possibile rendere efficiente l’inserimento manuale di queste quantità, in particolare se si desidera che i dati siano raggruppati in livelli strutturati.

Il linguaggio dei solai

Se l’opera in progetto è composta da diversi edifici a più piani, dovremo calcolare le superfici dei solai raggruppando i dati secondo la doppia classificazione piano/edificio. Saremo così in grado di fornire le superfici di solaio per ciascun piano, quelle totali per singolo edificio e quelle complessive per piano.

Un linguaggio semplice che descrive questa struttura informativa potrebbe essere questo:

solaio{
   edificio = "A",
   piano = 1,
   superficie = 4.50*3.25,
}

La sintassi che riscontriamo leggendo il frammento di codice, si basa su un costrutto chiave = valore ciascuno separato con una virgola e racchiuso in un coppia di parentesi graffe.

I valori testo sono racchiusi tra doppi apici mentre i valori numerici utilizzano il punto e non la virgola per separare la parte decimale.
Vi anticipo che il codice precedente corrisponde alla sintassi di Lua, un linguaggio di programmazione libero e multi-piattaforma, più volte trattato sul blog.

Continuando a tagliare su misura il codice per renderlo perfettamente adatto ad esprimere le quantità che vorremmo computare, osserviamo che non ci occorre identificare i singoli solai di uno stesso piano di uno stesso edificio. Alla chiave superficie assegniamo allora non un singolo valore o espressione, ma un elenco di espressioni corrispondenti alla geometria dell’impalcato, per esempio (possiamo commentare il codice inserendo due trattini):

-- cambiamo nome all'oggetto 🙂
solaiDiPiano{
   edificio   = "A",
   piano      = 1,
   superficie = {
      2*4.50*3.25,           -- qui due solai uguali
      3.80*3.25,
      4.64*4.20 - 0.40*0.70, -- qui una detrazione di una botola!
      4.24*(4.20 + 3.50),
      5.50*3.60,             -- la virgola finale è opzionale
      },                     -- ma rende più comode successive
}                            -- aggiunte di valori

Osservazioni operative

L’ambiente in cui si memorizza il codice è un editor di testo ASCII. La comodità sta nel fatto che i dati sono immediatamente disponibili su qualsiasi sistema operativo per eventuali modifiche o verifiche, e possiamo eseguire le operazioni con poca fatica.

Nel frattempo ho aggiunto altri dati come potete vedere dal prossimo frammento di codice:

solaiDiPiano{edificio="A", piano=1,
   superficie = {
      2*(1.60+3.60+2.07)*(4.345+4.245),
      1.92*2.64,
      3.60*(0.55+1.05)*2,
   }
}

solaiDiPiano{edificio="A", piano=2,
   superficie = {
      2*(1.60+3.60+2.07)*(4.345+4.245),
      1.92*2.64,
      3.60*(0.55+1.05)*2,
   }
}

solaiDiPiano{edificio="A", piano=3,
   superficie = {
      2*(1.65+3.60+2.12)*(4.42+4.32),
      1.92*2.64,
      3.60*(0.55+1.05)*2,
   }
}

solaiDiPiano{edificio="B", piano=1,
   superficie = {
      2*(2.54+2.54+2.56+2.545)*(4.345+4.245),
      2*1.00*2.64
   }
}

solaiDiPiano{edificio="B", piano=2,
   superficie = {
      2*(2.54+2.54+2.56+2.545)*(4.345+4.245),
      2*1.00*2.64
   }
}

solaiDiPiano{edificio="C", piano=1,
   superficie = {
      2*(1.60+3.60+2.07)*(4.345+4.245),
      1.92*2.64,
      3.60*(0.55+1.05)*2,
   }
}

solaiDiPiano{edificio="C", piano=2,
   superficie = {
      2*(1.60+3.60+2.07)*(4.345+4.245),
      1.92*2.64,
      3.60*(0.55+1.05)*2,
   }
}

solaiDiPiano{edificio="C", piano=3,
   superficie = {
      2*(1.65+3.60+2.12)*(4.42+4.32),
      1.92*2.64,
      3.60*(0.55+1.05)*2,
   }
}

Elaborare i dati

Ma come si elaborano i dati?
Il codice scritto corrisponde esattamente ad una serie di chiamate alla funzione Lua solaiDiPiano() a cui passiamo una tabella.
Eseguendo il codice, verranno calcolate le espressioni delle superfici e le istruzioni contenute nella funzione.

Elaborazione diretta

Proviamo a scrivere la funzione in modo da ottenere la superficie totale dei solai:

-- superficie totale
local totsup = 0

-- definizione della funzione
function solaiDiPiano(s)
   -- campi in s:
   -- s.superficie -> array di valori

   -- sommo tutti i valori in s.superficie
   -- e li inserisco nel totale
   for _, val in ipairs(s.superficie) do
      totsup = totsup + val
   end
end

-- carico il file dati
dofile("solai.lua")

-- stampo il risultato
print(string.format("Superficie totale : %0.3f",totsup))

Una volta installato sul sistema Lua, inseriamo i dati dei solai così come si è deciso di descriverli nel file testo “solai.lua”. Salviamo in un secondo file il codice che li elabora “calcsuptot.lua”, ed infine apriamo il terminale o la console ed eseguiamo il comando:

> lua calcsuptot.lua

Fatto! La schermata rappresentativa dell’operazione in Windows è la seguente, dove si può notare l’uso del comando cd per spostarsi nella directory dove avevo salvato i due file.

Esecuzione del report per la superficie totale in console

Esecuzione del report per la superficie totale in console

Elaborazione strutturata

Diamo una definizione strutturata della funzione solaiDiPiano() in modo da costruire una tabella dati chiamata Solai che potremo leggere in modi diversi. Questa è la via sicuramente più flessibile e potente.

-- serve un contenitore per i dati:
local Solai = {}

-- creazione della tabella dati
function solaiDiPiano(s)
   -- ricordiamo i nomi dei campi in s
   -- s.edificio   := nome edificio
   -- s.piano      := numero di piano
   -- s.superficie := array di valori

   -- se non esiste ancora creiamo
   -- una tabella per edificio
   -- rispetto al nome
   -- es.: Solai.A
   if not Solai[s.edificio] then
     Solai[s.edificio] = {}
   end

   -- prendiamoci un riferimento più comodo
   -- alla singola tabella edificio
   local edificio = Solai[s.edificio]

   -- calcoliamo la somma delle superfici
   local suppiano = 0
   for _, val in ipairs(s.superficie) do
      suppiano = suppiano + val
   end

   -- intenderemo la tabella edificio
   -- come un array dove l'indice sarà
   -- il numero di piano ed il valore
   -- corrispondente, la superficie
   if edificio[s.piano] then
      edificio[s.piano] = edificio[s.piano] + suppiano
   else
     edificio[s.piano] = suppiano
   end
end

Fatto questo, potremo elaborare decine od anche migliaia di elementi si solaio. Tutti i dati saranno memorizzati nella tabella Solaio Solaio.

A questo punto scriviamo la funzione di reporting che opererà in lettura. Vogliamo stampare per esempio i totali di piano per edificio:

-- report piano/edificio
function reportPianoEdificio()
   -- Solai contiene i dati in modo strutturato
   -- Al primo livello ci sono le tabelle edificio

   local totsup = 0
   for nomeEd, arraysup in pairs(Solai) do
      print(string.format("Edificio %s: --------------------", nomeEd))

      -- stampiamo le superfici piano per piano
      local totsupEd = 0
      for level, sup in pairs(arraysup) do
         totsupEd = totsupEd + sup
         print(string.format("Piano %2d, superficie %10.3f", level, sup))
      end

      totsup = totsup + totsupEd
      print("--------------------------------")
      print(string.format("Tot. sup. edificio %s: %10.3f", nomeEd, totsupEd))
      print("--------------------------------")
   end
   print("--------------------------------")
   print(string.format("Tot. superfici %10.3f", totsup))
   print("--------------------------------")
end

In console ottengo questo:

Esecuzione del report in console

Esecuzione del report Piano/Edificio in console

Se volessimo stampare le superfici piano per piano dovremo costruire una funzione in lettura di questo tipo:

-- report per piani
function reportPiani()
   -- una tabella per le sup di piano
   local suppiani = {}

   -- ciclo per singolo edificio
   -- primo livello della struttura dati Solaio
   for nomeEd, arraySup in pairs(Solai) do
      -- ciclo interno di lettura delle sup
      for piano, sup in pairs(arraySup) do
         if suppiani[piano] then
            suppiani[piano] = suppiani[piano] + sup
         else
            suppiani[piano] = sup
         end
      end
   end

   -- stampa
   local ts = 0
   for level, sup in pairs(suppiani) do
      ts = ts + sup
      print(string.format("Livello %2d: sup = %10.3f",level,sup))
   end
   print("--------------------------")
   print(string.format("Totale sup = %10.3f",ts))
end

--[[
-- l'output con i soliti dati è questo
Livello  1: sup =    463.233
Livello  2: sup =    463.233
Livello  3: sup =    290.833
--------------------------
Totale sup =   1217.299
--]]

Conclusioni

L’idea proposta è originale? No! Volendo, si possono fare molti esempi tratti dal mondo dell’informatica dove un linguaggio testuale descrive dati strutturati. Con Lua è semplice creare il proprio linguaggio ed elaborare i dati, pensiamo per esempio al caso più complesso riguardante la costruzione dei libretti dei ferri…

Naturalmente, potremo preparare report molto più professionali che non un semplice output in console, per esempio compilando un opportuno sorgente LaTeX per l’uscita in PDF, direttamente con le funzioni Lua di reporting.

Uno svantaggio consiste nella necessità di scrivere qualche linea di codice Lua, ma del resto questo consente la massima libertà di cucire la soluzione sul problema e non è poi così terribilmente difficile. A voi dunque l’onore e l’onere di rendere un po’ più attraente la stesura dei computi metrici!

Note tecniche

Il software occorrente per utilizzare la procedura è il seguente: un editor di testi, consiglio SciTE perché in grado di far eseguire il codice premendo semplicemente il tasto F5, e l’interprete Lua, quindi tutto software libero, gratuito e legalmente installabile anche in ambito professionale, infine un pizzico di fantasia.
Per imparare Lua non vi è niente di meglio del PiL.
Un saluto.

%d blogger hanno fatto clic su Mi Piace per questo: