Azalt () kullanan yararlı kod? [kapalı]


123

Python'da less () işlevini kullanan herhangi bir yararlı koda sahip olan var mı? Örneklerde gördüğümüz normal + ve * dışında herhangi bir kod var mı?

Bkz Python 3000) (kaderin azaltmak GvR tarafından


1
from functools import reduceaynı kodun hem Python 2 hem de 3 üzerinde çalışmasına izin verir.
jfs

Yanıtlar:


66

+ Ve * dışında bulduğum diğer kullanımlar, ve ve veya ile idi, ancak şimdi bu durumlara sahibiz anyve allbunları değiştirmeliyiz.

foldlve foldrScheme'de sık sık ortaya çıkıyor ...

İşte bazı sevimli kullanımlar:

Listeyi düzleştir

Hedef: [[1, 2, 3], [4, 5], [6, 7, 8]]dönüşmek [1, 2, 3, 4, 5, 6, 7, 8].

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

Bir numaranın rakam listesi

Hedef: [1, 2, 3, 4, 5, 6, 7, 8]dönüşmek 12345678.

Çirkin, yavaş yol:

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

Güzel reduceyol:

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

23
Bir listeyi düzleştirmek için listeyi tercih ederim (itertools.chain (* nested_list))
Roberto Bonvallet

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

3
Bitsel işlemler için de kullanışlıdır. Ya bitsel veya bir grup sayıyı almak istiyorsanız, örneğin bayrakları bir listeden bir bit maskesine dönüştürmeniz gerekiyorsa?
Antimon

6
Büyük listeler için bazı karşılaştırmalar yaparak 'çirkin' yol daha hızlıdır. timeit.repeat('int("".join(map(str, digit_list)))', setup = 'digit_list = list(d%10 for d in xrange(1,1000))', number=1000)~ 0.09 saniye 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)sürer, 0.36 saniye sürer (yaklaşık 4 kat daha yavaş). Temelde 10 ile çarpma, liste büyüdüğünde pahalı hale gelirken, int'den str'ye ve birleştirme ucuz kalır.
dr jimbob

3
Kabul edildiğinde, küçük listeler için (boyut 10) evet, bu durumda azaltma yöntemi 1.3 kat daha hızlıdır. Bununla birlikte, bu durumda bile, azaltmadan kaçınmak ve basit bir döngü yapmak daha da hızlıdır 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 sn, 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 sn ve basamakları str yöntemine dönüştürmek 0,16 sn sürmektedir.
dr jimbob

51

reduce()3 veya daha fazla sayı için En az ortak katı bulmak için kullanılabilir :

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

Misal:

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

1
Nedir lcmikinci satırında?
Beardc

1
@ BirdJaguarIV: cevaptaki bağlantıyı takip edin . lcm()iki sayının en küçük ortak katını döndürür.
jfs

39

reduce()noktalı isimleri çözmek için kullanılabilir (burada eval()kullanmak çok güvenli değildir):

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


12

Azaltmanın aptalca bir emir olduğunu düşünüyorum. Dolayısıyla:

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

1
Buradaki ironiyi de seviyorum
Roman

11

Kodumda reducebulduğum kullanım, mantık ifadesi için bazı sınıf yapısına sahip olduğum durumu içeriyordu ve bu ifade nesnelerinin bir listesini ifadelerin bir birleşimine dönüştürmem gerekiyordu. Zaten make_andiki ifade verilen bir birleşim yaratmak için bir fonksiyonum vardı , bu yüzden yazdım reduce(make_and,l). (Listenin boş olmadığını biliyordum; aksi takdirde şöyle bir şey olurdureduce(make_and,l,make_true) .)

Bu tam olarak (bazı) işlevsel programcıların benzer reduce(veya bu tür işlevler genellikle çağrıldığı için işlevleri katlama ) nedenidir . Orada gibi zaten birçok ikili fonksiyonlar genellikle +, *, min, max, birleştirmek ve benim durumumda, make_andve make_or. Sahip olmakreduce bu işlemleri listelere (veya ağaçlara veya genel olarak katlama işlevleri için olduğunuz her şeye) kaldırmayı önemsiz hale getirir.

Elbette sum, sık sık bazı örnekler (örneğin ) kullanılıyorsa, yazmaya devam etmek istemezsiniz reduce. Ancak, bunun yerine tanımlanması sumbazıları için-döngü ile, sen yapabilirsiniz aynı kolaylıkla ile tanımlamak reduce.

Başkalarının da belirttiği gibi okunabilirlik gerçekten bir sorundur. Ancak, insanların reducedaha az "net" bulmalarının tek sebebinin , bunun birçok insanın bildiği ve / veya kullandığı bir işlev olmaması olduğunu iddia edebilirsiniz.


boş listeye karşı korunmak için andoperatörün kısa devre davranışından yararlanabilirsiniz : L and reduce(make_and, L)bu durumda boş listenin geri
döndürülmesi

9

İşlev bileşimi : Art arda uygulamak istediğiniz işlevlerin bir listesine zaten sahipseniz, örneğin:

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]

