Pythonマップとその他の機能ツールの使用


127

これはかなりn00bishですが、Pythonで関数型プログラミングを学習/理解しようとしています。次のコード:

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)

生成する:

1.0 1
2.0 2
3.0 3
4.0 None
5.0 None

Q. Pythonでマップやその他の機能ツールを使用して、ループなしで以下を生成する方法はありますか?

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]

余談ですが、fooとbarの間に依存関係がある場合、実装はどのように変化するでしょうか。例えば

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

そして印刷:

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

PS:if、ループ、および/またはジェネレーターを使用して単純にそれを行う方法を知っていますが、機能ツールを使用して同じことを達成する方法を学びたいです。ifステートメントをmaptestに追加したり、maptest内のバーに別のフィルターマップを適用したりするだけのケースですか?


みんなありがとう。Pythonを使用して関数型プログラミングの概念を学習しようとしていることを認めなければなりません。

1
ここでそれのための素晴らしいチュートリアル:dreamsyssoft.com/python-scripting-tutorial/...
トリトンマン

回答:


54

最も簡単な方法はbars、さまざまな関数を渡すのではなく、以下から直接アクセスすることです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)

元のmaptest関数を使用して、ラムダ関数をmap次の場所で使用することもできます。

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

バーがリストから来たときに悪いもの
Phyo Arkar Lwin

59
このソリューションは、OPが学習しようとしている関数型プログラミングの原則に直接反します。関数型プログラミングの基本的なルールは、同じ引数で関数を呼び出すたびに、常に同じ出力が得られることです。これにより、グローバルな状態を持つことで発生する、バグの寄生者の巣が回避されます。maptestはバーの外部定義に依存しているため、この原則は破られています。
image_doctor 2013

3
スタックオーバーフローへようこそ。質問を閉じて適度に重くしたいので、この質問の回答のマークを外し、正しい回答に回答のマークを付けませんか?よろしく、私たち。
バハディールカンベル2014

1
@ image_doctor、FPでは、グローバル定数(nullary fuctionと見なされる)にアクセスしても問題ありません
Peter K

1
@BahadirCambelのスタックオーバーフローのモデレートは時として強引になりますが、チェックマークは常にOPに属します。
wizzwizz4

194

他の関数型言語に精通していますか?つまり、Pythonが関数型プログラミングをどのように実行するかを学習しようとしているのか、それとも関数型プログラミングについて学び、Pythonを車両として使用しようとしているのか?

また、リストの内包表記を理解していますか?

map(f, sequence)

以下と直接同等(*)です:

[f(x) for x in sequence]

実際、以前map()はpython 3.0からの削除が冗長であると考えられていました(それは起こりませんでした)。

map(f, sequence1, sequence2)

ほとんどが以下と同等です:

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

(シーケンスの長さが異なる場合の処理​​方法に違いがあります。ご覧のように、シーケンスのmap()1つが不足するとNoneを入力し、zip()最短のシーケンスが停止すると停止します)

したがって、特定の質問に対処するために、結果を生成しようとしています。

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

これを行うには、1つの引数を取り、それを出力する関数を記述し、その後にバーを続けます。

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

または、次のようなリストを作成することもできます。

[bars, bars, bars, ] # etc.

元のマップテストを使用します:

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

これを行う1つの方法は、事前に明示的にリストを作成することです。

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

または、itertoolsモジュールをプルすることもできます。 itertoolsPythonで関数型の遅延評価プログラミングを行うのに役立つ多くの巧妙な関数が含まれています。この場合、をitertools.repeat繰り返しますが、引数を無期限に出力します。この最後の事実は、次のことを行うことを意味します。

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

map()引数の1つが出力を生成している限り、出力は無限に続くため、出力は無限になります。ただし、itertools.imapと同じですmap()が、最短の反復可能オブジェクトが停止するとすぐに停止します。

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

お役に立てれば :-)

(*)python 3.0では少し異なります。そこで、map()は基本的にジェネレータ式を返します。


