使用reduce()的有用代码?[关闭]


123

这里有没有人有任何有用的代码在python中使用reduce()函数?除了示例中常见的+和*之外,是否还有其他代码?

通过GvR 引用Python 3000中的reduce()的命运


1
from functools import reduce允许相同的代码工作在两个Python 2和3
JFS

Answers:


66

除+和*外,我为它找到的其他用途是与和和或,但现在我们有anyall来替换这些情况。

foldl并且foldr确实在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]], [])

数字列表到一个数字

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

23
为了弄平列表,我更喜欢list(itertools.chain(* nested_list))
罗伯托·邦瓦莱特

13
sum([[1、2、3],[4、5],[6、7、8],[])
Gordon Wrigley 2010年

3
这对于按位运算也很有用。如果要采用按位或一堆数字,例如,如果需要将标志从列表转换为位掩码,该怎么办?
锑2012年

6
做一些基准测试,大型列表的“丑陋”方法更快。 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变得昂贵,而将str和concatenate连接的int保持便宜。
jimbob博士2013年

3
当然,对于小列表(大小为10),是,那么reduce方法的速度快1.3倍。但是,即使在这种情况下,避免reduce并执行简单循环甚至更快也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 s,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 s以及将数字转换为str方法需要0.16 s。
jimbob博士13年

51

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

1
什么是lcm在第二行?
beardc 2012年

1
@BirdJaguarIV:按照答案中的链接lcm()返回两个数字的最小公倍数。
2012年

39

reduce()可以用来解析点名(eval()太不安全了,无法使用):

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


12

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

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

1
我也喜欢这里的讽刺意味
罗马

11

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

这正是(某些)函数式程序员喜欢reduce(或折叠函数,通常称为此类函数)的原因。经常有已经有很多二元函数喜欢+*minmax,级联和,在我的情况,make_andmake_or。有一个reduce它,将这些操作提升到列表(或一般来说是折叠功能的树或其他任何东西)变得微不足道。

当然,如果sum经常使用某些实例化(例如),那么您就不想继续写作reduce。但是,除了sum使用某些for循环定义之外,您还可以使用轻松定义它reduce

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


为了防止空列表,您可以利用and运算符的短路行为:L and reduce(make_and, L)如果在这种情况下返回空列表是合适的
jfs 2012年

9

函数组成:如果您已经有了要连续应用的函数列表,例如:

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))))某种语法更易读和可维护。


1
优点是您可以更改要在代码中应用的功能列表。
hakanc


7

@Blair Conrad:您也可以使用sum来实现glob / reduce,如下所示:

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

这比您的两个示例中的任何一个都不那么冗长,完全是Python风格的,并且仍然只是一行代码。

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


2
两者sumreduce导致二次行为。可以在线性时间内完成:files = chain.from_iterable(imap(iglob, args))。尽管在这种情况下,由于时间的关系,glob()访问磁盘可能并不重要。
jfs 2012年

6

reduce 可用于支持链式属性查找:

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

当然,这相当于

self.request.user.email

但是在代码需要接受任意属性列表时很有用。

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


4

reduce当您需要查找类似set对象序列的并集或交集时,此功能很有用。

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

(除了实际set的,其中的一个示例是Django的Q对象。)

另一方面,如果要处理bools,则应使用anyall

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


3

我正在为一种语言编写一个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折叠的方向错误,所以我大致使用了:

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

我用compose = lambda *func: lambda arg: reduce(lambda x, f: f(x), reversed(funcs), arg),以生成的功能性能测试所有可能的组合。
2012年

3

reduce可以用来获取第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],因为它是具有最大3rd元素的列表+


max(lst,key = lambda x:x [2])
aoeu256 '19

3

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循环可以完成相同的事情,但是,……花一些严肃的时间使用函数式语言往往会使我在映射和归约方面进行思考。

顺便说一句,如果有人像我在中所做的那样设置属性的更好方法extract,而您要设置的属性的父级可能还不存在,请告诉我。


3

不知道这是您要追求的,但是您可以在Google上搜索源代码

点击链接以搜索“ function:reduce()lang:python”在Google Code搜索上

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

  • MoinMoin
  • 佐佩
  • 数字
  • 科学Python

等),但由于它们是大型项目,因此这些不足为奇。

reduce的功能可以使用函数递归来完成,我想Guido认为它更明确。

更新:

由于Google的代码搜索已于2012年1月15日停产,因此除了恢复常规的Google搜索外,还有一个名为“代码片段集”的代码看起来很有希望。这个(封闭的)问题的答案中提到了许多其他资源。是否可以替换Google Code Search?

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

Nullege搜索引擎是Python示例(使用开源代码)的一个很好的来源。


1
“可以使用函数递归来完成reduce的功能” ...或for循环。
杰森·奥伦多夫

2
此外,搜索reduce()会产生在其代码内定义reduce函数的项目。你应该寻找郎咸平:蟒蛇“减少(”来找到内置功能的实际用途。
Seun Osewa

@Seun Osewa:即使进行搜索lang:python "reduce("也会reduce根据源代码的编码样式找到的定义。
martineau 2011年

2
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']
#     )},
#     [])
#},
#[])

1
您能否对这里发生的事情添加一些解释?否则,其实用性根本就不明显。
Zoran Pavlovic 2014年

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

2

我曾经用sqlalchemy-searchable中的运算符reduce 来连接PostgreSQL搜索向量列表||

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)

1

我有一个老式的pipegrep Python实现,该实现使用reduce和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]
steveha 2010年

实际上,@ steveha,您的示例将生成扩展的glob列表的列表,而不是匹配glob的所有项目的平面列表,但是您可以使用列表理解+和,如@ [Eli Courtwright](#16198 ) 指出。
布莱尔·康拉德

1
好的,您是正确的,对此感到抱歉。我还是不太喜欢extend / reduce / lambda / map的组合!我建议itertools使用docs.python.org/library/itertools.html中flatten()配方import,然后编写:(files = flatten(glob.glob(f) for f in args) 这一次,我在发布代码之前对其进行了测试,并且我知道这可以正常工作。)
Steveha

files = chain.from_iterable(imap(iglob, args))其中chainimap来自itertools模块,glob.iglob如果从模式args产生多个目录中的文件,则很有用。
jfs 2012年

1

假设有一些年度统计数据存储在“计数器”列表中。我们想要查找不同年份中每个月的MIN / MAX值。例如,对于1月将是10。对于2月将是15。我们需要将结果存储在新的Counter中。

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

1

我有代表某种重叠区间(基因组外显子)的对象,并使用__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)

1

我只是发现了有用的用法reduce:在不删除定界符的情况下拆分字符串该代码完全来自“编程口语”博客。这是代码:

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中无法普遍解决的极端情况。有关更深入的解释,我将您重定向到原始博客文章。


0

使用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"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.