di leonardo maffi
by leonardo maffi
Ultima revisione: 19 ottobre 2005
Last revision: Oct 19 2005
It seems clear that languages somewhat different from those in existence today
would enhance the preparation of structured programs. We will perhaps eventually
be writing only small modules which are identified by name as they are used
to build larger ones, so that devices like indentation, rather than delimiters,
might become feasible for expressing local structure in the source language.
-- Donald Knuth, "Structured Programming with go to Statements", Computing
Surveys, Vo. 6, No. 4, December 1974
In questa pagina mostro alcuni piccoli programmini in Python che ho costruito
di recente, che non meriterebbero una pagina tutta per loro, ma che ritengo
sufficientemente interessanti da venire comunque mostrati. (I listati colorati
li ho ottenuti con una versione modificata di PySourceColor di M.E.Farmer
Jr.).
Here I show some tiny Python programs that I've created
recently. Each one isn't so important to deserve a full page, so I collect them
all in this page. (To produce the colorized Html code I've used a modified version
of PySourceColor by M.E.Farmer Jr.).
def bottles(n):
if n == 0:
return "No bottles"
if n == 1:
return "1 bottle"
else:
return str(n) + " bottles"
s = "of beer on the wall"
i = 99
while i != 0:
print bottles(i), s
print bottles(i), "of beer"
if i>1:
print "Take one down, pass it around"
else:
print "Take it down, pass it around"
i = i - 1
print bottles(i), s
print
La famosa sequenza inventata/scoperta dal grande J. H. Conway
http://mathworld.wolfram.com/LookandSaySequence.html
Sequenza A005150 sull'archivio di Sloane: http://www.research.att.com/projects/OEIS?Anum=A005150
Si parte da 1, e un termine si ottiene dal precedente contando quante cifre
sono presenti. Per cui il secondo termine e' "un uno" (11), il terzo
è "due uno" (21), il quarto è "un due e un uno"
(1211), eccetera. Nella sequenza appaiono solo le cifre 1 2 e 3. Ecco i primi
termini della sequenza:
This is the famous sequence invented/found by J. H. Conway
http://mathworld.wolfram.com/LookandSaySequence.html
It's the A005150 sequence in the Sloane archive: http://www.research.att.com/projects/OEIS?Anum=A005150
We start with 1, and you can produce the a term from the precedent one conunting
how much digits there are. So the second term is "one one" (11), the
third is "two ones" (21), the fouth is "one two and one one",
etc. Only the digits 1, 2 and 3 appear in any term. Here are the first terms
of the sequence:
1, 11, 21, 1211, 111221,
312211, 13112221, 1113213211, 31131211131221, 13211311123113112211, 11131221133112132113212221,
3113112221232112111312211312113211, 1321132132111213122112311311222113111221131221,
11131221131211131231121113112221121321132132211331222113112211, 311311222113111231131112132112311321322112111312211312111322212311322113212221]
Queste funzioni calcolano i primi n termini di questa sequenza.
Queste funzioni sono ottimizzate per essere le più veloci (e non le più
corte). Questa è la mia più veloce una volta compilata con Psyco:
The following functions give the first n terms of this
sequence. The following functions are opmized for speed (and not for shortness).
This is my faster one if you can compile it with Psyco:
from itertools import groupby
def audioactive1(n): # faster compiled
strl = {("1","1","1"): "31", ("1","1"): "21", ("1",): "11",
("2","2","2"): "32", ("2","2"): "22", ("2",): "12",
("3","3","3"): "33", ("3","3"): "23", ("3",): "13" }
s = [1]
prec = str(s[-1])
for i in xrange(n-1):
r = []
for e,l in groupby(prec):
r.append( strl[tuple(l)] )
prec = "".join(r)
s.append( int(prec) )
return s
E questa è la più veloce se non dispone del compilatore Psyco:
def audioactive2(n): # faster uncompiled
strl = {("1","1","1"): "31", ("1","1"): "21", ("1",): "11",
("2","2","2"): "32", ("2","2"): "22", ("2",): "12",
("3","3","3"): "33", ("3","3"): "23", ("3",): "13" }
result = [1]
prec = "1"
for i in xrange(n-1):
prec = "".join( strl[tuple(l)] for e,l in groupby(prec) )
result.append( int(prec) )
return result
Ho definito delle funzioni nestList e split simili a quelle del
Mathematica (non riportate qui), con esse il codice per produrre i termini della
serie viene molto breve:
I've written the nestList and split functions similar
to the Mathematica ones (I don't show them here), with them the code to compute
the sequence terms becomes quite short:
from
util import
split,
nestList
print map(int,nestList(lambda
s:"".join([str(len(e))+e[0]for
e in
split(s)]),"1",9))
Questa è un'altra famosa sequenza:
Another famous sequence:
http://www.research.att.com/projects/OEIS?Anum=A010060
01101001100101101001011001101001..
Ci sono varie varianti di questa sequenza, comunque espressa nella forma binaria
data sopra, può essere definita ricorsivamente usando l'operatore di
nagezione sui bit. Il primo termine è 0. Quando i primi 2^n termini sono
stati specificati, possiamo definirli s, allora i successivi 2^n elementi possono
essere ottenuti negando s bit a bit. Adesso abbiamo definito i primi 2^(n+1)
elementi, e proseguiamo ricorsivamente.
The Thue-Morse sequence in the form given above, as a sequence of bits, can
be defined recursively using the operation of bitwise negation. So, the first
element is 0. Then once the first 2^n elements have been specified, forming
a string s, then the next 2^n elements must form the bitwise negation of s.
Now we have defined the first 2^(n+1) elements, and we recurse.
Eccola qui mentre cresce:
Here is it growing:
0
01
0110
01101001
0110100110010110
...
Espressa in base 10:
Expressed in base 10:
0, 1, 6, 105, 27030, 1771476585, 7608434000728254870, ...
Eccone una implementazione piuttosto breve e veloce, che usa la
.translate:
Here is a quite short and fast implementation that uses
.translate:
table = [" "] * 256
table[ord("0")] = "1"
table[ord("1")] = "0"
table = "".join(table)
s = "0"
for i in xrange(12):
s += s.translate(table)
print s
http://blogs.pragprog.com/cgi-bin/pragdave.cgi/Practices/Kata/KataEleven.rdoc
Questi Katà sono esercizi di forma, cioè sono esercizi nei quali
si può cercare di migliorare sempre più secondo vari metri di
giudizio. Questo richiede di ordinare alfabeticamente le lettere di una stringa,
convertite in minuscolo e pulite da spazi e punteggiatura. Il tutto deve essere
datto senza usare funzioni di ordinamento presenti nella biblioteca del linguaggio.
Queste sono la stringa di input e il risultato desiderato di esempio:
Such katas are exercises form, you can try to solve them
maximizing different aspects. Problem: sort the letters of a string, stripped
of punctuation and spaces, converted in lowercase, and without using the library
sort of the language. Here are the example input and the desired output strings:
"When not studying nuclear physics, Bambi likes to play beach volleyball."
aaaaabbbbcccdeeeeeghhhiiiiklllllllmnnnnooopprsssstttuuvwyyyy
Ecco alcune mie versioni:
Here are some of my versions:
s = "When not studying nuclear physics, Bambi likes to play beach volleyball."
# Questa e' la mia versione Python 2.4 piu' corta:
# This is my shortest Python 2.4 version:
print "".join(c for c in sorted(s.lower())if 123>ord(c)>96)
# Versione piu' lenta cyhe usa la biblioteca incorporata di ordinamento:
# Slower version without using the sorting library routine:
print "".join(c*s.lower().count(c)for c in map(chr,range(97,123)))
# Per una stringa enorme gia' in memoria questa puo' essere la versione piu' veloce:
# For a huge string already in memory this can be the faster version:
f = dict().fromkeys( map(chr,range(256)), 0)
for c in s: f[c] += 1
print "".join(chr(i)*(f[chr(i)]+f[chr(i-32)]) for i in range(97,123))
# Per un file piuttosto grande su disco questa puo' essere la versione piu' veloce:
# For a very big file on disk this can be the faster version:
import os, stat, array
filename = "text"
a = array.array('B')
f = open(filename, "rb")
a.fromfile(f, os.stat(filename)[stat.ST_SIZE])
f.close()
f = [0] * 256
for c in a: f[c] += 1
print "".join(chr(c)*(f[c]+f[c-32]) for c in range(97,123)
http://blogs.pragprog.com/cgi-bin/pragdave.cgi/Practices/Kata/KataSix.rdoc
Questo katà richiede di trovare tutti gli anagrammi dato un dizionario
di parole. Questa è una versione tra le più veloci:
This kata asks to find all the anagrams from a word dictionary.
This is among the faster versions:
words = open("wordlist.txt", "r").read().split()
dictwords = {}
for iw, w in enumerate(words):
wsignature = "".join(sorted(list(w.lower())))
if wsignature in dictwords:
dictwords[wsignature].append(iw)
else:
dictwords[wsignature] = [iw]
for anaglist in dictwords.values():
if len(anaglist) > 1:
print [words[i] for i in anaglist]
wo = open("wordlist.txt","r").read().split()
d = {}
for i, w in enumerate(wo):
s = "".join(sorted(list(w.lower())))
d[s] = d.get(s, []) + [i]
for l in d.values():
if len(l) > 1:
print [wo[i] for i in l]
Ecco un piccolo programma (ottimizzato per la piccola lunghezza) per trovare
gli anagrammi di una parola data, utilizzando un dizionario:
Here is a little program (optimized for shortness) to
find all the anagrams of a given word, using a dictionary:
anagram = raw_input("Find anagrams of word: ")
words = open('wordlist.txt').read().split()
sa = sorted(anagram.lower())
la = len(anagram)
result = [w for w in words if len(w)==la and sorted(w)==sa]
print "Anagrams:", ' '.join(result)
Versione corta:
Short version:
txt = open("text").read().lower()
for c in ".,;:-\\'\"": txt = txt.replace(c," ")
words = txt.split()
f = {}
for w in words:
f[w] = f.get(w,0) + 1
for w in sorted(f, key=f.get,reverse=True)[:30]:
print w, f[w]
Versione più veloce:
txt = open("text").read().lower()
ctable = [chr(i) for i in range(256)]
for c in ".,;:-\\'\"": ctable[ord(c)] = " "
ctable = "".join(ctable)
words = txt.translate(ctable).split()
f = {}
for w in words:
f[w] = f.get(w,0) + 1
for w in sorted(f, key=f.get,reverse=True)[:30]:
print w, f[w]
Risultato:
Output:
the 379
i 314
and 257
to 210
a 160
my 106
of 81
have 80
is 79
in 77
it 66
that 64
as 53
on 46
but 45
be 41
me 40
for 38
am 37
he 34
up 33
you 32
can 31
are 30
time 29
at 29
hir 29
with 28
we 27
was 27
Dato un testo, questo programma rimescola le lettere interne ad ogni sua parola,
lasciando in generale nella loro posizione solo la prima e l'ultima. La cosa
interessante è che il testo risultante è spesso leggibile:
Given a text, this program scrambles the letter of each
of its word, generally keeping only the first and last letter in their positions.
It's interesting to see that the resulting text is often readable still:
from random import shuffle
symb = set(".,.:;-*!?()\"[]^'\n")
def wscramble(w):
worig = w
if w[0] in symb:
w0 = w[0]
w = w[1:]
else:
w0 = ""
if w[-1] in symb:
wm1 = w[-1]
w = w[:-1]
else:
wm1 = ""
if len(w)>2:
sw = list(w[1:-1])
shuffle(sw)
return w0 + w[0] + "".join(sw) + w[-1] + wm1
else:
return worig
t = open("text").read()[:1000].split(" ")
print " ".join(wscramble(w) for w in t if len(w)>1)
Probabilmente il programma si potrebbe scrivere più corto. Ecco un risultato:
Probably this program can be made shorter. Here is an
output:
The nghit air seems to thicekn as we wlak out itno the woods naer the cmap. The slmels of nhigt are lkie candy to my sseens and the sudnos of wlidlfie in the dcatnise awkaen deep feneigls of bneliongg. lead us truhgoh the wodos, knwnoig the way to nice aera near slaml sraetm. Nkoo ssnees my ftiaimrialy with the area and fowolls csole bnehid. The pnie nldeees on the path and frnes that grow tilhcky uendr the teers seem to tcark our mvtnmoees. ca’nt hlep but feel taht we are mikang vrey niosy acvdnae onto the emney. My taniring tells me to be senlit as wlak but hvae to reimnd msylef that this is not an “paeoonirt”, just ncie nautre wlak. hop dwon an eennmbamkt and feel the soft garss uednr my pwas. Nkoo is rgiht behnid me. ltitle sraetm fwlos qteiuly out from the fnres and ploos beteewn smoe stomoh rocks. The water is caler and cool and the wlid mnit grwinog nareby adds mgcaail secnt to the gdlae. can snese that Noko is very happy wtih the lcotiaon and shi slwloy
I possibili sottoinsiemi di un insieme di n elementi sono 2^n, e questo suggerisce
un algoritmo iterativo ovvio per calcolarli tutti. ma esiste un algoritmo più
efficiente per calcolarli. Questa è la versione Python più corta
che lo implementa:
All possibile subsets of a set of n elemets are 2^n, and
this suggests an obvious algorithm to compute them all. But it exists a faster
algorithm to compute them. Here is my shortest Python implementation of it:
def subsets1(s):
r = [[]]
for e in s: r += [x+[e] for x in r]
return r
def subsets(sequence):
result = [[]] * (2**len(sequence))
for i,e in enumerate(sequence):
i2, el = 2**i, [e]
for j in xrange(i2):
result[j+i2] = result[j] + el
return result
def xsubsets(sequence):
for i in xrange(2**len(sequence)):
subset, position = [], 0
while i:
i, rest = divmod(i, 2)
if rest: subset.append(sequence[position])
position += 1
yield subset
La seconda versione è la più veloce, la terza versione è
un generatore, è molto lenta, e sfrutta l'algoritmo ovvio. Questo è
un risultato per cinque elementi:[[], [0], [1], [0, 1], [2], [0, 2], [1, 2], [0, 1, 2], [3], [0, 3], [1, 3], [0, 1, 3], [2, 3], [0, 2, 3], [1, 2, 3], [0, 1, 2, 3], [4], [0, 4], [1, 4], [0, 1, 4], [2, 4], [0, 2, 4], [1, 2, 4], [0, 1, 2, 4], [3, 4], [0, 3, 4], [1, 3, 4], [0, 1, 3, 4], [2, 3, 4], [0, 2, 3, 4], [1, 2, 3, 4], [0, 1, 2, 3, 4]]
Codice trovato online:
from win32com.client import Dispatch
xlApp = Dispatch("Excel.Application")
xlApp.Visible = 1
xlApp.Workbooks.Add()
xlApp.ActiveSheet.Cells(1,1).Value = 'Something'
xlApp.ActiveWorkbook.ActiveSheet.Cells(1,2).Value = 'Something else'
# xlApp.Close(SaveChanges=0)
xlApp.Quit()
del xlApp
>>> alist = [[1,2,3], [4,5], [6,7]] >>> sum(alist, []) [1, 2, 3, 4, 5, 6, 7]
>>> filter(str.islower, "aAKrUa") 'ara'
Si può vedere che filter applicato su una stringa restituisce una stringa
e non una lista, e che come funzione di filtraggio si possono passare anche
metodi stringa, dato che in genere i metodi possono venire usati anche così:
It can be seen that filter applied to a string gives a
string as output, and not a list; and that as filter function you can use a
string method too, because generally methods can be used like this too:
>>> "abba".islower() True
>>> str.islower("abba") True