Nützlicher Code, der redu () verwendet? [geschlossen]


123

Hat hier jemand nützlichen Code, der die Funktion redu () in Python verwendet? Gibt es einen anderen Code als die üblichen + und *, die wir in den Beispielen sehen?

Siehe Fate of redu () in Python 3000 von GvR


1
from functools import reduceermöglicht den gleichen Code auf Python 2 und 3.
jfs

Antworten:


66

Die anderen Verwendungen, die ich neben + und * dafür gefunden habe, waren mit und und oder, aber jetzt haben anyund müssen wir alldiese Fälle ersetzen.

foldlund foldrkommen oft in Schema ...

Hier sind einige nette Verwendungen:

Reduzieren Sie eine Liste

Ziel: verwandeln [[1, 2, 3], [4, 5], [6, 7, 8]]in [1, 2, 3, 4, 5, 6, 7, 8].

reduce(list.__add__, [[1, 2, 3], [4, 5], [6, 7, 8]], [])

Liste der Ziffern einer Zahl

Ziel: verwandeln [1, 2, 3, 4, 5, 6, 7, 8]in 12345678.

Hässlicher, langsamer Weg:

int("".join(map(str, [1,2,3,4,5,6,7,8])))

Schöner reduceWeg:

reduce(lambda a,d: 10*a+d, [1,2,3,4,5,6,7,8], 0)

23
Zum Reduzieren einer Liste bevorzuge ich Liste (itertools.chain (* nested_list))
Roberto Bonvallet

13
Summe ([[1, 2, 3], [4, 5], [6, 7, 8]], [])
Gordon Wrigley

3
Es ist auch nützlich für bitweise Operationen. Was ist, wenn Sie das Bitweise oder eine Reihe von Zahlen verwenden möchten, z. B. wenn Sie Flags von einer Liste in eine Bitmaske konvertieren müssen?
Antimon

6
Bei einigen Benchmarks ist der "hässliche" Weg für große Listen schneller. timeit.repeat('int("".join(map(str, digit_list)))', setup = 'digit_list = list(d%10 for d in xrange(1,1000))', number=1000)dauert ~ 0,09 Sekunden, während es 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 Sekunden dauert (ungefähr 4-mal langsamer). Grundsätzlich wird die Multiplikation mit 10 teuer, wenn die Liste groß wird, während int to str und Verkettung billig bleiben.
Dr. Jimbob

3
Zugegeben, ja für kleine Listen (Größe 10) ist die Reduzierungsmethode 1,3-mal schneller. Selbst in diesem Fall 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)dauert das Vermeiden des Reduzierens und Ausführen einer einfachen Schleife noch schneller 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)dauert 0,12 s und das Konvertieren von Ziffern in die str-Methode dauert 0,16 s.
Dr. Jimbob

51

reduce()kann verwendet werden, um das kleinste gemeinsame Vielfache für 3 oder mehr Zahlen zu finden :

#!/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)

Beispiel:

>>> lcm(100, 23, 98)
112700
>>> lcm(*range(1, 20))
232792560

1
Was steht lcmin der zweiten Zeile?
Bart

1
@BirdJaguarIV: Folgen Sie dem Link in der Antwort. lcm()Gibt das kleinste gemeinsame Vielfache von zwei Zahlen zurück.
JFS

39

reduce()könnte verwendet werden, um gepunktete Namen aufzulösen (wo eval()die Verwendung zu unsicher ist):

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


12

Ich denke reduzieren ist ein dummer Befehl. Daher:

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

1
Ich mag auch die Ironie hier
Roman

11

Die Verwendung reduce, die ich in meinem Code gefunden habe, beinhaltete die Situation, in der ich eine Klassenstruktur für logische Ausdrücke hatte und eine Liste dieser Ausdrucksobjekte in eine Konjunktion der Ausdrücke konvertieren musste. Ich hatte bereits die Funktion make_and, eine Konjunktion mit zwei Ausdrücken zu erstellen, also schrieb ich reduce(make_and,l). (Ich wusste, dass die Liste nicht leer war; sonst wäre es so etwas gewesen reduce(make_and,l,make_true).)

