„ Програмиране с Python“, ФМИ
Стефан Кънев & Николай Бачийски
02.04.2007г.
Какво ще правим?
- Ще си припомним условието
- Ще ви предложим примерно решение
- Ще видим дали може да се направи по-хубаво
Условие—начало
Основната ви цел е да напишете функция със следните име и аргументи: module_game(m1, m2)
.
Входни данни: m1
и m2
са модули.
Резултат: n-торка с 2 елемента, с броя на точките събрали първия и втория модул при играта, която е описана по-долу в условието.
Предварително известни факти:
- всички речници в модулите са или празни или само от цели числа. Ключовете им са винаги низове. Примери:
{} {'baba': 5} {'a': 42, 'b': -69, 'x': 4360000634}
- всички списъци в модулите са или празни или са списъци от n-торки от числа. n-торките също могат да бъдат празни. Примери:
[] [()] [(1023, 4095, 8191), (), (), (0, 17), (3,)]
- всички функции в модулите приемат два аргумента цели числа и връщат цяло число
- може да разчитате, че тези условия ще бъдат изпълнени и няма нужда да ги проверявате
Правила на играта:
За атрибути по-надолу ще смятаме само тези реални атрибути на модулите, които не започват с _
(долна черта, подчертавка)
Условие—правило 0
Всеки модул получава 1 точка за всяка функция в него, чието име започва с последните 3 символа на друга функция в същия модул. Ето пример за част от модул, която ни гарантира две точки по този параграф:
s = "baba"
def destroy(x, y):
print "Destroyed."
return 0
def roy_keen(x, y):
print "Babyboy."
return 0
def roy_boss(x, y):
print "Holy-shmolly, that is tea!"
return 0
Условие—правило 1
Всеки модул получава 1 точка, за всеки списък в него, за който средното-аритметично на сумите от квадратите на n-орките в него е по-голямо от средно-аритметичното на всички цели числа в другия модул. Ако някоя n-торка е празна, нейната сума може да се смята за 0. Ако един списък е празен, неговото аритметично също може да се смята за 0.
Пример: имаме списъка [(1, 2), (), (8, -2), (-1, 5, -1)]
. Сумите на квадратите на n-торките са съответно: 5, 0, 68, 27. Средното им аритметично е точно 25.
Условие—правило 2
По 1 точка получава модул, за всяка функция в него, която удовлетворява следните условия:
- извикана с аргументи 0 и 0 връща 0
- комутативна e за всички двойки числа от 0 до 99 включително
Пример за такава функция: def f(x, y): return x*y
Условие—правило 2
3 точки носи на модул всеки речник, за който:
- нека сортираме стойностите му по брой на цифрите в низходящ ред
- ако две стойности имат равен брой цифри, приемаме, че те са ъгли в градуси и сравняваме техните тангенси (отново низходящо)
- взимаме ключовете на първите 6 елемента. Ако елементите на речника са по-малко от 6, спираме дотук
- ако тези 6 ключа са ‘Chapman’, ‘Cleese’, ‘Gilliam’, ‘Idle’, ‘Jones’, ‘Palin’ независимо в какъв ред или с малки или големи букви, модулът получва трите точки
Пример за такъв речник: {'Chapman': 11, 'Charlie': 99, 'GILLIAM': 102, 'Idle': 666, 'Jones': 883, 'Palin': 55, 'Cleese': 1101}
Код—начало
def module_game(m1, m2):
return (assess(m1, m2), assess(m2, m1))
def assess(mod, mod_other):
return sum((
end3_funcs(mod),
avg_lists(mod, mod_other),
commutatives(mod),
3*monty_python_dicts(mod),
))
def valid_attributes(mod):s
return (attr for attr in dir(mod) if not attr.startswith('_'))
sum
приема последователност и му даваме n-торка:sum((
valid_attributes
няма нужда да връща списък, може да ползваме generator expression
Код—правило 0
def end3_funcs(mod):
func_names = [attr for attr in valid_attributes(mod) if callable(getattr(mod, attr))]
count = 0
for func in func_names:
if len(func) < 3: continue
start = func[:3]
if any((otherfunc.endswith(start) for otherfunc in func_names if func != otherfunc)):
count += 1
return count
- Защо
func_names
е списък (или „за влагането на генератори“)? - Какво е
any
?
Влагане на генератори
g = (x*x for x in xrange(4))
for a in g:
for b in g:
print (a, b)
(0, 1)
(0, 4)
(0, 9)
- Ред на извикване:
g.next()
във външния цикъл,a
, става 0g.next()
във вътрешния циъкл,b
, става 1g.next()
във вътрешния циъкл,b
, става 4g.next()
във вътрешния циъкл,b
, става 9
- генераторът не се копира
- … и за съжаление не може лесно да се копира
- генераторът може да се ползва само веднъж
Ами тогава?
- Списъци:
g = [x for x in xrange(4)]
- Създаване на 2:
g = (x*x for x in xrange(4))
g_inner = (x*x for x in xrange(4)) - Създаване направо вътре:
for a in (x*x for x in xrange(4)):
for b in (x*x for x in xrange(4)):
print (a, b) - Функция генератор:
def gen_sq(n):
for x in xrange(n):
yield x*x - Още една функция:
def gen_sq2(n):
return (x*x for x in xrange(n))
Код—правило 1
def avg_lists(mod, other):
other_ints = [getattr(other, attr) for attr in valid_attributes(other)
if isinstance(getattr(other, attr), (int, long))]
other_sum, other_len = sum(other_ints), len(other_ints)
mod_lists = (getattr(mod, attr) for attr in valid_attributes(mod)
if isinstance(getattr(mod, attr), list))
count = 0
square = lambda x: x*x
for l in mod_lists:
jingled_items = [sum(imap(square, tup)) for tup in l]
jingled_sum, jingled_len = sum(jingled_items), len(jingled_items)
# don't use floats
# we know that always both the sum and the len are non-negative
# except that other_sum could be negative
if jingled_sum > 0 and 0 == other_len:
count += 1
elif jingled_len == 0 and other_sum < 0:
count += 1
elif jingled_sum*other_len > jingled_len*other_sum:
count += 1
return count
Код—правило 2
def commutatives(mod):
# този път ф-ии--не имена
funcs = [getattr(mod, attr) for attr in valid_attributes(mod)
if callable(getattr(mod, attr))]
count = 0
for func in funcs:
if func(0,0) == 0 and\
all((func(i, j) == func(j, i)
for i in xrange(0, 99) for j in xrange(i+1, 100))):
count += 1
return count
Код—правило 2
def commutatives(mod):
# този път ф-ии--не имена
funcs = [getattr(mod, attr) for attr in valid_attributes(mod)
if callable(getattr(mod, attr))]
count = 0
for func in funcs:
point_commutative = ( func(i, j) == func(j, i)
for i in xrange(0, 99) for j in xrange(i+1, 100))
if func(0,0) == 0 and\
all(point_commutative):
count += 1
return count
any и all
any(iterable)
— връща истина, ако поне един от елементите наiterable
е истина.all(iterable)
— връща истина, ако всички елементи наiterable
са истина.
>>>people = ('God', 'Monty', 'Pinkie', 'Mityo')
>>>wannaplay = lambda name: 'x' in name
>>>friends = lambda x,y: x[0] == y[0] and x != y
>>>capitalised = lambda x: x[0].isupper()
>>>print any(map(wannaplay, people))
False # Nobody wants to play
>>>print any([friends(name, 'Mityo') for name in people])
True # Monty
>>>print all(imap(capitalised, people))
True
>>>print all((friends(name, 'Mityo') for name in people))
False
Код—правило 3
def monty_python_dicts(mod):
WANTED = set(['chapman', 'cleese', 'gilliam', 'idle', 'jones', 'palin'])
dicts = [getattr(mod, attr) for attr in valid_attributes(mod) if isinstance(getattr(mod, attr), dict)]
count = 0
def make_strange_cmp(dict_):
def strange_cmp(x, y):
x, y = dict_[x], dict_[y]
sx, sy = str(sx).lstrip('-'), str(sx).lstrip('-')
if (len(sx) != len(sy)):
return cmp(len(sx), len(sy))
return cmp(math.tan(math.radians(x)), math.tan(math.radians(y)))
for d in dicts:
ordered = sorted(d, cmp=make_strange_cmp(d), reverse=True)
if set(imap(string.lower, ordered[:6])) == WANTED:
count += 1
return count
Код—правило 3
def monty_python_dicts(mod):
WANTED = set(['chapman', 'cleese', 'gilliam', 'idle', 'jones', 'palin'])
dicts = [getattr(mod, attr) for attr in valid_attributes(mod)
if isinstance(getattr(mod, attr), dict)]
count = 0
# DSU, DSU
def make_decorate(dict_):
def decorate(key):
value = dict_[key]
svalue = str(value).lstrip('-')
return (len(svalue), math.tan(math.radians(value)))
return decorate
for d in dicts:
ordered = sorted(d, key=make_decorate(d), reverse=True)
if set(imap(string.lower, ordered[:6])) == WANTED:
count += 1
return count
DSU
- DSU = Decorate, Sort, Undecorate
- Трансформацията на Шварц (Schwartzian transform)
- Искаме да сортираме списък от имена първо по второто име, после по пъврото
names = ['Monty Python', 'Bilbo Baggins', 'Mityo Python']
# 2 пъти split
# цялото се вика n*log(n) пъти
def names_cmp(n1, n2):
first1, last1 = n1.split()
first2, last2 = n2.split()
if last1 != last2:
return cmp(last1, last2)
else:
return cmp(first1, first2)
names.sort(names_cmp)
DSU (2)
# (('Python', 'Monty'), 'Monty Python')
decorated = zip([tuple(reversed(name.split())) for name in names], names)
decorated.sort()
undecorated = [value for (decor, value) in decorated]
print undecorated
['Bilbo Baggins', 'Mityo Python', 'Monty Python']
- списъци и n-орки се сравняват първо по първи елемент, ако са равни—по-втори и т.н.
zip
взима няколко списъка и връща списък от n-орки с поредните елементи на списъците- обработката на един елемент се вика веднъж и се пише веднъж
DSU (3)
ready = sorted(names, key = lambda name: tuple(reversed(name.split())))
или:
def decorate(name):
first, last = name.split()
return last, first
ready = sorted(names, key = decorate)
- В Python 2.4 има доста нововъведения: sorted,
key
параметъра наsort
иsorted
- Параметърът
key
очаква функция с един аргумент и сортира по нейната стойност, а не по стойността на елементите на поредицата - Стойностите на поредицата не се променят от викането на функцията, дадена на
key
- DSU се прилага вече много по-лесно и кратко :)
Няма коментари:
Публикуване на коментар