それで、私はマップとは異なり、itertools.imap(f, sequence1, sequence2)実際には同等であることを正しく理解してい[f(x1, x2) for x1, x2 in zip(sequence1, sequence2)]ますか?
Jon Coombs、2014

ビットのテスト、私はので、多分、これはより多くの「同等」になり、それがitertools.imapオブジェクトを返すことを参照してくださいlist(itertools.imap(f, sequence1, sequence2))
ジョン・クームス

これは承認された答えであるはずです。
Rob Grant

30

ここにあなたが探している解決策があります:

>>> 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]map を使用するよりもリスト内包(部分)を使用することをお勧めします。これにより、反復ごとの関数呼び出しのオーバーヘッドが回避されます(非常に重要になる可能性があります)。forループで使用する場合は、ジェネレーター内包表記を使用すると速度が向上します。

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

違いは、ジェネレータ内包が遅延ロードされることです。

更新 このコメントへの応答:

もちろん、バーをコピーするのではなく、すべてのエントリが同じバーリストです。したがって、それらのいずれか(元のバーを含む)を変更すると、それらすべてが変更されます。

これは妥当なポイントだと思います。これには2つの解決策が考えられます。最も効率的なのは、おそらく次のようなものです。

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

タプルは不変であるため、これにより、このリスト内包(またはそのルートに行く場合はジェネレーター内包)の結果によってバーが変更されるのを防ぎます。結果のすべてを本当に変更する必要がある場合は、これを行うことができます:

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

ただし、これはメモリ使用量と速度の両方の点で少し高価になる可能性があるため、実際にそれぞれに追加する必要がない限り、これはお勧めしません。


1
もちろん、バーをコピーするのではなく、すべてのエントリが同じバーリストです。したがって、それらのいずれか(元のバーを含む)を変更すると、それらすべてが変更されます。
vartec 2009年

20

関数型プログラミングとは、副作用のないコードを作成することです。

マップは機能リスト変換の抽象化です。これを使用して、何かのシーケンスを取得し、それを別のシーケンスに変換します。

イテレータとして使用しようとしています。それをしないでください。:)

以下は、マップを使用して必要なリストを作成する方法の例です。より短い解決策があります(私は単に内包表記を使用します)が、これは、どのマップが少し優れているかを理解するのに役立ちます。

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

new_list = map(my_transform, input_list)

この時点では、データ操作しか行っていないことに注意してください。これで印刷できます:

for n,l in new_list:
    print n, ll

-「ループなし」の意味がわかりません。fpはループを回避するためのものではありません(リスト内のすべてのアイテムを調べることはできません)。それは副作用を回避することであり、それによりバグの書き込みが少なくなります。


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

これは最も簡単で正しく機能するものです。これを回答として受け入れてください
Phyo Arkar Lwin

1
これは単なるコードです。説明はありません。多くのユーザーは、この回答の意味を理解していません。@PhyoArkarLwin
ProgramFast

6

次に、map(function, *sequences)関数のパラメーターの概要を示します。

  • function 関数の名前です。
  • sequences任意の数のシーケンスで、通常はリストまたはタプルです。 mapそれらを同時に繰り返し、現在の値をに与えますfunction。そのため、シーケンスの数は関数のパラメーターの数と等しくなければなりません。

functionのパラメータの一部を反復しようとしているが、他のパラメータは一定に維持しているようですが、残念ながらmapサポートしていません。そのような機能をPythonに追加するという古い提案を見つけましが、マップコンストラクトは非常にクリーンで確立されているため、そのようなものが実装されることはないと思います。

他の人が示唆しているように、グローバル変数やリスト内包表記などの回避策を使用してください。


0

これでいいですか?

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()のパラメーターを 'bars'などのように呼び出すことができます。単数形barは、リスト全体が実際に必要なときに、反復値を受け取ることを意味します。
Nikhil Chelliah 2009年

1
それは私が信じている反復値を実際に受け取っています。
Chris

0

これはどう:

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))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.