Dies ist genau der Grund, warum (einige) Funktionsprogrammierer Funktionen mögen reduce(oder falten , wie solche Funktionen normalerweise genannt werden). Oft gibt es bereits viele binären Funktionen wie +, *, min, max, Verkettung und in meinem Fall, make_andund make_or. Mit a reduceist es trivial, diese Operationen auf Listen zu heben (oder auf Bäume oder was auch immer Sie haben, für Falzfunktionen im Allgemeinen).

Wenn bestimmte Instanziierungen (z. B. sum) häufig verwendet werden, möchten Sie natürlich nicht weiter schreiben reduce. Anstatt das summit einer for-Schleife zu definieren, können Sie es genauso einfach mit definieren reduce.

Die Lesbarkeit ist, wie von anderen erwähnt, in der Tat ein Problem. Sie könnten jedoch argumentieren, dass der einzige Grund, warum Menschen reduceweniger "klar" finden, darin besteht, dass es sich nicht um eine Funktion handelt, die viele Menschen kennen und / oder nutzen.


Um sich vor einer leeren Liste zu schützen, könnten Sie das Kurzschlussverhalten des andBedieners ausnutzen : L and reduce(make_and, L)Wenn die Rückgabe einer leeren Liste in diesem Fall angemessen ist
jfs

9

Funktionszusammensetzung : Wenn Sie bereits eine Liste von Funktionen haben, die Sie nacheinander anwenden möchten, z.

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]

Dann können Sie sie alle nacheinander anwenden mit:

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

In diesem Fall ist die Methodenverkettung möglicherweise besser lesbar. Aber manchmal ist es nicht möglich, und diese Art von Komposition ist möglicherweise besser lesbar und wartbar als eine f1(f2(f3(f4(x))))Art Syntax.


1
Ein Vorteil ist, dass Sie die Liste der Funktionen ändern können, die im Code angewendet werden sollen.
Hakanc


7

@Blair Conrad: Sie können Ihren Glob / Reduce auch mit sum implementieren, wie folgt:

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

Dies ist weniger ausführlich als eines Ihrer beiden Beispiele, perfekt pythonisch und immer noch nur eine Codezeile.

Um die ursprüngliche Frage zu beantworten, versuche ich persönlich, die Verwendung von Reduzieren zu vermeiden, da dies nie wirklich notwendig ist und ich finde, dass es weniger klar ist als andere Ansätze. Einige Leute gewöhnen sich jedoch daran, das Verständnis zu reduzieren, und ziehen es vor, es aufzulisten (insbesondere Haskell-Programmierer). Wenn Sie jedoch noch nicht über ein Problem in Bezug auf die Reduzierung nachdenken, müssen Sie sich wahrscheinlich keine Gedanken über die Verwendung machen.


2
Beides sumund reduceführen zu quadratischem Verhalten. Dies kann in linearer Zeit erfolgen : files = chain.from_iterable(imap(iglob, args)). Obwohl es in diesem Fall wahrscheinlich keine Rolle spielt, da glob () Zeit benötigt, um auf eine Festplatte zuzugreifen.
JFS

6

reduce kann verwendet werden, um die Suche nach verketteten Attributen zu unterstützen:

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

Dies ist natürlich gleichbedeutend mit

self.request.user.email

Dies ist jedoch nützlich, wenn Ihr Code eine beliebige Liste von Attributen akzeptieren muss.

(Verkettete Attribute beliebiger Länge sind beim Umgang mit Django-Modellen häufig.)


4

reduceist nützlich, wenn Sie die Vereinigung oder den Schnittpunkt einer Folge von setähnlichen Objekten finden müssen.

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

(Abgesehen von tatsächlichen sets sind ein Beispiel dafür Djangos Q-Objekte .)

Auf der anderen Seite, wenn Sie es zu tun bools, sollten Sie verwenden anyund all:

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


3

Ich schreibe eine Kompositionsfunktion für eine Sprache, daher konstruiere ich die Kompositionsfunktion mit reduct zusammen mit meinem Apply-Operator.

Kurz gesagt, compose enthält eine Liste von Funktionen, die zu einer einzigen Funktion zusammengesetzt werden sollen. Wenn ich eine komplexe Operation habe, die schrittweise angewendet wird, möchte ich alles so zusammensetzen:

complexop = compose(stage4, stage3, stage2, stage1)

Auf diese Weise kann ich es dann auf einen Ausdruck wie folgt anwenden:

complexop(expression)

