Python haritası ve diğer işlevsel araçları kullanma


127

Bu oldukça anlamsız, ancak python'da işlevsel programlamayı öğrenmeye / anlamaya çalışıyorum. Aşağıdaki kod:

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)

üretir:

1.0 1
2.0 2
3.0 3
4.0 None
5.0 None

S. Aşağıdakileri döngüler olmadan üretmek için python'da map veya diğer işlevsel araçları kullanmanın bir yolu var mı?

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]

Bir yan not olarak, foo ve bar arasında bir bağımlılık varsa uygulama nasıl değişirdi. Örneğin

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

ve yazdır:

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

Not: If, loop ve / veya generator kullanarak nasıl safça yapılacağını biliyorum, ancak aynı şeyi işlevsel araçları kullanarak nasıl başaracağımı öğrenmek istiyorum. Bu, yalnızca maptest'e bir if ifadesi eklemek veya maptest içinde dahili olarak çubuklara başka bir filtre eşlemesi uygulamak mı?


Teşekkürler beyler. İşlevsel programlama kavramlarını python aracılığıyla öğrenmeye çalıştığımı itiraf etmeliyim.

1
Bunun için güzel bir öğretici burada: dreamsyssoft.com/python-scripting-tutorial/…
Rocky Pulley

Yanıtlar:


54

En kolay yol bars, farklı işlevlerden geçmek değil , doğrudan şunlardan erişmek olacaktır 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)

Orijinal maptestişlevinizle, ayrıca bir lambda işlevi de kullanabilirsiniz map:

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

listeden çubuklar geldiğinde kötü olan
Phyo Arkar Lwin

59
Bu çözüm, doğrudan OP'nin öğrenmeye çalışmak istediği fonksiyonel programlama ilkelerine aykırıdır. Fonksiyonel programlamada temel bir kural, aynı argümanlarla bir fonksiyonu her çağırdığınızda, HER ZAMAN aynı çıktıyı alacağınızdır. Bu, küresel bir duruma sahip olmanın getirdiği böcek yuvalarını önler. Harita testi, çubukların harici bir tanımına bağlı olduğundan, bu ilke geçersizdir.
image_doctor

3
Sevgili Stack overflow, soruları kapatmaktan ve orta derecede yoğun bir şekilde hoşlandığınız için, neden bu sorunun yanıt olarak işaretini kaldırıp doğru yanıtı yanıt olarak işaretlemiyorsunuz? Saygılarımızla, bize.
Bahadır Cambel

1
@image_doctor, FP'de küresel sabite erişmek tamamen uygundur (sıfır işlev olarak kabul edilir)
Peter K

1
@BahadirCambel Stack Overflow denetimi bazen zor olabilir, ancak onay işareti her zaman ve her zaman OP'ye aittir.
wizzwizz4

194

Diğer işlevsel dilleri biliyor musunuz? Yani, python'un işlevsel programlamayı nasıl yaptığını öğrenmeye mi çalışıyorsunuz yoksa işlevsel programlama ve python'u araç olarak kullanmayı mı öğrenmeye çalışıyorsunuz?

Ayrıca, liste anlayışlarını anlıyor musunuz?

map(f, sequence)

(*) ile doğrudan eşdeğerdir:

[f(x) for x in sequence]

Aslında, bir map()zamanlar fazlalık olarak python 3.0'dan kaldırılması planlandığını düşünüyorum (bu olmadı).

map(f, sequence1, sequence2)

çoğunlukla şuna eşdeğerdir:

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

(Dizilerin farklı uzunlukta olduğu durumu nasıl ele aldığı konusunda bir fark vardır. Gördüğünüz gibi map(), dizilerden biri bittiğinde Yok'u doldurur, oysa zip()en kısa dizi durduğunda durur)

Yani, özel sorunuza hitap etmek için sonucu üretmeye çalışıyorsunuz:

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

Bunu, tek bir bağımsız değişken alan ve bunu yazdıran ve ardından çubuklar yazan bir işlev yazarak yapabilirsiniz:

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

Alternatif olarak, şuna benzeyen bir liste oluşturabilirsiniz:

[bars, bars, bars, ] # etc.

ve orijinal harita testinizi kullanın:

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

Bunu yapmanın bir yolu, listeyi önceden açıkça oluşturmaktır:

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

Alternatif olarak, itertoolsmodülü içeri çekebilirsiniz . itertoolspython'da işlevsel tarzda yavaş değerlendirme programlaması yapmanıza yardımcı olan birçok akıllı işlev içerir. Bu durumda, itertools.repeatüzerinde yinelediğinizde argümanını sonsuza kadar çıkaracak olanı istiyoruz . Bu son gerçek, eğer yaparsanız:

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

map()argümanlardan biri hala çıktı ürettiği sürece devam ettiğinden sonsuz çıktı elde edeceksiniz. Ancak, itertools.imaptıpkı gibidir map(), ancak yinelenebilir en kısa durur durmaz durur.

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

Bu yardımcı olur umarım :-)

(*) Python 3.0'da biraz farklıdır. Burada, map () esasen bir üretici ifadesi döndürür.


