Czy ktoś tutaj ma jakiś przydatny kod, który używa funkcji redukuj () w Pythonie? Czy istnieje kod inny niż zwykłe + i *, które widzimy w przykładach?
Zapoznaj się z losem redukuj () w Pythonie 3000 przez GvR
Czy ktoś tutaj ma jakiś przydatny kod, który używa funkcji redukuj () w Pythonie? Czy istnieje kod inny niż zwykłe + i *, które widzimy w przykładach?
Zapoznaj się z losem redukuj () w Pythonie 3000 przez GvR
Odpowiedzi:
Inne zastosowania, które znalazłem, oprócz + i *, były z i i lub, ale teraz mamy anyi allzastąpić te przypadki.
foldli foldrczęsto pojawiają się w Schemacie ...
Oto kilka uroczych zastosowań:
Spłaszcz listę
Cel: obrócić [[1, 2, 3], [4, 5], [6, 7, 8]]się [1, 2, 3, 4, 5, 6, 7, 8].
reduce(list.__add__, [[1, 2, 3], [4, 5], [6, 7, 8]], [])
Lista cyfr do numeru
Cel: obrócić [1, 2, 3, 4, 5, 6, 7, 8]się 12345678.
Brzydki, powolny sposób:
int("".join(map(str, [1,2,3,4,5,6,7,8])))
Ładny reducesposób:
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)trwa ~ 0,09 sekundy, podczas gdy 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)zajmuje 0,36 sekundy (około 4 razy wolniej). Zasadniczo mnożenie przez 10 staje się kosztowne, gdy lista staje się duża, podczas gdy int do str i konkatenacja pozostają tanie.
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)zajmuje 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)zajmuje 0,12 s, a konwersja cyfr na metodę str zajmuje 0,16 s.
reduce()można użyć do znalezienia najmniejszej wspólnej wielokrotności dla 3 lub więcej liczb :
#!/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)
Przykład:
>>> lcm(100, 23, 98)
112700
>>> lcm(*range(1, 20))
232792560
lcmw drugiej linii?
Znajdź przecięcie N podanych list:
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))
zwroty:
result = set([3, 4, 5])
Użycie tego, reducektóre znalazłem w moim kodzie, wiązało się z sytuacją, w której miałem pewną strukturę klas dla wyrażeń logicznych i musiałem przekonwertować listę tych obiektów wyrażeń na koniunkcję wyrażeń. Miałem już funkcję make_andtworzenia koniunkcji na podstawie dwóch wyrażeń, więc napisałem reduce(make_and,l). (Wiedziałem, że lista nie jest pusta; w przeciwnym razie byłoby to coś podobnego reduce(make_and,l,make_true)).
To jest dokładnie powód, dla którego (niektórzy) programiści funkcyjni lubią reduce(lub zwijają funkcje, jak zwykle nazywa się takie funkcje). Są to często już wiele binarne funkcje, takie jak +, *, min, max, łączenie i, w moim przypadku, make_andi make_or. Mającyreduce sprawia, że przenoszenie tych operacji do list (lub drzew lub czegokolwiek innego, ogólnie dla funkcji fold) jest trywialne.
Oczywiście, jeśli sumczęsto używane są pewne instancje (takie jak ), nie chcesz dalej pisać reduce. Jednak zamiast zdefiniowaniu sumz jakiegoś dla pętli, to można równie łatwo zdefiniować ją reduce.
Czytelność, jak wspominali inni, jest rzeczywiście problemem. Można jednak argumentować, że jedynym powodem, dla którego ludzie uważają, że jest reducemniej „przejrzysty”, jest fakt, że nie jest to funkcja, którą wiele osób zna i / lub używa.
andoperatora w zwarciu : L and reduce(make_and, L)jeśli zwrócenie pustej listy jest w tym przypadku właściwe
Kompozycja funkcji : jeśli masz już listę funkcji, które chcesz kolejno zastosować, na przykład:
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]
Następnie możesz zastosować je wszystkie kolejno za pomocą:
>>> 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'
W takim przypadku łańcuch metod może być bardziej czytelny. Ale czasami nie jest to możliwe, a tego rodzaju kompozycja może być bardziej czytelna i łatwiejsza w utrzymaniu niż f1(f2(f3(f4(x))))rodzaj składni.
Możesz zamienić na value = json_obj['a']['b']['c']['d']['e']:
value = reduce(dict.__getitem__, 'abcde', json_obj)
Jeśli masz już ścieżkę a/b/c/..w postaci listy. Na przykład Zmień wartości w dyktach zagnieżdżonych poleceń przy użyciu elementów na liście .
@Blair Conrad: Możesz także zaimplementować swoją glob / redukuj używając sum, na przykład:
files = sum([glob.glob(f) for f in args], [])
Jest to mniej rozwlekłe niż którykolwiek z twoich dwóch przykładów, jest doskonale Pythonowe i nadal zawiera tylko jedną linię kodu.
Odpowiadając na pierwotne pytanie, osobiście staram się unikać korzystania z funkcji redukuj, ponieważ nigdy nie jest to konieczne i uważam, że jest to mniej jasne niż inne podejścia. Jednak niektórzy ludzie przyzwyczaili się do redukcji i wolą listy składanych (szczególnie programiści Haskell). Ale jeśli jeszcze nie myślisz o problemie związanym z redukcją, prawdopodobnie nie musisz się martwić o jego użycie.
sumi reduceprowadzą do zachowania kwadratowego. Można to zrobić w czasie liniowym: files = chain.from_iterable(imap(iglob, args)). Chociaż prawdopodobnie nie ma to znaczenia w tym przypadku, ponieważ glob () potrzebuje czasu, aby uzyskać dostęp do dysku.
reduce może służyć do obsługi wyszukiwania połączonych atrybutów:
reduce(getattr, ('request', 'user', 'email'), self)
Oczywiście jest to równoważne z
self.request.user.email
ale jest to przydatne, gdy kod musi akceptować dowolną listę atrybutów.
(Połączone atrybuty o dowolnej długości są powszechne w przypadku modeli Django).
reducejest przydatna, gdy trzeba znaleźć sumę lub część setwspólną sekwencji podobnych obiektów.
>>> reduce(operator.or_, ({1}, {1, 2}, {1, 3})) # union
{1, 2, 3}
>>> reduce(operator.and_, ({1}, {1, 2}, {1, 3})) # intersection
{1}
(Oprócz rzeczywistych sets, przykładem są obiekty Q Django ).
Z drugiej strony, jeśli masz do czynienia z bools, powinieneś użyć anyi all:
>>> any((True, False, True))
True
Po grepowaniu kodu wydaje się, że jedyną rzeczą, do której użyłem redukcji, jest obliczenie silni:
reduce(operator.mul, xrange(1, x+1) or (1,))
Piszę funkcję redagowania dla języka, więc konstruuję funkcję skomponowaną za pomocą redukuj wraz z moim operatorem stosowania.
W skrócie, funkcja Compose pobiera listę funkcji do utworzenia w jedną funkcję. Jeśli mam złożoną operację, która jest wykonywana etapami, chcę to wszystko złożyć w następujący sposób:
complexop = compose(stage4, stage3, stage2, stage1)
W ten sposób mogę następnie zastosować to do wyrażenia takiego:
complexop(expression)
Chcę, aby było to równoważne z:
stage4(stage3(stage2(stage1(expression))))
Teraz, aby zbudować moje wewnętrzne obiekty, chcę powiedzieć:
Lambda([Symbol('x')], Apply(stage4, Apply(stage3, Apply(stage2, Apply(stage1, Symbol('x'))))))
(Klasa Lambda buduje funkcję zdefiniowaną przez użytkownika, a Apply buduje aplikację funkcji).
Teraz zmniejsz niestety fałdy w niewłaściwy sposób, więc skończyło się na użyciu z grubsza:
reduce(lambda x,y: Apply(y, x), reversed(args + [Symbol('x')]))
Aby dowiedzieć się, co redukuje produkuje, wypróbuj te w 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)do generowania wszystkich możliwych kombinacji funkcji do testowania wydajności.
Redukcja może być użyta do uzyskania listy z maksymalnym n-tym elementem
reduce(lambda x,y: x if x[2] > y[2] else y,[[1,2,3,4],[5,2,5,7],[1,6,0,2]])
zwróci [5, 2, 5, 7], ponieważ jest to lista z maksymalnie trzecim elementem +
Zmniejsz nie ogranicza się do operacji skalarnych; może być również używany do sortowania rzeczy do wiader. (Do tego najczęściej używam redukuj).
Wyobraź sobie przypadek, w którym masz listę obiektów i chcesz uporządkować ją hierarchicznie na podstawie właściwości przechowywanych płasko w obiekcie. W poniższym przykładzie tworzę listę obiektów metadanych związanych z artykułami w gazecie zakodowanej w formacie XML z articlesfunkcją. articlesgeneruje listę elementów XML, a następnie odwzorowuje je jeden po drugim, tworząc obiekty zawierające interesujące informacje na ich temat. W pierwszej kolejności chcę pozwolić użytkownikowi przeglądać artykuły według sekcji / podsekcji / nagłówka. Dlatego używam reducelisty artykułów i zwracam pojedynczy słownik, który odzwierciedla hierarchię sekcji / podsekcji / artykułów.
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, {})
Podaję tutaj obie funkcje, ponieważ myślę, że pokazuje, jak mapowanie i redukcja mogą ładnie się uzupełniać w przypadku obiektów. To samo można by było osiągnąć za pomocą pętli for, ale spędzanie trochę czasu z językiem funkcjonalnym skłaniało mnie do myślenia w kategoriach mapy i redukcji.
Nawiasem mówiąc, jeśli ktoś ma lepszy sposób ustawiania właściwości, jak ja, w extractprzypadku gdy rodzice nieruchomości, którą chcesz ustawić, mogą jeszcze nie istnieć, daj mi znać.
Nie jestem pewien, czy tego szukasz, ale możesz wyszukiwać kod źródłowy w Google .
Skorzystaj z linku do wyszukiwania „function: zredukuj () lang: python” w wyszukiwarce Google Code
Na pierwszy rzut oka poniższe projekty wykorzystują reduce()
itd. itd., ale nie jest to wcale zaskakujące, ponieważ są to ogromne projekty.
Funkcjonalność redukcji można wykonać za pomocą rekurencji funkcji, co, jak sądzę, Guido uważał za bardziej wyraźne.
Aktualizacja:
Ponieważ wyszukiwanie kodu Google zostało przerwane 15 stycznia 2012 r., Oprócz powrotu do zwykłych wyszukiwań w Google, istnieje coś, co nazywa się kolekcją fragmentów kodu, co wygląda obiecująco. Szereg innych zasobów jest wymienionych w odpowiedziach na to (zamknięte) pytanie Zastąpienie Google Code Search? .
Aktualizacja 2 (29 maja 2017):
Dobrym źródłem przykładów Pythona (w kodzie open source) jest wyszukiwarka Nullege .
forpętli.
lang:python "reduce("spowoduje znalezienie definicji reducezależności od stylu kodowania kodu źródłowego.
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']
# )},
# [])
#},
#[])
Kiedyś reduce konkatenowałem listę wektorów wyszukiwania PostgreSQL z ||operatorem w 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)
Mam starą implementację pipegrep w Pythonie, która używa reduktora i modułu glob do tworzenia listy plików do przetworzenia:
files = []
files.extend(reduce(lambda x, y: x + y, map(glob.glob, args)))
Wydawało mi się wtedy przydatne, ale naprawdę nie jest to konieczne, ponieważ coś podobnego jest równie dobre i prawdopodobnie bardziej czytelne
files = []
for f in args:
files.extend(glob.glob(f))
files = [glob.glob(f) for f in args]
itertools, korzystając z flatten()przepisu z docs.python.org/library/itertools.html , a następnie napisać: files = flatten(glob.glob(f) for f in args) (I tym razem przetestowałem kod przed wysłaniem i wiem, że działa poprawnie.)
files = chain.from_iterable(imap(iglob, args))gdzie chain, imappochodzą z itertoolsmodułu i glob.iglobjest przydatne, jeśli wzorzec z argsmoże dawać pliki z kilku katalogów.
Powiedzmy, że istnieje kilka rocznych danych statystycznych przechowywanych na liście Liczników. Chcemy znaleźć wartości MIN / MAX w każdym miesiącu w różnych latach. Na przykład w styczniu będzie to 10. A w lutym będzie to 15. Musimy przechowywać wyniki w nowym liczniku.
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
Mam obiekty reprezentujące pewnego rodzaju nakładające się interwały (egzony genomowe) i przedefiniowałem ich przecięcie za pomocą __and__:
class Exon:
def __init__(self):
...
def __and__(self,other):
...
length = self.length + other.length # (e.g.)
return self.__class__(...length,...)
Następnie, kiedy mam ich kolekcję (na przykład w tym samym genie), używam
intersection = reduce(lambda x,y: x&y, exons)
Właśnie znalazłem przydatne użycie reduce: dzielenie ciągu bez usuwania ogranicznika . Kod w całości pochodzi z bloga Programatically Speaking. Oto kod:
reduce(lambda acc, elem: acc[:-1] + [acc[-1] + elem] if elem == "\n" else acc + [elem], re.split("(\n)", "a\nb\nc\n"), [])
Oto wynik:
['a\n', 'b\n', 'c\n', '']
Zauważ, że obsługuje skrajne przypadki, których popularna odpowiedź w SO nie. Aby uzyskać bardziej szczegółowe wyjaśnienia, przekierowuję do oryginalnego wpisu na blogu.
Korzystanie z redukuj (), aby sprawdzić, czy lista dat jest następująca po sobie:
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 reducepozwala temu samemu kodowi działać zarówno w Pythonie 2, jak i 3.