Und ich möchte, dass es gleichbedeutend ist mit:

stage4(stage3(stage2(stage1(expression))))

Um nun meine internen Objekte zu erstellen, möchte ich sagen:

Lambda([Symbol('x')], Apply(stage4, Apply(stage3, Apply(stage2, Apply(stage1, Symbol('x'))))))

(Die Lambda-Klasse erstellt eine benutzerdefinierte Funktion und Apply erstellt eine Funktionsanwendung.)

Reduzieren Sie jetzt leider die Falte in die falsche Richtung, so dass ich ungefähr verwendet habe:

reduce(lambda x,y: Apply(y, x), reversed(args + [Symbol('x')]))

Probieren Sie diese in der REPL aus, um herauszufinden, welche Reduzierungen entstehen:

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

Ich habe verwendet , compose = lambda *func: lambda arg: reduce(lambda x, f: f(x), reversed(funcs), arg)um alle möglichen Kombinationen von Funktionen für Performance - Tests zu erzeugen.
JFS

3

Mit reduzieren kann die Liste mit dem maximalen n-ten Element abgerufen werden

reduce(lambda x,y: x if x[2] > y[2] else y,[[1,2,3,4],[5,2,5,7],[1,6,0,2]])

würde [5, 2, 5, 7] zurückgeben, da es sich um die Liste mit maximalem 3. Element + handelt


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

3

Reduzieren ist nicht auf skalare Operationen beschränkt. Es kann auch verwendet werden, um Dinge in Eimer zu sortieren. (Dies ist, was ich am häufigsten reduzieren verwenden).

Stellen Sie sich einen Fall vor, in dem Sie eine Liste von Objekten haben und diese hierarchisch neu organisieren möchten, basierend auf Eigenschaften, die flach im Objekt gespeichert sind. Im folgenden Beispiel erstelle ich mit der articlesFunktion eine Liste von Metadatenobjekten, die sich auf Artikel in einer XML-codierten Zeitung beziehen . articlesgeneriert eine Liste von XML-Elementen und ordnet sie dann einzeln zu, wobei Objekte erstellt werden, die einige interessante Informationen über sie enthalten. Am Frontend möchte ich den Benutzer die Artikel nach Abschnitt / Unterabschnitt / Überschrift durchsuchen lassen. Daher verwende ich reducedie Liste der Artikel und gebe ein einzelnes Wörterbuch zurück, das die Hierarchie von Abschnitten / Unterabschnitten / Artikeln widerspiegelt.

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, {})

Ich gebe hier beide Funktionen an, weil ich denke, dass es zeigt, wie sich Map und Reduce im Umgang mit Objekten gut ergänzen können. Dasselbe hätte mit einer for-Schleife erreicht werden können, ... aber einige ernsthafte Zeit mit einer funktionalen Sprache zu verbringen, hat mich dazu gebracht, in Bezug auf Karte und Reduzierung zu denken.

Übrigens, wenn jemand eine bessere Möglichkeit hat, Eigenschaften festzulegen, als ich es tue extract, bei denen die Eltern der Eigenschaft, die Sie festlegen möchten, möglicherweise noch nicht existieren, lassen Sie es mich bitte wissen.


3

Sie sind sich nicht sicher, ob Sie danach suchen, aber Sie können den Quellcode bei Google suchen .

Folgen Sie dem Link für eine Suche nach "function: redu () lang: python" in der Google Code-Suche

Auf den ersten Blick verwenden folgende Projekte reduce()

  • Moin Moin
  • Zope
  • Numerisch
  • ScientificPython

etc. etc. aber dann sind diese kaum überraschend, da es sich um riesige Projekte handelt.

Die Funktionalität der Reduzierung kann mithilfe der Funktionsrekursion erfolgen, die Guido meiner Meinung nach expliziter fand.

Aktualisieren:

Da die Codesuche von Google am 15. Januar 2012 eingestellt wurde, gibt es neben der Rückkehr zur regulären Google-Suche eine vielversprechende Code-Snippets-Sammlung . Eine Reihe anderer Ressourcen werden in Antworten auf diese (geschlossene) Frage erwähnt. Ersatz für Google Code Search? .

Update 2 (29. Mai 2017):

Eine gute Quelle für Python-Beispiele (im Open-Source-Code) ist die Nullege-Suchmaschine .


