Apakah ada orang di sini yang memiliki kode berguna yang menggunakan fungsi reduce () di python? Apakah ada kode selain + dan * biasa yang kita lihat di contoh?
Lihat Fate of reduce () dengan Python 3000 oleh GvR
Apakah ada orang di sini yang memiliki kode berguna yang menggunakan fungsi reduce () di python? Apakah ada kode selain + dan * biasa yang kita lihat di contoh?
Lihat Fate of reduce () dengan Python 3000 oleh GvR
Jawaban:
Kegunaan lain yang saya temukan untuk itu selain + dan * dulu dengan dan atau, tetapi sekarang kami memiliki any
dan all
mengganti kasing tersebut.
foldl
dan foldr
sering muncul di Skema ...
Inilah beberapa penggunaan lucu:
Ratakan daftar
Sasaran: berubah [[1, 2, 3], [4, 5], [6, 7, 8]]
menjadi [1, 2, 3, 4, 5, 6, 7, 8]
.
reduce(list.__add__, [[1, 2, 3], [4, 5], [6, 7, 8]], [])
Daftar digit angka
Sasaran: berubah [1, 2, 3, 4, 5, 6, 7, 8]
menjadi 12345678
.
Jelek, cara lambat:
int("".join(map(str, [1,2,3,4,5,6,7,8])))
reduce
Cara yang cantik :
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)
membutuhkan ~ 0,09 detik sementara 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)
membutuhkan 0,36 detik (sekitar 4 kali lebih lambat). Pada dasarnya perkalian dengan 10 menjadi mahal ketika daftarnya menjadi besar, sementara int ke str dan penggabungan tetap murah.
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)
membutuhkan 0,06 detik, 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)
membutuhkan 0,12 detik dan mengubah angka menjadi metode str membutuhkan 0,16 detik.
reduce()
dapat digunakan untuk mencari kelipatan persekutuan terkecil untuk 3 angka atau lebih :
#!/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)
Contoh:
>>> lcm(100, 23, 98)
112700
>>> lcm(*range(1, 20))
232792560
lcm
di baris kedua?
Temukan perpotongan N daftar yang diberikan:
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))
kembali:
result = set([3, 4, 5])
Penggunaan reduce
yang saya temukan dalam kode saya melibatkan situasi di mana saya memiliki beberapa struktur kelas untuk ekspresi logika dan saya perlu mengonversi daftar objek ekspresi ini ke sambungan ekspresi. Saya sudah memiliki fungsi make_and
untuk membuat hubungan dengan dua ekspresi, jadi saya menulis reduce(make_and,l)
. (Saya tahu daftarnya tidak kosong; jika tidak maka akan menjadi seperti ini reduce(make_and,l,make_true)
.)
Inilah alasan sebenarnya mengapa (beberapa) pemrogram fungsional menyukai reduce
(atau melipat fungsi, seperti fungsi biasanya disebut). Ada sering sudah banyak fungsi biner seperti +
, *
, min
, max
, Rangkaian dan, dalam kasus saya, make_and
dan make_or
. Memiliki a reduce
membuatnya mudah untuk mengangkat operasi ini ke daftar (atau pohon atau apa pun yang Anda dapatkan, untuk fungsi lipat secara umum).
Tentu saja, jika instansiasi tertentu (seperti sum
) sering digunakan, maka Anda tidak ingin terus menulis reduce
. Namun, alih-alih mendefinisikan sum
dengan beberapa for-loop, Anda dapat dengan mudah mendefinisikannya dengan reduce
.
Keterbacaan, seperti yang disebutkan oleh orang lain, memang menjadi masalah. Namun, Anda dapat berargumen bahwa satu-satunya alasan mengapa orang merasa reduce
kurang "jelas" adalah karena ini bukan fungsi yang diketahui dan / atau digunakan banyak orang.
and
operator: L and reduce(make_and, L)
jika mengembalikan daftar kosong sesuai dalam kasus ini
Komposisi fungsi : Jika Anda sudah memiliki daftar fungsi yang ingin Anda terapkan secara berurutan, seperti:
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]
Kemudian Anda dapat menerapkan semuanya secara berurutan dengan:
>>> 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'
Dalam kasus ini, rangkaian metode mungkin lebih mudah dibaca. Tetapi terkadang tidak mungkin, dan komposisi semacam ini mungkin lebih mudah dibaca dan dipelihara daripada f1(f2(f3(f4(x))))
sejenis sintaks.
Anda bisa mengganti value = json_obj['a']['b']['c']['d']['e']
dengan:
value = reduce(dict.__getitem__, 'abcde', json_obj)
Jika Anda sudah memiliki jalur a/b/c/..
sebagai daftar. Misalnya, Ubah nilai dalam dikt dari dicts bersarang menggunakan item dalam daftar .
@Blair Conrad: Anda juga bisa menerapkan glob / reduce menggunakan jumlah, seperti:
files = sum([glob.glob(f) for f in args], [])
Ini kurang bertele-tele daripada salah satu dari dua contoh Anda, Pythonic sempurna, dan masih hanya satu baris kode.
Jadi untuk menjawab pertanyaan awal, saya pribadi mencoba menghindari penggunaan pengurangan karena tidak pernah benar-benar diperlukan dan menurut saya kurang jelas dibandingkan pendekatan lainnya. Namun, beberapa orang terbiasa mengurangi dan memilihnya untuk daftar pemahaman (terutama programmer Haskell). Tetapi jika Anda belum memikirkan suatu masalah dalam hal pengurangan, Anda mungkin tidak perlu khawatir untuk menggunakannya.
sum
dan reduce
mengarah pada perilaku kuadrat. Hal ini dapat dilakukan dalam waktu linier: files = chain.from_iterable(imap(iglob, args))
. Meskipun mungkin tidak masalah dalam kasus ini karena waktu yang dibutuhkan untuk glob () mengakses disk.
reduce
dapat digunakan untuk mendukung pencarian atribut yang dirantai:
reduce(getattr, ('request', 'user', 'email'), self)
Tentu saja, ini sama dengan
self.request.user.email
tetapi berguna ketika kode Anda perlu menerima daftar atribut yang berubah-ubah.
(Atribut berantai dengan panjang sembarang adalah umum ketika berhadapan dengan model Django.)
reduce
berguna ketika Anda perlu menemukan penyatuan atau perpotongan urutan set
objek -seperti.
>>> reduce(operator.or_, ({1}, {1, 2}, {1, 3})) # union
{1, 2, 3}
>>> reduce(operator.and_, ({1}, {1, 2}, {1, 3})) # intersection
{1}
(Terlepas dari set
s sebenarnya , contohnya adalah objek Q Django .)
Di sisi lain, jika Anda berurusan dengan bool
s, Anda harus menggunakan any
dan all
:
>>> any((True, False, True))
True
Setelah grep kode saya, tampaknya satu-satunya hal yang saya gunakan untuk mengurangi adalah menghitung faktorial:
reduce(operator.mul, xrange(1, x+1) or (1,))
Saya sedang menulis fungsi compose untuk sebuah bahasa, jadi saya membuat fungsi compose menggunakan reduce bersama dengan operator apply saya.
Singkatnya, compose mengambil daftar fungsi untuk dibuat menjadi satu fungsi. Jika saya memiliki operasi kompleks yang diterapkan secara bertahap, saya ingin menggabungkan semuanya seperti ini:
complexop = compose(stage4, stage3, stage2, stage1)
Dengan cara ini, saya kemudian dapat menerapkannya ke ekspresi seperti ini:
complexop(expression)
Dan saya ingin itu setara dengan:
stage4(stage3(stage2(stage1(expression))))
Sekarang, untuk membangun objek internal saya, saya ingin mengatakan:
Lambda([Symbol('x')], Apply(stage4, Apply(stage3, Apply(stage2, Apply(stage1, Symbol('x'))))))
(Kelas Lambda membangun fungsi yang ditentukan pengguna, dan Apply membangun aplikasi fungsi.)
Sekarang, sayangnya, kurangi lipatan dengan cara yang salah, jadi saya akhirnya menggunakan, secara kasar:
reduce(lambda x,y: Apply(y, x), reversed(args + [Symbol('x')]))
Untuk mengetahui apa yang mengurangi produksi, coba ini di 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)
untuk menghasilkan semua kemungkinan kombinasi dari fungsi untuk pengujian kinerja.
mengurangi dapat digunakan untuk mendapatkan daftar dengan elemen ke maksimum
reduce(lambda x,y: x if x[2] > y[2] else y,[[1,2,3,4],[5,2,5,7],[1,6,0,2]])
akan mengembalikan [5, 2, 5, 7] karena ini adalah daftar dengan maks elemen ke-3 +
Mengurangi tidak terbatas pada operasi skalar; itu juga dapat digunakan untuk menyortir barang ke dalam ember. (Ini yang paling sering saya gunakan untuk mengurangi).
Bayangkan sebuah kasus di mana Anda memiliki daftar objek, dan Anda ingin menata ulang secara hierarki berdasarkan properti yang disimpan secara datar di objek. Dalam contoh berikut, saya membuat daftar objek metadata yang terkait dengan artikel di surat kabar berenkode XML dengan articles
fungsi tersebut. articles
menghasilkan daftar elemen XML, dan kemudian memetakannya satu per satu, menghasilkan objek yang menyimpan beberapa info menarik tentangnya. Di bagian depan, saya ingin membiarkan pengguna menelusuri artikel berdasarkan bagian / subbagian / tajuk utama. Jadi saya gunakan reduce
untuk mengambil daftar artikel dan mengembalikan satu kamus yang mencerminkan hierarki bagian / subbagian / artikel.
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, {})
Saya memberikan kedua fungsi di sini karena menurut saya ini menunjukkan bagaimana map dan reduce dapat saling melengkapi dengan baik ketika berhadapan dengan objek. Hal yang sama dapat dilakukan dengan for loop, ... tetapi menghabiskan waktu serius dengan bahasa fungsional cenderung membuat saya berpikir dalam kerangka peta dan mengurangi.
Ngomong-ngomong, jika ada yang memiliki cara yang lebih baik untuk menyetel properti seperti yang saya lakukan extract
, di mana orang tua properti yang ingin Anda setel mungkin belum ada, beri tahu saya.
Tidak yakin apakah ini yang Anda cari tetapi Anda dapat mencari kode sumber di Google .
Ikuti link untuk pencarian di 'function: reduce () lang: python' di pencarian Google Code
Sekilas penggunaan proyek berikut reduce()
dll. dll tetapi kemudian ini tidak mengejutkan karena mereka adalah proyek besar.
Fungsi pengurangan dapat dilakukan dengan menggunakan rekursi fungsi yang menurut saya Guido pikir lebih eksplisit.
Memperbarui:
Sejak Pencarian Kode Google dihentikan pada 15-Jan-2012, selain kembali ke pencarian Google biasa, ada sesuatu yang disebut Koleksi Cuplikan Kode yang terlihat menjanjikan. Sejumlah sumber lain disebutkan dalam menjawab pertanyaan (tertutup) ini tentang Penggantian untuk Google Code Search? .
Pembaruan 2 (29-Mei-2017):
Sumber yang baik untuk contoh Python (dalam kode sumber terbuka) adalah mesin pencari Nullege .
for
loop.
lang:python "reduce("
akan menemukan definisi reduce
tergantung pada gaya pengkodean kode sumber.
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']
# )},
# [])
#},
#[])
Saya biasa reduce
menggabungkan daftar vektor pencarian PostgreSQL dengan ||
operator di 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)
Saya memiliki implementasi pipegrep Python lama yang menggunakan reduce dan modul glob untuk membuat daftar file untuk diproses:
files = []
files.extend(reduce(lambda x, y: x + y, map(glob.glob, args)))
Saya merasa itu berguna pada saat itu, tetapi sebenarnya tidak perlu, karena sesuatu yang serupa sama baiknya, dan mungkin lebih mudah dibaca
files = []
for f in args:
files.extend(glob.glob(f))
files = [glob.glob(f) for f in args]
itertools
, menggunakan flatten()
resep dari docs.python.org/library/itertools.html , dan kemudian menulis: files = flatten(glob.glob(f) for f in args)
(Dan kali ini, saya menguji kode sebelum mempostingnya, dan saya tahu ini berfungsi dengan benar.)
files = chain.from_iterable(imap(iglob, args))
di mana chain
, imap
dari itertools
modul dan glob.iglob
berguna jika pola dari args
dapat menghasilkan file dari beberapa direktori.
Katakanlah ada beberapa data statistik tahunan yang menyimpan daftar Penghitung. Kami ingin menemukan nilai MIN / MAX setiap bulan di tahun yang berbeda. Misalnya, untuk Januari akan menjadi 10. Dan untuk Februari akan menjadi 15. Kami perlu menyimpan hasilnya di Penghitung baru.
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
Saya memiliki objek yang mewakili beberapa jenis interval yang tumpang tindih (ekson genom), dan mendefinisikan ulang persimpangannya menggunakan __and__
:
class Exon:
def __init__(self):
...
def __and__(self,other):
...
length = self.length + other.length # (e.g.)
return self.__class__(...length,...)
Kemudian ketika saya memiliki koleksi mereka (misalnya, dalam gen yang sama), saya gunakan
intersection = reduce(lambda x,y: x&y, exons)
Saya baru saja menemukan penggunaan berguna dari reduce
: memisahkan string tanpa menghapus pembatas . Kode sepenuhnya dari blog Programatically Speaking. Berikut kodenya:
reduce(lambda acc, elem: acc[:-1] + [acc[-1] + elem] if elem == "\n" else acc + [elem], re.split("(\n)", "a\nb\nc\n"), [])
Inilah hasilnya:
['a\n', 'b\n', 'c\n', '']
Perhatikan bahwa itu menangani kasus tepi yang jawaban populer di SO tidak. Untuk penjelasan lebih mendalam, saya mengarahkan Anda ke postingan blog asli.
Menggunakan reduce () untuk mengetahui apakah daftar tanggal berurutan:
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
memungkinkan kode yang sama untuk bekerja pada Python 2 dan 3.