Полезный код, который использует Reduce()?

У кого-нибудь здесь есть какой-нибудь полезный код, который использует функцию Reduce() в Python? Есть ли другой код, кроме обычных + и *, который мы видим в примерах?

См. Fate of Reduce () в Python 3000 от GvR

24 ответа

Другие применения, которые я нашел для этого, кроме + и *, были с и и или, но теперь у нас есть 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)

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

reduce() может быть использован для разрешения точечных имен (где eval() слишком небезопасно для использования):

>>> import __main__
>>> reduce(getattr, "os.path.abspath".split('.'), __main__)
<function abspath at 0x009AB530>

Найдите пересечение 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])

через: Python - пересечение двух списков

Я думаю, что уменьшить это глупая команда. Следовательно:

reduce(lambda hold,next:hold+chr(((ord(next.upper())-65)+13)%26+65),'znlorabggbbhfrshy','')

Использование 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, вы можете так же легко определить его с помощью reduce,

Читаемость, как уже упоминалось другими, действительно является проблемой. Однако можно утверждать, что это единственная причина, по которой люди находят reduce менее "ясно", потому что это не та функция, которую многие люди знают и / или используют.

Композиция функций: если у вас уже есть список функций, которые вы хотите применить последовательно, например:

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/.. как список. Например, Измените значения в dict для вложенных dicts, используя элементы в списке.

@Blair Conrad: Вы могли бы также реализовать свой глобус / уменьшить, используя сумму, например так:

files = sum([glob.glob(f) for f in args], [])

Это менее многословно, чем любой из ваших двух примеров, совершенно Pythonic и все еще только одна строка кода.

Поэтому, чтобы ответить на первоначальный вопрос, я лично стараюсь избегать использования Reduce, потому что это никогда не требуется, и я считаю, что он менее понятен, чем другие подходы. Тем не менее, некоторые люди привыкли сокращать и предпочитают это перечислять (особенно программисты на Haskell). Но если вы еще не думаете о проблеме с точки зрения сокращения, вам, вероятно, не нужно беспокоиться об ее использовании.

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 Джанго.)

С другой стороны, если вы имеете дело с bools, вы должны использовать any а также all:

>>> any((True, False, True))
True

Уменьшить можно использовать для получения списка с максимальным 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], так как это список с максимальным 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, {})

Я даю обе функции здесь, потому что я думаю, что это показывает, как map и сокращения могут хорошо дополнять друг друга при работе с объектами. То же самое можно было бы сделать с помощью цикла for, но затрачивание серьезного времени на функциональном языке, как правило, заставляло меня думать с точки зрения карты и сокращения.

Кстати, если у кого-то есть лучший способ установить свойства, как я делаю в extractгде родители объекта, который вы хотите установить, еще не существуют, пожалуйста, дайте мне знать.

Я пишу функцию compose для языка, поэтому я создаю составную функцию с использованием метода Reduce вместе с оператором apply.

В двух словах, compose принимает список функций для объединения в одну функцию. Если у меня есть сложная операция, которая применяется поэтапно, я хочу собрать все это вместе так:

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')]))

Чтобы выяснить, что производит уменьшение, попробуйте это в REPL:

reduce(lambda x, y: (x, y), range(1, 11))
reduce(lambda x, y: (y, x), reversed(range(1, 11)))

После анализа моего кода, кажется, единственное, что я использовал для сокращения - это вычисление факториала:

reduce(operator.mul, xrange(1, x+1) or (1,))

Не уверен, что это то, что вам нужно, но вы можете искать исходный код в Google.

Перейдите по ссылке для поиска по запросу "функция: уменьшить () lang:python" в поиске кода Google.

На первый взгляд следующие проекты используют reduce()

  • MoinMoin
  • Zope
  • числовой
  • ScientificPython

и т.п.

Функциональность Reduce может быть реализована с помощью рекурсии функций, которая, как мне кажется, Гвидо считал более явной.

Обновить:

Так как 15 января 2012 г. поиск Google по кодам был прекращен, помимо возврата к обычным поискам в Google, есть нечто, называемое " Коллекция фрагментов кода", которое выглядит многообещающим. В ответах на этот (закрытый) вопрос упоминается ряд других ресурсов. Замена Google Code Search?,

Обновление 2 (29 мая 2017 года):

Хорошим источником примеров Python (в открытом исходном коде) является поисковая система Nullege.

def dump(fname,iterable):
  with open(fname,'w') as f:
    reduce(lambda x, y: f.write(unicode(y,'utf-8')), iterable)
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: расщепление строки без удаления разделителя. Код полностью из блога Programatics 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', '']

Обратите внимание, что он обрабатывает крайние случаи, которые популярный ответ в SO не делает. Для более подробного объяснения я перенаправляю вас к оригинальному сообщению в блоге.

Я использовал reduce объединить список векторов поиска PostgreSQL с || оператор в sqlalchemy-для поиска:

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)

У меня есть старая реализация Python pipegrep, которая использует Redu и модуль glob для создания списка файлов для обработки:

files = []
files.extend(reduce(lambda x, y: x + y, map(glob.glob, args)))

В то время я нашел это удобным, но на самом деле в этом нет необходимости, поскольку нечто похожее не менее хорошо и, вероятно, более читабельно.

files = []
for f in args:
    files.extend(glob.glob(f))

У меня есть объекты, представляющие какие-то перекрывающиеся интервалы (геномные экзоны), и я пересмотрел их пересечение, используя __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)

Допустим, есть некоторые годовые статистические данные, хранящиеся в списке счетчиков. Мы хотим найти значения 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

С помощью метода Reduce() выясняется, является ли список дат последовательным:

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"
Другие вопросы по тегам