Öyleyse, haritanın aksine itertools.imap(f, sequence1, sequence2)gerçekten eşdeğer olduğunu doğru anlıyor muyum [f(x1, x2) for x1, x2 in zip(sequence1, sequence2)]?
Jon Coombs

Biraz test ettiğimde, bunun bir itertools.imap nesnesi döndürdüğünü görüyorum, bu nedenle bu daha 'eşdeğer' olabilir:list(itertools.imap(f, sequence1, sequence2))
Jon Coombs

Bu onaylanmış cevap olmalıdır.
Rob Grant

30

İşte aradığınız çözüm:

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

[(x, bars) for x in foos]Her yinelemede bir işlev çağrısının ek yükünü ortadan kaldırdığı için (bu çok önemli olabilir) haritayı kullanmak yerine bir liste anlama ( parça) kullanmanızı öneririm . Sadece bir for döngüsünde kullanacaksanız, bir jeneratör anlayışı kullanarak daha iyi hızlar elde edersiniz:

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

Aradaki fark, jeneratör anlayışının tembel yüklenmesidir. .

GÜNCELLEME Bu yoruma yanıt olarak:

Elbette, çubukları kopyalamadığınızı biliyorsunuz, tüm girişler aynı çubuklar listesidir. Dolayısıyla, bunlardan herhangi birini (orijinal çubuklar dahil) değiştirirseniz, hepsini değiştirirsiniz.

Sanırım bu geçerli bir nokta. Bunun için aklıma gelen iki çözüm var. En verimli olanı muhtemelen şuna benzer:

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

Tuplelar değişmez olduğundan, bu, bu liste anlayışının sonuçları (veya bu rotaya giderseniz oluşturucu anlayışı) yoluyla çubukların değiştirilmesini önleyecektir. Sonuçların her birini gerçekten değiştirmeniz gerekiyorsa, bunu yapabilirsiniz:

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

Bununla birlikte, bu hem bellek kullanımı hem de hız açısından biraz pahalı olabilir, bu yüzden her birine gerçekten eklemeniz gerekmedikçe, buna karşı tavsiye ederim.


1
Elbette, çubukları kopyalamadığınızı biliyorsunuz, tüm girişler aynı çubuklar listesidir. Dolayısıyla, bunlardan herhangi birini (orijinal çubuklar dahil) değiştirirseniz, hepsini değiştirirsiniz.
vartec

20

Fonksiyonel programlama, yan etkisiz kod oluşturmakla ilgilidir.

harita, işlevsel bir liste dönüştürme soyutlamasıdır. Bir şey dizisini alıp başka bir şeyin dizisine dönüştürmek için kullanırsınız.

Bir yineleyici olarak kullanmaya çalışıyorsunuz. Bunu yapma. :)

İstediğiniz listeyi oluşturmak için haritayı nasıl kullanabileceğinize dair bir örnek aşağıda verilmiştir. Daha kısa çözümler var (sadece kavramaları kullanırım), ancak bu, hangi haritanın biraz daha iyi olduğunu anlamanıza yardımcı olacaktır:

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

new_list = map(my_transform, input_list)

Bu noktada, yalnızca bir veri işleme yaptığınıza dikkat edin. Şimdi yazdırabilirsiniz:

for n,l in new_list:
    print n, ll

- "Döngüsüz" ile ne demek istediğinden emin değilim. fp döngüleri önlemekle ilgili değildir (bir listedeki her öğeyi her birini ziyaret etmeden inceleyemezsiniz). Yan etkilerden kaçınmak, böylece daha az hata yazmakla ilgilidir.


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

Bu en kolay ve doğru şekilde işlevsel olanıdır. lütfen bunu cevap olarak kabul edin
Phyo Arkar Lwin

1
Bu sadece koddur. Açıklama yok. Birçok kullanıcı bu cevabın ne anlama geldiğini anlamıyor. @PhyoArkarLwin
ProgramFast

6

İşte map(function, *sequences)fonksiyon parametrelerine genel bir bakış :

  • function işlevinizin adıdır.
  • sequencesgenellikle liste veya tuple olan herhangi bir dizi dizidir. eşzamanlı olarakmap bunların üzerinde yinelenecek ve mevcut değerleri verecektir . Bu nedenle, dizi sayısı, fonksiyonunuzun parametre sayısına eşit olmalıdır.function

Görünüşe göre bazı functionparametreleri için yinelemeye çalışıyorsunuz, ancak diğerlerini sabit tutuyorsunuz ve ne yazık ki mapbunu desteklemiyor. Eski bir teklif buldumPython'a böyle bir özellik eklemek , ancak harita yapısı o kadar temiz ve sağlam ki böyle bir şeyin uygulanacağından şüphe duyuyorum.

Başkalarının önerdiği gibi, genel değişkenler veya liste anlayışları gibi bir geçici çözüm kullanın.


0

Bu yapar mı?

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
Maptest2 () parametresini 'barlar' gibi bir şey çağırmak isteyebilirsiniz. Tekil bar, aslında tüm listeyi istediğinizde yinelenen bir değer aldığını ima eder.
Nikhil Chelliah

1
Aslında inanıyorum ki yinelenen bir değer alıyor.
Chris

0

Buna ne dersin:

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