Menggunakan peta python dan alat fungsional lainnya


127

Ini cukup n00bish, tapi saya mencoba belajar / memahami pemrograman fungsional dengan python. Kode berikut:

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3]

def maptest(foo, bar):
    print foo, bar

map(maptest, foos, bars)

menghasilkan:

1.0 1
2.0 2
3.0 3
4.0 None
5.0 None

P. Apakah ada cara untuk menggunakan peta atau alat fungsional lain dalam python untuk menghasilkan yang berikut tanpa loop dll.

1.0 [1,2,3]
2.0 [1,2,3]
3.0 [1,2,3]
4.0 [1,2,3]
5.0 [1,2,3]

Hanya sebagai catatan bagaimana implementasi akan berubah jika ada ketergantungan antara foo dan bar. misalnya

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3,4,5]

dan cetak:

1.0 [2,3,4,5]
2.0 [1,3,4,5]
3.0 [1,2,4,5]
...

PS: Saya tahu bagaimana melakukannya dengan naif menggunakan loop, dan / atau generator, tapi saya ingin belajar bagaimana mencapai hal yang sama dengan menggunakan alat fungsional. Apakah ini hanya kasus menambahkan pernyataan if ke maptest atau menerapkan peta filter lain ke bar secara internal di dalam maptest?


Terima kasih kawan Saya harus mengakui bahwa saya sedang mencoba mempelajari konsep pemrograman fungsional melalui python.

1
Sebuah tutorial yang bagus untuk itu di sini: dreamsyssoft.com/python-scripting-tutorial/…
Rocky Pulley

Jawaban:


54

Cara termudah adalah tidak melewati barsfungsi yang berbeda, tetapi untuk mengaksesnya langsung dari maptest:

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3]

def maptest(foo):
    print foo, bars

map(maptest, foos)

Dengan maptestfungsi asli Anda, Anda juga bisa menggunakan fungsi lambda di map:

map((lambda foo: maptest(foo, bars)), foos)

buruk ketika bar datang dari daftar
Phyo Arkar Lwin

59
Solusi ini secara langsung bertentangan dengan prinsip-prinsip pemrograman fungsional yang ingin dipelajari oleh OP. Aturan mendasar dalam pemrograman fungsional adalah bahwa setiap kali Anda memanggil fungsi dengan argumen yang sama, Anda SELALU mendapatkan output yang sama. Ini menghindari sarang serangga yang diperkenalkan dengan memiliki status global. Karena maptest tergantung pada definisi eksternal bar, prinsip ini rusak.
image_doctor

3
Dear Stack overflow, karena Anda suka menutup pertanyaan dan cukup banyak, mengapa Anda tidak menandai pertanyaan ini sebagai jawaban dan menandai respons yang benar sebagai jawaban? Salam, kita.
Bahadir Cambel

1
@ image_doctor, di FP itu sangat ok untuk mengakses konstanta global (ada yang menganggap sebagai nullary fuction)
Peter K

1
@BahadirCambel Stack Overflow moderasi mungkin kadang-kadang berat, tetapi tanda centang selalu, dan akan selalu, milik OP.
wizzwizz4

194

Apakah Anda terbiasa dengan bahasa fungsional lainnya? yaitu apakah Anda mencoba untuk mempelajari bagaimana python melakukan pemrograman fungsional, atau apakah Anda mencoba untuk belajar tentang pemrograman fungsional dan menggunakan python sebagai kendaraan?

Juga, apakah Anda memahami daftar?

map(f, sequence)

setara langsung (*) untuk:

[f(x) for x in sequence]

Bahkan, saya pikir map()pernah dijadwalkan untuk dihapus dari python 3.0 sebagai mubazir (itu tidak terjadi).

map(f, sequence1, sequence2)

sebagian besar setara dengan:

[f(x1, x2) for x1, x2 in zip(sequence1, sequence2)]

(ada perbedaan dalam cara menangani kasus di mana urutannya memiliki panjang yang berbeda. Seperti yang Anda lihat, map()mengisi Tidak ada ketika salah satu urutan habis, sedangkan zip()berhenti ketika urutan terpendek berhenti)