Daha sonra hepsini arka arkaya uygulayabilirsiniz:

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

Bu durumda, yöntem zincirleme daha okunaklı olabilir. Ancak bazen bu mümkün değildir ve bu tür bir kompozisyon, bir f1(f2(f3(f4(x))))tür sözdiziminden daha okunabilir ve sürdürülebilir olabilir .


1
Bir avantaj, koda uygulanacak işlevler listesini değiştirebilmenizdir.
hakanc


7

@Blair Conrad: Glob'unuzu / azaltmanızı toplamı kullanarak da uygulayabilirsiniz, örneğin:

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

Bu, iki örnekten herhangi birinden daha az ayrıntılı, mükemmel bir şekilde Pythonic ve hala yalnızca bir kod satırı.

Bu nedenle, orijinal soruyu yanıtlamak için, kişisel olarak azaltmayı kullanmaktan kaçınmaya çalışıyorum çünkü asla gerçekten gerekli değil ve diğer yaklaşımlardan daha az net buluyorum. Ancak, bazı insanlar anlamaları listelemeye alışıyor ve bunu tercih ediyor (özellikle Haskell programcıları). Ancak, azaltma açısından bir sorun hakkında zaten düşünmüyorsanız, muhtemelen onu kullanma konusunda endişelenmenize gerek yoktur.


2
Hem sumve reducekuadratik davranışa yol açar. Bu doğrusal zamanda yapılabilir: files = chain.from_iterable(imap(iglob, args)). Glob () 'un bir diske erişmesi için geçen süre nedeniyle bu durumda muhtemelen önemi yoktur.
jfs

6

reduce zincirleme öznitelik aramalarını desteklemek için kullanılabilir:

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

Tabii ki bu eşdeğerdir

self.request.user.email

ancak kodunuzun rastgele bir öznitelik listesini kabul etmesi gerektiğinde kullanışlıdır.

(Django modelleriyle uğraşırken rastgele uzunlukta zincirli öznitelikler yaygındır.)


4

reducebenzer setnesneler dizisinin birleşimini veya kesişimini bulmanız gerektiğinde kullanışlıdır .

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

(Gerçek ' setler dışında bunlara bir örnek Django'nun Q nesneleridir. .)

Öte yandan, bools ile uğraşıyorsanız , anyve kullanmalısınız all:

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


3

Bir dil için bir oluşturma işlevi yazıyorum, bu nedenle oluşturulan işlevi, uygulama operatörümle birlikte azaltmayı kullanarak oluşturuyorum.

Özetle, compose tek bir işlevde oluşturmak için işlevlerin bir listesini alır. Aşamalı olarak uygulanan karmaşık bir işlemim varsa hepsini bir araya getirmek istiyorum:

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

Bu şekilde, bunu şöyle bir ifadeye uygulayabilirim:

complexop(expression)

Ve şuna eşdeğer olmasını istiyorum:

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

Şimdi, iç nesnelerimi oluşturmak için şunu söylemesini istiyorum:

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

(Lambda sınıfı, kullanıcı tanımlı bir işlev oluşturur ve Apply, bir işlev uygulaması oluşturur.)

Şimdi, azaltın, maalesef yanlış şekilde katlayın, bu yüzden kabaca şunu kullanarak yaralandım:

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

Hangi azalmanın ürettiğini bulmak için, REPL'de şunları deneyin:

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

Kullandığım compose = lambda *func: lambda arg: reduce(lambda x, f: f(x), reversed(funcs), arg)için performans testleri için fonksiyonlar tüm olası kombinasyonları oluşturmak.
jfs

3

azaltma, maksimum n'inci elemanı içeren listeyi elde etmek için kullanılabilir

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

en fazla 3. eleman içeren liste olduğu için [5, 2, 5, 7] döndürür +


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

3

Azaltma, skaler işlemlerle sınırlı değildir; şeyleri kovalara ayırmak için de kullanılabilir. (Bu, en sık kullandığım şeydir).

Bir nesne listesine sahip olduğunuz ve nesnede düz olarak depolanan özelliklere dayalı olarak bunu hiyerarşik olarak yeniden düzenlemek istediğiniz bir durum düşünün. Aşağıdaki örnekte, articlesişlevle XML kodlu bir gazetedeki makalelerle ilgili meta veri nesnelerinin bir listesini oluşturuyorum . articlesXML öğelerinin bir listesini oluşturur ve ardından bunların içinden birer birer eşleyerek, kendileriyle ilgili ilginç bilgiler içeren nesneler üretir. Ön uçta, kullanıcının makaleleri bölüm / alt bölüm / başlığa göre taramasına izin vermek istiyorum. Bu yüzden reducemakalelerin listesini alıp bölüm / alt bölüm / makale hiyerarşisini yansıtan tek bir sözlüğü döndürmek için kullanıyorum.

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

Burada her iki işlevi de veriyorum çünkü bu, eşleme ve azaltmanın nesnelerle uğraşırken birbirlerini nasıl güzel bir şekilde tamamlayabileceğini gösterdiğini düşünüyorum. Aynı şey bir for döngüsü ile de başarılabilirdi, ama işlevsel bir dille ciddi zaman geçirmek, beni harita açısından düşünmeye ve azaltmaya yöneltti.

