Przydatny kod, w którym jest używana funkcja edit ()? [Zamknięte]

123

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

cnu
źródło
1
from functools import reducepozwala temu samemu kodowi działać zarówno w Pythonie 2, jak i 3.
jfs

Odpowiedzi:

66

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)
Claudiu
źródło
23
Do spłaszczenia listy wolę listę (itertools.chain (* nested_list))
Roberto Bonvallet
13
suma ([[1, 2, 3], [4, 5], [6, 7, 8]], [])
Gordon Wrigley,
3
Jest to również przydatne w przypadku operacji bitowych. A co jeśli chcesz wziąć bitowe lub kilka liczb, na przykład jeśli chcesz przekonwertować flagi z listy na maskę bitową?
Antymon
6
W przypadku niektórych testów porównawczych „brzydki” sposób jest szybszy w przypadku dużych list. 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.
dr jimbob
3
Oczywiście tak dla małych list (rozmiar 10), wtedy metoda redukcji jest 1,3 razy szybsza. Jednak nawet w tym przypadku uniknięcie redukcji i wykonanie prostej pętli jest jeszcze szybsze 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.
dr jimbob
51

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
jfs
źródło
1
Co znajduje się lcmw drugiej linii?
beardc
1
@BirdJaguarIV: kliknij link w odpowiedzi. lcm()zwraca najmniejszą wspólną wielokrotność dwóch liczb.
jfs
39

reduce()może być użyty do rozpoznawania nazw z kropkami (gdzie eval()jest zbyt niebezpieczne):

>>> import __main__
>>> reduce(getattr, "os.path.abspath".split('.'), __main__)
<function abspath at 0x009AB530>
jfs
źródło
12

Myślę, że redukcja to głupie polecenie. W związku z tym:

reduce(lambda hold,next:hold+chr(((ord(next.upper())-65)+13)%26+65),'znlorabggbbhfrshy','')
Chris X
źródło
1
Podoba mi się też ironia
Roman
11

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.

mweerden
źródło
aby zabezpieczyć się przed pustą listą, możesz wykorzystać zachowanie andoperatora w zwarciu : L and reduce(make_and, L)jeśli zwrócenie pustej listy jest w tym przypadku właściwe
jfs
9

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.

beardc
źródło
1
Zaletą jest to, że możesz zmienić listę funkcji do zastosowania w kodzie.
hakanc
7

@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.

Eli Courtwright
źródło
2
Obie 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.
jfs
6

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

Jian
źródło
4

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
Jian
źródło
3

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,))
Tomi Kyöstilä
źródło
5
Następnie zamień go na math.factorial docs.python.org/2/library/math.html#math.factorial
pihentagy
3

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)))
ben
źródło
Użyłem 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.
jfs
3

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 +

Sidharth C. Nadhan
źródło
max (lst, key = lambda x: x [2])
aoeu256
3

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ć.

tborg
źródło
3

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

  • MoinMoin
  • Zope
  • Numeryczne
  • ScientificPython

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 .

Brendan
źródło
1
„Funkcjonalność redukcji można zrealizować za pomocą rekursji funkcji” ... Lub forpętli.
Jason Orendorff
2
Ponadto wyszukanie metody define () daje w wyniku projekty, które definiują funkcje redukujące w swoim kodzie. Należy szukać lang: pyton „reduce (” aby znaleźć rzeczywiste zwyczaje wbudowanej funkcji.
Seun Osewa
@Seun Osewa: Nawet wyszukiwanie lang:python "reduce("spowoduje znalezienie definicji reducezależności od stylu kodowania kodu źródłowego.
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']
#     )},
#     [])
#},
#[])
Aleksei astynax Pirogov
źródło
1
Czy mógłbyś dodać trochę wyjaśnienia, co się tutaj dzieje? W przeciwnym razie użyteczność nie jest wcale oczywista.
Zoran Pavlovic
2
def dump(fname,iterable):
  with open(fname,'w') as f:
    reduce(lambda x, y: f.write(unicode(y,'utf-8')), iterable)
deddu
źródło
2

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)
bjmc
źródło
1

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))
Blair Conrad
źródło
A co ze zrozumieniem listy? Wydaje się, że to idealna aplikacja do tego: files = [glob.glob(f) for f in args]
steveha
Właściwie @steveha, twój przykład da w wyniku listę list rozszerzonych globów, a nie płaską listę wszystkich elementów, które pasują do globów, ale możesz użyć wyrażenia listy + sumy, jako @ [Eli Courtwright] (# 16198 ) zwraca uwagę.
Blair Conrad
1
Okay, masz rację, przepraszam za to. Nadal nie podoba mi się kombinacja rozszerzania / zmniejszania / lambda / mapy! Poleciłbym zaimportować 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.)
steveha
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.
jfs
1

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
lessthanl0l
źródło
1

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)
JulienD
źródło
1

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.

MatthewRock
źródło
0

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"
lessthanl0l
źródło