Jadi, untuk menjawab pertanyaan spesifik Anda, Anda mencoba menghasilkan hasilnya:

foos[0], bars
foos[1], bars
foos[2], bars
# etc.

Anda bisa melakukan ini dengan menulis fungsi yang mengambil satu argumen dan mencetaknya, diikuti oleh bilah:

def maptest(x):
     print x, bars
map(maptest, foos)

Atau, Anda dapat membuat daftar yang terlihat seperti ini:

[bars, bars, bars, ] # etc.

dan gunakan maptest asli Anda:

def maptest(x, y):
    print x, y

Salah satu cara untuk melakukan ini adalah membangun daftar secara eksplisit sebelumnya:

barses = [bars] * len(foos)
map(maptest, foos, barses)

Atau, Anda bisa menarik itertoolsmodul. itertoolsberisi banyak fungsi pintar yang membantu Anda melakukan pemrograman evaluasi gaya-malas dengan python. Dalam hal ini, kami ingin itertools.repeat, yang akan menampilkan argumennya tanpa batas saat Anda mengulanginya. Fakta terakhir ini berarti bahwa jika Anda melakukannya:

map(maptest, foos, itertools.repeat(bars))

Anda akan mendapatkan output tanpa akhir, karena map()terus berjalan selama salah satu argumen masih menghasilkan output. Namun, itertools.imapseperti map(), tetapi berhenti segera setelah iterable terpendek berhenti.

itertools.imap(maptest, foos, itertools.repeat(bars))

Semoga ini membantu :-)

(*) Agak berbeda dengan python 3.0. Di sana, map () pada dasarnya mengembalikan ekspresi generator.


Jadi, apakah saya memahami dengan benar bahwa, tidak seperti peta, itertools.imap(f, sequence1, sequence2)benar-benar setara dengan [f(x1, x2) for x1, x2 in zip(sequence1, sequence2)]?
Jon Coombs

Menguji sedikit, saya melihat bahwa ia mengembalikan objek itertools.imap, jadi mungkin ini akan lebih 'setara':list(itertools.imap(f, sequence1, sequence2))
Jon Coombs

Ini harus menjadi jawaban yang disetujui.
Rob Grant

30

Inilah solusi yang Anda cari:

>>> foos = [1.0, 2.0, 3.0, 4.0, 5.0]
>>> bars = [1, 2, 3]
>>> [(x, bars) for x in foos]
[(1.0, [1, 2, 3]), (2.0, [1, 2, 3]), (3.0, [1, 2, 3]), (4.0, [1, 2, 3]), (5.0, [
1, 2, 3])]

Saya akan merekomendasikan menggunakan pemahaman daftar ( [(x, bars) for x in foos]bagian) lebih dari menggunakan peta karena menghindari overhead panggilan fungsi pada setiap iterasi (yang bisa sangat signifikan). Jika Anda hanya akan menggunakannya dalam for for loop, Anda akan mendapatkan kecepatan yang lebih baik dengan menggunakan pemahaman generator:

>>> y = ((x, bars) for x in foos)
>>> for z in y:
...     print z
...
(1.0, [1, 2, 3])
(2.0, [1, 2, 3])
(3.0, [1, 2, 3])
(4.0, [1, 2, 3])
(5.0, [1, 2, 3])

Perbedaannya adalah bahwa pemahaman generator dimuat malas .

UPDATE Sebagai tanggapan terhadap komentar ini:

Tentu saja Anda tahu, bahwa Anda tidak menyalin bilah, semua entri adalah daftar bilah yang sama. Jadi, jika Anda memodifikasi salah satu dari mereka (termasuk bilah asli), Anda memodifikasi semuanya.

Saya kira ini adalah poin yang valid. Ada dua solusi untuk ini yang bisa saya pikirkan. Yang paling efisien mungkin adalah sesuatu seperti ini:

tbars = tuple(bars)
[(x, tbars) for x in foos]

Karena tupel tidak dapat diubah, ini akan mencegah bar dimodifikasi dari hasil pemahaman daftar ini (atau pemahaman generator jika Anda memilih rute itu). Jika Anda benar-benar perlu memodifikasi setiap hasil, Anda dapat melakukan ini:

from copy import copy
[(x, copy(bars)) for x in foos]

Namun, ini bisa sedikit mahal baik dalam hal penggunaan memori dan dalam kecepatan, jadi saya sarankan menentangnya kecuali jika Anda benar-benar perlu menambah masing-masing.


1
Tentu saja Anda tahu, bahwa Anda tidak menyalin bilah, semua entri adalah daftar bilah yang sama. Jadi, jika Anda memodifikasi salah satu dari mereka (termasuk bilah asli), Anda memodifikasi semuanya.
vartec

20

Pemrograman fungsional adalah tentang membuat kode bebas efek samping.

peta adalah abstraksi transformasi daftar fungsional. Anda menggunakannya untuk mengambil urutan sesuatu dan mengubahnya menjadi urutan sesuatu yang lain.

Anda mencoba menggunakannya sebagai iterator. Jangan lakukan itu. :)

Berikut adalah contoh bagaimana Anda dapat menggunakan peta untuk membangun daftar yang Anda inginkan. Ada beberapa solusi yang lebih pendek (saya hanya menggunakan pemahaman), tetapi ini akan membantu Anda memahami peta yang sedikit lebih baik:

def my_transform_function(input):
    return [input, [1, 2, 3]]

new_list = map(my_transform, input_list)

Perhatikan pada titik ini, Anda hanya melakukan manipulasi data. Sekarang Anda dapat mencetaknya:

for n,l in new_list:
    print n, ll

- Saya tidak yakin apa yang Anda maksud dengan 'tanpa loop.' fp bukan tentang menghindari loop (Anda tidak dapat memeriksa setiap item dalam daftar tanpa mengunjungi masing-masing). Ini tentang menghindari efek samping, sehingga lebih sedikit menulis bug.


12
>>> from itertools import repeat
>>> for foo, bars in zip(foos, repeat(bars)):
...     print foo, bars
... 
1.0 [1, 2, 3]
2.0 [1, 2, 3]
3.0 [1, 2, 3]
4.0 [1, 2, 3]
5.0 [1, 2, 3]

11
import itertools

foos=[1.0, 2.0, 3.0, 4.0, 5.0]
bars=[1, 2, 3]

print zip(foos, itertools.cycle([bars]))

Ini yang paling mudah dan fungsional dengan benar. terimalah ini sebagai jawaban
Phyo Arkar Lwin

1
Ini hanya kode. Tidak ada penjelasan. Banyak pengguna tidak mengerti apa arti jawaban ini. @PhyoArkarLwin
ProgramFast

6

Berikut ini ikhtisar parameter ke map(function, *sequences)fungsi:

  • function adalah nama fungsi Anda.
  • sequencesadalah sejumlah urutan, yang biasanya daftar atau tupel. mapakan mengulanginya secara bersamaan dan memberikan nilai saat ini function. Itu sebabnya jumlah urutan harus sama dengan jumlah parameter untuk fungsi Anda.

Kedengarannya seperti Anda mencoba untuk beralih ke beberapa functionparameter tetapi tetap membuat yang lain konstan, dan sayangnya maptidak mendukungnya. Saya menemukan proposal lama untuk menambahkan fitur seperti itu ke Python, tetapi konstruksinya sangat bersih dan mapan sehingga saya ragu sesuatu seperti itu akan pernah diimplementasikan.

Gunakan solusi seperti variabel global atau daftar pemahaman, seperti yang disarankan orang lain.


0

Apakah ini akan berhasil?

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3]

def maptest2(bar):
  print bar

def maptest(foo):
  print foo
  map(maptest2, bars)

map(maptest, foos)

1
Anda mungkin ingin memanggil param untuk maptest2 () sesuatu seperti 'bilah'. Singular barmenyiratkan itu menerima nilai berulang, ketika Anda benar-benar ingin seluruh daftar.
Nikhil Chelliah

1
Itu benar-benar menerima nilai berulang yang saya percaya.
Chris

0

Bagaimana dengan ini:

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3]

def maptest(foo, bar):
    print foo, bar

map(maptest, foos, [bars]*len(foos))
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.