1
"Die Funktionalität der Reduzierung kann mithilfe der Funktionsrekursion erfolgen" ... oder einer forSchleife.
Jason Orendorff

2
Wenn Sie nach redu () suchen, erhalten Sie Projekte, die Reduktionsfunktionen in ihrem Code definieren. Sie sollten nach lang: python "redu (" suchen, um die tatsächliche Verwendung der integrierten Funktion zu finden.
Seun Osewa

@Seun Osewa: Selbst bei der Suche lang:python "reduce("werden Definitionen gefunden, die vom reduceCodierungsstil des Quellcodes abhängen.
Martineau

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
Könnten Sie vielleicht eine kleine Erklärung hinzufügen, was hier los ist? Ansonsten ist die Nützlichkeit überhaupt nicht offensichtlich.
Zoran Pavlovic

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

2

Ich habe reduce eine Liste von PostgreSQL-|| Suchvektoren mit dem Operator in sqlalchemy-searchable verkettet:

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

Ich habe eine alte Python-Implementierung von pipegrep , die reduct und das glob-Modul verwendet, um eine Liste der zu verarbeitenden Dateien zu erstellen:

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

Ich fand es damals praktisch, aber es ist wirklich nicht notwendig, da etwas Ähnliches genauso gut und wahrscheinlich besser lesbar ist

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

Wie wäre es mit einem Listenverständnis? Dies scheint eine perfekte Anwendung dafür zu sein: files = [glob.glob(f) for f in args]
steveha

Tatsächlich, @steveha, führt Ihr Beispiel zu einer Liste von Listen erweiterter Globs und nicht zu einer flachen Liste aller Elemente, die mit den Globs übereinstimmen. Sie können jedoch ein Listenverständnis + eine Summe als @ [Eli Courtwright] (# 16198) verwenden ) weist darauf hin.
Blair Conrad

1
Okay, du hast recht, tut mir leid. Ich mag die Kombination von Extend / Reduce / Lambda / Map immer noch nicht sehr! Ich würde empfehlen itertools, das flatten()Rezept unter Verwendung des Rezepts aus docs.python.org/library/itertools.html zu importieren und dann zu schreiben: files = flatten(glob.glob(f) for f in args) (Und dieses Mal habe ich den Code vor dem Posten getestet und weiß, dass dies korrekt funktioniert.)
steveha

files = chain.from_iterable(imap(iglob, args))where chain, imapare from itertoolsmodule und glob.iglobist nützlich, wenn ein Muster von argsDateien aus mehreren Verzeichnissen ergeben kann.
JFS

1

Angenommen, es sind einige jährliche Statistikdaten in einer Liste von Zählern gespeichert. Wir möchten die MIN / MAX-Werte in jedem Monat über die verschiedenen Jahre hinweg ermitteln. Zum Beispiel wäre es für Januar 10. Und für Februar wäre es 15. Wir müssen die Ergebnisse in einem neuen Zähler speichern.

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

Ich habe Objekte, die eine Art überlappender Intervalle darstellen (genomische Exons), und habe ihre Schnittmenge neu definiert mit __and__:

class Exon:
    def __init__(self):
        ...
    def __and__(self,other):
        ...
        length = self.length + other.length  # (e.g.)
        return self.__class__(...length,...)

Wenn ich dann eine Sammlung davon habe (zum Beispiel im selben Gen), benutze ich

intersection = reduce(lambda x,y: x&y, exons)

1

Ich habe gerade eine nützliche Verwendung von reduce: Teilen von Zeichenfolgen gefunden, ohne das Trennzeichen zu entfernen . Der Code stammt vollständig aus dem Blog "Programmatisch sprechend". Hier ist der Code:

reduce(lambda acc, elem: acc[:-1] + [acc[-1] + elem] if elem == "\n" else acc + [elem], re.split("(\n)", "a\nb\nc\n"), [])

Hier ist das Ergebnis:

['a\n', 'b\n', 'c\n', '']

Beachten Sie, dass es Randfälle behandelt, die in SO nicht häufig beantwortet werden. Für eine ausführlichere Erklärung leite ich Sie zum ursprünglichen Blog-Beitrag weiter.


0

Verwenden Sie redu (), um herauszufinden, ob eine Liste von Daten aufeinanderfolgend ist:

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"
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.