Хтось тут має корисний код, який використовує функцію redu () у python? Чи є якийсь код, крім звичайних + та *, який ми бачимо в прикладах?
Дивіться долю скорочення () у Python 3000 від GvR
Хтось тут має корисний код, який використовує функцію redu () у python? Чи є якийсь код, крім звичайних + та *, який ми бачимо в прикладах?
Дивіться долю скорочення () у Python 3000 від GvR
Відповіді:
Інші застосунки, які я знайшов для цього, окрім + і *, були з та і або, але тепер ми маємо anyі allзамінити ці випадки.
foldlі foldrпридумати в схемі багато ...
Ось кілька милих звичаїв:
Вирівняти список
Мета: перетворитися [[1, 2, 3], [4, 5], [6, 7, 8]]на [1, 2, 3, 4, 5, 6, 7, 8].
reduce(list.__add__, [[1, 2, 3], [4, 5], [6, 7, 8]], [])
Список цифр до числа
Мета: перетворитися [1, 2, 3, 4, 5, 6, 7, 8]на 12345678.
Некрасивий, повільний шлях:
int("".join(map(str, [1,2,3,4,5,6,7,8])))
Гарний reduceспосіб:
reduce(lambda a,d: 10*a+d, [1,2,3,4,5,6,7,8], 0)
timeit.repeat('int("".join(map(str, digit_list)))', setup = 'digit_list = list(d%10 for d in xrange(1,1000))', number=1000)займає ~ 0,09 секунди, тоді як timeit.repeat('reduce(lambda a,d: 10*a+d, digit_list)', setup = 'digit_list = list(d%10 for d in xrange(1,1000))', number=1000)займає 0,36 секунди (приблизно в 4 рази повільніше). В основному множення на 10 стає дорогим, коли список стає великим, тоді як int до str та конкатенація залишаються дешевими.
timeit.repeat('convert_digit_list_to_int(digit_list)', setup = 'digit_list = [d%10 for d in xrange(1,10)]\ndef convert_digit_list_to_int(digits):\n i = 0\n for d in digits:\n i = 10*i + d\n return i', number=100000)займає 0,06 с, timeit.repeat('reduce(lambda a,d: 10*a+d, digit_list)', setup = 'digit_list = list(d%10 for d in xrange(1,10))', number=100000)займає 0,12 с, а перетворення цифр у метод str займає 0,16 с.
reduce()можна використовувати для пошуку найменшого загального кратного для 3 або більше чисел :
#!/usr/bin/env python
from fractions import gcd
from functools import reduce
def lcm(*args):
return reduce(lambda a,b: a * b // gcd(a, b), args)
Приклад:
>>> lcm(100, 23, 98)
112700
>>> lcm(*range(1, 20))
232792560
lcmу другому рядку?
lcm()повертає найменше спільне кратне два числа.
Знайдіть перетин N заданих списків:
input_list = [[1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7]]
result = reduce(set.intersection, map(set, input_list))
повертає:
result = set([3, 4, 5])
Використання цього, reduceщо я знайшов у своєму коді, включало ситуацію, коли у мене була якась структура класу для логічного вираження, і мені потрібно було перетворити список цих об'єктів вираження у сполучення виразів. У мене вже була функція make_andстворити сполучник із заданими двома виразами, тому я написав reduce(make_and,l). (Я знав, що список не порожній; інакше це було б щось подібне reduce(make_and,l,make_true).)
Це саме та причина, що (деякі) функціональні програмісти люблять reduce(або складають функції, як такі функції зазвичай називають). Є часто вже багато бінарних функцій , таких як +, *, min, max, конкатенація і, в моєму випадку, make_andі make_or. Наявність reduceманіфесту піднімає ці операції до списків (або дерев чи будь-чого, що ви отримали, для функцій складання загалом).
Звичайно, якщо sumчасто використовуються певні моменти (наприклад ), то ви не хочете продовжувати писати reduce. Однак замість того, щоб визначити sumз деяким for-loop, ви можете так само легко визначити його reduce.
Читання, як зазначають інші, справді є проблемою. Ви можете стверджувати, що єдиною причиною, чому люди вважають reduceменш "зрозумілою" є те, що це не є функцією, яку багато людей знають та / або використовують.
andоператора короткого замикання : L and reduce(make_and, L)якщо повернення порожнього списку в цьому випадку
Склад функції : Якщо у вас вже є список функцій, які ви хочете застосовувати послідовно, такі як:
color = lambda x: x.replace('brown', 'blue')
speed = lambda x: x.replace('quick', 'slow')
work = lambda x: x.replace('lazy', 'industrious')
fs = [str.lower, color, speed, work, str.title]
Потім ви можете застосовувати їх послідовно за допомогою:
>>> call = lambda s, func: func(s)
>>> s = "The Quick Brown Fox Jumps Over the Lazy Dog"
>>> reduce(call, fs, s)
'The Slow Blue Fox Jumps Over The Industrious Dog'
У цьому випадку ланцюжок методів може бути більш читабельним. Але іноді це неможливо, і такий вид композиції може бути більш читабельним та доглянутим, ніж f1(f2(f3(f4(x))))якийсь синтаксис.
Ви можете замінити value = json_obj['a']['b']['c']['d']['e']на:
value = reduce(dict.__getitem__, 'abcde', json_obj)
Якщо у вас вже є шлях a/b/c/..у списку. Наприклад, Зміна значень у викладених вкладених диктах за допомогою елементів у списку .
@Blair Conrad: Ви також можете реалізувати глобус / зменшити за допомогою суми, наприклад:
files = sum([glob.glob(f) for f in args], [])
Це менш багатослівно, ніж будь-який із ваших двох прикладів, ідеально піфонічний і все ще є лише одним рядком коду.
Тому, щоб відповісти на початкове запитання, я особисто намагаюся уникати використання скорочення, оскільки це ніколи не потрібно, і я вважаю його менш зрозумілим, ніж інші підходи. Однак деякі люди звикають до скорочення і вважають за краще перелічити розуміння (особливо програмістів Haskell). Але якщо ви вже не замислюєтесь над проблемою з точки зору зменшення, вам, ймовірно, не потрібно хвилюватися про її використання.
sumй reduceінше веде до квадратичної поведінки. Це може бути зроблено в лінійний час: files = chain.from_iterable(imap(iglob, args)). Хоча це, мабуть, не має значення в цьому випадку через час, який потрібно глобалу () отримати доступ до диска.
reduce може використовуватися для підтримки ланцюгових пошукових атрибутів:
reduce(getattr, ('request', 'user', 'email'), self)
Звичайно, це рівнозначно
self.request.user.email
але корисно, коли ваш код повинен прийняти довільний список атрибутів.
(Приковані атрибути довільної довжини є загальними при роботі з моделями Джанго.)
reduceкорисно, коли вам потрібно знайти об'єднання або перетин послідовності setоб'єктів-подібних.
>>> reduce(operator.or_, ({1}, {1, 2}, {1, 3})) # union
{1, 2, 3}
>>> reduce(operator.and_, ({1}, {1, 2}, {1, 3})) # intersection
{1}
(Окрім фактичних sets, прикладом таких є Q-об'єкти Django .)
З іншого боку, якщо ви маєте справу з boolами, слід використовувати anyі all:
>>> any((True, False, True))
True
Після схвалення мого коду, здається, єдине, для чого я скористався, - це обчислення факторіалу:
reduce(operator.mul, xrange(1, x+1) or (1,))
Я пишу композитну функцію для мови, тому я будую складену функцію, використовуючи скорочення разом зі своїм оператором застосування.
Коротше кажучи, композиція приймає список функцій для складання в одну функцію. Якщо у мене є складна операція, яка застосовується поетапно, я хочу все це скласти так:
complexop = compose(stage4, stage3, stage2, stage1)
Таким чином, я можу застосувати його до такого виразу:
complexop(expression)
І я хочу, щоб це було рівнозначним:
stage4(stage3(stage2(stage1(expression))))
Тепер, щоб створити свої внутрішні об’єкти, я хочу, щоб він сказав:
Lambda([Symbol('x')], Apply(stage4, Apply(stage3, Apply(stage2, Apply(stage1, Symbol('x'))))))
(Клас Lambda створює функцію, визначену користувачем, і Apply будує додаток функції.)
Тепер, зменшуйте, на жаль, складки неправильно, тому я завершив використання, приблизно:
reduce(lambda x,y: Apply(y, x), reversed(args + [Symbol('x')]))
Щоб розібратися, що знижує кількість продуктів, спробуйте це у відповіді:
reduce(lambda x, y: (x, y), range(1, 11))
reduce(lambda x, y: (y, x), reversed(range(1, 11)))
compose = lambda *func: lambda arg: reduce(lambda x, f: f(x), reversed(funcs), arg)для створення всіх можливих комбінацій функцій для тестування продуктивності.
скорочення може бути використане для отримання списку з максимальним n-м елементом
reduce(lambda x,y: x if x[2] > y[2] else y,[[1,2,3,4],[5,2,5,7],[1,6,0,2]])
повернеться [5, 2, 5, 7], оскільки це список з max 3-го елемента +
Зменшення не обмежується скалярними операціями; його також можна використовувати для сортування речей у відра. (Це найчастіше я використовую у зниженні).
Уявіть собі випадок, коли у вас є список об’єктів, і ви хочете переорганізувати його ієрархічно на основі властивостей, що зберігаються на об'єкті. У наступному прикладі я створюю список об’єктів метаданих, пов’язаних зі статтями в газеті, кодованій XML, з articlesфункцією. articlesстворює список XML-елементів, а потім по черзі відображає їх, створюючи об'єкти, що містять цікаву інформацію про них. На передньому кінці я хочу дозволити користувачу переглядати статті за розділом / підрозділом / заголовком. Тому я використовую reduceперелік статей і повертаю єдиний словник, який відображає ієрархію розділу / підрозділу / статті.
from lxml import etree
from Reader import Reader
class IssueReader(Reader):
def articles(self):
arts = self.q('//div3') # inherited ... runs an xpath query against the issue
subsection = etree.XPath('./ancestor::div2/@type')
section = etree.XPath('./ancestor::div1/@type')
header_text = etree.XPath('./head//text()')
return map(lambda art: {
'text_id': self.id,
'path': self.getpath(art)[0],
'subsection': (subsection(art)[0] or '[none]'),
'section': (section(art)[0] or '[none]'),
'headline': (''.join(header_text(art)) or '[none]')
}, arts)
def by_section(self):
arts = self.articles()
def extract(acc, art): # acc for accumulator
section = acc.get(art['section'], False)
if section:
subsection = acc.get(art['subsection'], False)
if subsection:
subsection.append(art)
else:
section[art['subsection']] = [art]
else:
acc[art['section']] = {art['subsection']: [art]}
return acc
return reduce(extract, arts, {})
Тут я надаю обидві функції, тому що я думаю, що це показує, як карта та зменшення можуть красиво доповнювати один одного при роботі з об'єктами. Те ж саме можна було б зробити з циклом for, ... але витратити якийсь серйозний час з функціональною мовою, як правило, змусив мене думати з точки зору карти та зменшити.
До речі, якщо у когось є кращий спосіб встановити властивості, як я роблю extract, де батьки власності, яку ви хочете встановити, ще не існують, будь ласка, повідомте мене про це.
Не впевнений, що це те, що ви хочете, але ви можете шукати вихідний код в Google .
Перейдіть за посиланням для пошуку за функцією 'function: redu () lang: python' у пошуку Google Code
На перший погляд використовуються наступні проекти reduce()
і т. д., але тоді це навряд чи дивно, оскільки це величезні проекти.
Функціональність зменшення може бути виконана за допомогою функціональної рекурсії, яку, напевно, Гайдо вважав більш чіткою.
Оновлення:
Оскільки пошук коду Google було припинено 15 січня 2012 року, окрім того, як повернутися до звичайних пошукових запитів Google, є щось, що називається Колекція фрагментів коду, що виглядає перспективно. Ряд інших ресурсів згадується у відповідях на це (закрите) запитання Заміна для пошуку в коді Google?.
Оновлення 2 (29 травня-2017):
Хорошим джерелом для прикладів Python (у відкритому коді) є пошукова система Nullege .
forциклу.
lang:python "reduce("знайдемо визначення reduceзалежно від стилю кодування вихідного коду.
import os
files = [
# full filenames
"var/log/apache/errors.log",
"home/kane/images/avatars/crusader.png",
"home/jane/documents/diary.txt",
"home/kane/images/selfie.jpg",
"var/log/abc.txt",
"home/kane/.vimrc",
"home/kane/images/avatars/paladin.png",
]
# unfolding of plain filiname list to file-tree
fs_tree = ({}, # dict of folders
[]) # list of files
for full_name in files:
path, fn = os.path.split(full_name)
reduce(
# this fucction walks deep into path
# and creates placeholders for subfolders
lambda d, k: d[0].setdefault(k, # walk deep
({}, [])), # or create subfolder storage
path.split(os.path.sep),
fs_tree
)[1].append(fn)
print fs_tree
#({'home': (
# {'jane': (
# {'documents': (
# {},
# ['diary.txt']
# )},
# []
# ),
# 'kane': (
# {'images': (
# {'avatars': (
# {},
# ['crusader.png',
# 'paladin.png']
# )},
# ['selfie.jpg']
# )},
# ['.vimrc']
# )},
# []
# ),
# 'var': (
# {'log': (
# {'apache': (
# {},
# ['errors.log']
# )},
# ['abc.txt']
# )},
# [])
#},
#[])
Я використовував reduce для об'єднання списку пошукових векторів PostgreSQL з ||оператором у sqlalchemy- search :
vectors = (self.column_vector(getattr(self.table.c, column_name))
for column_name in self.indexed_columns)
concatenated = reduce(lambda x, y: x.op('||')(y), vectors)
compiled = concatenated.compile(self.conn)
У мене є стара реалізація Pygre на pipegrep, який використовує файл зменшення та модуль glob, щоб створити список файлів для обробки:
files = []
files.extend(reduce(lambda x, y: x + y, map(glob.glob, args)))
Я вважав це зручним у той час, але це дійсно не потрібно, оскільки щось подібне настільки ж добре, і, ймовірно, читабельніше
files = []
for f in args:
files.extend(glob.glob(f))
files = [glob.glob(f) for f in args]
itertools, скориставшись flatten()рецептом з docs.python.org/library/itertools.html , а потім напишіть : files = flatten(glob.glob(f) for f in args) (І цього разу я перевірив код, перш ніж публікувати його, і я знаю, що це працює правильно.)
files = chain.from_iterable(imap(iglob, args))де chain, imapзнаходяться з itertoolsмодуля і glob.iglobкорисно, якщо шаблон із argsфайлу може отримати файли з декількох каталогів.
Скажімо, що існує кілька щорічних статистичних даних, що зберігаються у списку лічильників. Ми хочемо знайти значення MIN / MAX в кожному місяці протягом різних років. Наприклад, за січень було б 10. А для лютого було б 15. Нам потрібно зберігати результати в новому лічильнику.
from collections import Counter
stat2011 = Counter({"January": 12, "February": 20, "March": 50, "April": 70, "May": 15,
"June": 35, "July": 30, "August": 15, "September": 20, "October": 60,
"November": 13, "December": 50})
stat2012 = Counter({"January": 36, "February": 15, "March": 50, "April": 10, "May": 90,
"June": 25, "July": 35, "August": 15, "September": 20, "October": 30,
"November": 10, "December": 25})
stat2013 = Counter({"January": 10, "February": 60, "March": 90, "April": 10, "May": 80,
"June": 50, "July": 30, "August": 15, "September": 20, "October": 75,
"November": 60, "December": 15})
stat_list = [stat2011, stat2012, stat2013]
print reduce(lambda x, y: x & y, stat_list) # MIN
print reduce(lambda x, y: x | y, stat_list) # MAX
У мене є об'єкти, що представляють собою певні інтервали, що перекриваються (геномні екзони), і переглянув їх перетин, використовуючи __and__:
class Exon:
def __init__(self):
...
def __and__(self,other):
...
length = self.length + other.length # (e.g.)
return self.__class__(...length,...)
Потім, коли у мене є колекція їх (наприклад, у тому ж гені), я використовую
intersection = reduce(lambda x,y: x&y, exons)
Щойно я знайшов корисне використання reduce: розділення рядка без вилучення роздільника . Код повністю створений з блогу Programatic Speaking. Ось код:
reduce(lambda acc, elem: acc[:-1] + [acc[-1] + elem] if elem == "\n" else acc + [elem], re.split("(\n)", "a\nb\nc\n"), [])
Ось результат:
['a\n', 'b\n', 'c\n', '']
Зауважте, що він обробляє крайові випадки, на які популярна відповідь в ТА не відповідає. Для більш поглибленого пояснення я перенаправляю вас до оригінальної публікації в блозі.
Використовуючи зменшити (), щоб дізнатися, чи перелік дат є послідовним:
from datetime import date, timedelta
def checked(d1, d2):
"""
We assume the date list is sorted.
If d2 & d1 are different by 1, everything up to d2 is consecutive, so d2
can advance to the next reduction.
If d2 & d1 are not different by 1, returning d1 - 1 for the next reduction
will guarantee the result produced by reduce() to be something other than
the last date in the sorted date list.
Definition 1: 1/1/14, 1/2/14, 1/2/14, 1/3/14 is consider consecutive
Definition 2: 1/1/14, 1/2/14, 1/2/14, 1/3/14 is consider not consecutive
"""
#if (d2 - d1).days == 1 or (d2 - d1).days == 0: # for Definition 1
if (d2 - d1).days == 1: # for Definition 2
return d2
else:
return d1 + timedelta(days=-1)
# datelist = [date(2014, 1, 1), date(2014, 1, 3),
# date(2013, 12, 31), date(2013, 12, 30)]
# datelist = [date(2014, 2, 19), date(2014, 2, 19), date(2014, 2, 20),
# date(2014, 2, 21), date(2014, 2, 22)]
datelist = [date(2014, 2, 19), date(2014, 2, 21),
date(2014, 2, 22), date(2014, 2, 20)]
datelist.sort()
if datelist[-1] == reduce(checked, datelist):
print "dates are consecutive"
else:
print "dates are not consecutive"
from functools import reduceдозволяє однаковому коду працювати і на Python 2 і 3.