使用 reduce()的有用代码?

这里有人有在 python 中使用 reduce ()函数的有用代码吗?除了我们在示例中看到的通常的 + 和 * 之外,还有其他代码吗?

通过 GvR 查阅 在 Python3000中 reduce ()的命运

87823 次浏览

Not sure if this is what you are after but you can search source code on Google.

点击这个链接,在谷歌代码搜索中搜索 “ function: reduce () lang: python”

乍一看,以下项目使用 reduce()

  • MoinMoin
  • Zope
  • 数字
  • 科学巨蟒

等等,等等,但是这些并不令人惊讶,因为它们是巨大的项目。

Reduce 的功能可以通过使用函数递归来实现,我猜 Guido 认为函数递归更加明确。

更新:

自从谷歌的代码搜索在2012年1月15日停止以来,除了恢复到常规的谷歌搜索之外,还有一种叫做 代码段集合的东西看起来很有前途。在这个(封闭的)问题 代替 Google 代码搜索?的答案中提到了许多其他的资源。

更新2(2017年5月29日) :

A good source for Python examples (in open-source code) is the 无效搜索引擎.

我有一个旧的 Python 实现 pipegrep,它使用 reduce 和 globb 模块来构建要处理的文件列表:

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

当时我发现它很方便,但是真的没有必要,因为类似的东西也一样好,而且可能更易读

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

@ Blair Conrad: 你也可以实现 global/reduce 使用 sum,像这样:

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

这比您的两个示例中的任何一个都要简单,完全是 Python 式的,而且仍然只有一行代码。

因此,为了回答最初的问题,我个人尽量避免使用 reduce,因为它从来都不是真正必要的,而且我发现它比其他方法更不清楚。然而,有些人习惯于 reduce,并开始喜欢它而不是列出理解(特别是 Haskell 程序员)。但是,如果您还没有考虑到减少的问题,那么您可能不需要担心使用它。

在浏览了我的代码之后,似乎我使用 reduce 的唯一目的就是计算阶乘:

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

我在代码中发现的 reduce的用法涉及到这样一种情况: 我有一些逻辑表达式的类结构,我需要将这些表达式对象的列表转换为表达式的连接。我已经有一个函数 make_and来创建给定两个表达式的连词,所以我编写了 reduce(make_and,l)。(我知道这个列表不是空的; 否则它将类似于 reduce(make_and,l,make_true)。)

这正是(一些)函数式程序员喜欢 reduce(或 弃牌函数,因为这类函数通常被调用)的原因。通常已经有许多二进制函数,如 +*minmax、串联,在我的例子中,还有 make_andmake_or。使用 reduce可以轻松地将这些操作提升为列表(或树或其他类型的操作,对于折叠函数而言)。

当然,如果经常使用某些实例化(比如 sum) ,那么就不需要继续编写 reduce。但是,与使用 for 循环定义 sum不同,使用 reduce定义 可以同样容易。

正如其他人提到的,可读性确实是一个问题。然而,您可能会争辩说,人们发现 reduce不那么“清晰”的唯一原因是因为它不是许多人知道和/或使用的函数。

除了 + 和 * 之外,我还发现了其他一些用法,但是现在我们有了 anyall来替换这些用例。

foldlfoldr在 Scheme 中出现过很多次..。

下面是一些可爱的用法:

列张清单

目标: 把 [[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]], [])

List of digits to a number

Goal: turn [1, 2, 3, 4, 5, 6, 7, 8] into 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() can be used to find 3个或3个以上数字的最小公倍数:

#!/usr/bin/env python
from math 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>

我正在为一种语言编写一个组合函数,因此我使用 reduce 和 application 操作符构造组合函数。

简而言之,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')]))

为了弄清楚 reduce 产生了什么,请在 REPL 中尝试以下步骤:

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

找出 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])

Via: Python-两个列表的交集

你可以用以下方式取代 value = json_obj['a']['b']['c']['d']['e']:

value = reduce(dict.__getitem__, 'abcde', json_obj)

如果您已经将路径 a/b/c/..作为列表。

我认为 reduce 是一个愚蠢的命令。因此:

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

Reduce 不仅限于标量操作; 它还可以用于将事物排序到桶中。(这是我最常用的 reduce)。

想象一下这样一种情况: 您拥有一个对象列表,并且希望根据对象中直接存储的属性按层次结构重新组织它。在下面的示例中,我使用 articles函数生成与 XML 编码的报纸中的文章相关的元数据对象列表。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 和 reduce 在处理对象时如何很好地互补。同样的事情也可以通过 for 循环来完成,... 但是在函数式语言上花费一些时间往往会让我从 map 和 reduce 的角度思考问题。

顺便说一下,如果有人有更好的方法来设置属性,就像我在 extract中所做的那样,您想要设置的属性的父级可能还不存在,请让我知道。

Reduce 可以用来获取具有最大 nth 元素的列表

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 3rd 元素 + 的列表

复合函数 : 如果您已经有了一个希望连续应用的函数列表,比如:

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))))类型的语法更具可读性和可维护性。

使用 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"

假设有一些年度统计数据存储了一个计数器列表。 我们希望找到跨越不同年份的每个月的 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
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']
#     )},
#     [])
#},
#[])

我有代表某种重叠区间(基因组外显子)的对象,并使用 __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可用于支持链式属性查找:

reduce(getattr, ('request', 'user', 'email'), self)

当然,这相当于

self.request.user.email

但是当您的代码需要接受任意的属性列表时,它非常有用。

(在处理 Django 模型时,任意长度的链状属性是常见的。)

当需要查找类 set对象序列的并集或交集时,reduce非常有用。

>>> reduce(operator.or_, ({1}, {1, 2}, {1, 3}))  # union
{1, 2, 3}
>>> reduce(operator.and_, ({1}, {1, 2}, {1, 3}))  # intersection
{1}

(Apart from actual sets, an example of these are 姜戈的 Q 对象.)

另一方面,如果你处理的是 bool,你应该使用 anyall:

>>> any((True, False, True))
True
def dump(fname,iterable):
with open(fname,'w') as f:
reduce(lambda x, y: f.write(unicode(y,'utf-8')), iterable)

我刚刚发现了 reduce的有用用法: 不移除分隔符就拆分字符串. 这段代码完全来自于 ProgramativeQuingblog。以下是代码:

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 中流行的答案所不能处理的边缘情况。为了更深入的解释,我重定向到原来的博客文章你。

I used reduce 连接 PostgreSQL 搜索向量列表 with the || operator in sqlalchemy-searchable:

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)