Bu arada, benim yaptığım gibi mülkleri ayarlamanın daha iyi bir yolu varsa, ayarlamak extractistediğiniz mülkün ebeveynlerinin henüz mevcut olmadığı durumlarda lütfen bana bildirin.


3

Peşinde olduğun şeyin bu olup olmadığından emin değilim ama Google'da kaynak kodunu arayabilirsin .

'Function: below () lang: python' ile ilgili arama için bağlantıyı takip edinGoogle Code aramasında ilgili

İlk bakışta aşağıdaki projeler şunu kullanır: reduce()

  • MoinMoin
  • Zope
  • Sayısal
  • scientificpython

vb. ama sonra bunlar büyük projeler oldukları için pek şaşırtıcı değil.

Azaltma işlevselliği, Guido'nun daha açık bulduğunu düşündüğüm işlev özyineleme kullanılarak yapılabilir.

Güncelleme:

Google'ın Kod Arama 15 Ocak 2012'de sona erdiğinden, normal Google aramalarına geri dönmenin yanı sıra, umut verici görünen Kod Parçacıkları Koleksiyonu adlı bir şey var . Bu (kapalı) sorunun yanıtlarında bir dizi başka kaynaktan bahsedilmiştir Google Code Search’ün Değiştirilmesi?.

Güncelleme 2 (29-Mayıs-2017):

Python örnekleri için iyi bir kaynak (açık kaynak kodunda) Nullege arama motorudur .


1
"Azaltma işlevselliği, işlev özyineleme kullanılarak yapılabilir" ... Veya bir fordöngü.
Jason Orendorff

2
Ayrıca, düşürmek () için arama yapmak, kodlarında azaltma işlevlerini tanımlayan projeler verir.
Yerleşik işlevin

@Seun Osewa: Aramanız bile kaynak kod kodlama stiline bağlı olarak lang:python "reduce("tanımları bulacaktır reduce.
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
Burada neler olduğuna dair küçük bir açıklama ekleyebilir misiniz? Aksi takdirde, yararlılık gerçekten hiç açık değildir.
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

Ben kullanılan reduce PostgreSQL arama vektörlerin bir listesini bitiştirmek ile ||sqlalchemy aranabilir operatör:

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

İşlenecek dosyaların bir listesini oluşturmak için azaltma ve glob modülünü kullanan eski bir pipegrep Python uygulamasına sahibim :

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

O sırada kullanışlı buldum, ancak gerçekten gerekli değil, çünkü benzer bir şey aynı derecede iyi ve muhtemelen daha okunabilir

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

Bir liste anlayışına ne dersiniz? Bu, bunun için mükemmel bir uygulama gibi görünüyor: files = [glob.glob(f) for f in args]
steveha

Aslında, @steveha, örneğiniz kürelerle eşleşen tüm öğelerin düz bir listesi yerine genişletilmiş kürelerden oluşan bir listeyle sonuçlanacaktır, ancak @ [Eli Courtwright] (# 16198) olarak bir liste anlama + toplam kullanabilirsiniz ) işaret.
Blair Conrad

1
Tamam, haklısın, bunun için üzgünüm. Uzat / azalt / lambda / harita kombinasyonunu hala pek sevmiyorum! Docs.python.org/library/itertools.html'deki tarifi itertoolskullanarak içe aktarmanızı ve ardından şunu yazmanızı öneririm : (Ve bu sefer kodu göndermeden önce test ettim ve bunun doğru çalıştığını biliyorum.)flatten()files = flatten(glob.glob(f) for f in args)
steveha

files = chain.from_iterable(imap(iglob, args))nerede chain, imapgelmektedir itertoolsmodül ve glob.iglobbir desen halinde yararlıdır argsbirkaç dizinleri dosyaları oluşturabilir.
jfs

1

Sayaçların bir listesi olarak saklanan bazı yıllık istatistik verileri olduğunu varsayalım. MİN / MAKS değerlerini farklı yıllarda her ay bulmak istiyoruz. Örneğin, Ocak için 10, Şubat için 15 olacaktır. Sonuçları yeni bir Sayaçta saklamamız gerekir.

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

Bir tür örtüşen aralıkları (genomik eksonlar) temsil eden nesnelerim var ve bunları kullanarak kesişimlerini yeniden tanımladım __and__:

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

Sonra onlardan bir koleksiyonum olduğunda (örneğin, aynı gende), kullanıyorum

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

1

Az önce reduce: ayırıcıyı kaldırmadan dizeyi ayırmanın yararlı kullanımını buldum . Kod tamamen Programatik Olarak Konuşma blogundan alınmıştır. İşte kod:

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

İşte sonuç:

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

SO'daki popüler yanıtın sahip olmadığı uç durumları ele aldığına dikkat edin. Daha ayrıntılı açıklama için sizi orijinal blog gönderisine yönlendiriyorum.


0

Bir tarih listesinin ardışık olup olmadığını öğrenmek için azaltmak () kullanın:

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"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.