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
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
Antworten:
Die anderen Verwendungen, die ich neben + und * dafür gefunden habe, waren mit und und oder, aber jetzt haben any
und müssen wir all
diese Fälle ersetzen.
foldl
und foldr
kommen 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 reduce
Weg:
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)
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.
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.
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
lcm
in der zweiten Zeile?
Finden Sie den Schnittpunkt von N gegebenen Listen:
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))
kehrt zurück:
result = set([3, 4, 5])
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_and
und make_or
. Mit a reduce
ist 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 sum
mit 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 reduce
weniger "klar" finden, darin besteht, dass es sich nicht um eine Funktion handelt, die viele Menschen kennen und / oder nutzen.
and
Bedieners ausnutzen : L and reduce(make_and, L)
Wenn die Rückgabe einer leeren Liste in diesem Fall angemessen ist
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.
Sie könnten ersetzen value = json_obj['a']['b']['c']['d']['e']
durch:
value = reduce(dict.__getitem__, 'abcde', json_obj)
Wenn Sie den Pfad bereits a/b/c/..
als Liste haben. Ändern Sie beispielsweise die Werte im Diktat verschachtelter Diktate mithilfe von Elementen in einer Liste .
@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.
sum
und reduce
fü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.
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.)
reduce
ist 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 set
s sind ein Beispiel dafür Djangos Q-Objekte .)
Auf der anderen Seite, wenn Sie es zu tun bool
s, sollten Sie verwenden any
und all
:
>>> any((True, False, True))
True
Nach dem Durchsuchen meines Codes scheint das einzige, wofür ich Reduzieren verwendet habe, die Berechnung der Fakultät zu sein:
reduce(operator.mul, xrange(1, x+1) or (1,))
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)))
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.
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
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 articles
Funktion eine Liste von Metadatenobjekten, die sich auf Artikel in einer XML-codierten Zeitung beziehen . articles
generiert 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 reduce
die 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.
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()
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 .
for
Schleife.
lang:python "reduce("
werden Definitionen gefunden, die vom reduce
Codierungsstil des Quellcodes abhängen.
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']
# )},
# [])
#},
#[])
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)
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))
files = [glob.glob(f) for f in args]
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.)
files = chain.from_iterable(imap(iglob, args))
where chain
, imap
are from itertools
module und glob.iglob
ist nützlich, wenn ein Muster von args
Dateien aus mehreren Verzeichnissen ergeben kann.
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
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)
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.
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"
from functools import reduce
ermöglicht den gleichen Code auf Python 2 und 3.