使用python map和其他功能工具

问题:使用python map和其他功能工具

这相当不错,但是我正在尝试学习/理解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

问:有没有一种方法可以使用map或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,循环和/或生成器天真地做到这一点,但是我想学习如何使用功能性工具来实现这一点。是否只是在maptest中添加if语句或在maptest内部将其他过滤器映射应用于bar的情况?

This is quite n00bish, but I’m trying to learn/understand functional programming in python. The following code:

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)

produces:

1.0 1
2.0 2
3.0 3
4.0 None
5.0 None

Q. Is there a way to use map or any other functional tools in python to produce the following without loops etc.

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]

Just as a side note how would the implementation change if there is a dependency between foo and bar. e.g.

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

and print:

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

P.S: I know how to do it naively using if, loops and/or generators, but I’d like to learn how to achieve the same using functional tools. Is it just a case of adding an if statement to maptest or apply another filter map to bars internally within maptest?


回答 0

最简单的方法是不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以下代码中使用lambda函数:

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

The easiest way would be not to pass bars through the different functions, but to access it directly from 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)

With your original maptest function you could also use a lambda function in map:

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

回答 1

您是否熟悉其他功能语言?即,您是要学习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()当其中一个序列用完时,填入None,而zip()当最短序列停止时,则填满)

因此,为了解决您的特定问题,您尝试产生结果:

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

您可以通过编写一个带有单个参数并打印它的函数,然后加上杠来做到这一点:

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

或者,您可以创建一个如下所示的列表:

[bars, bars, bars, ] # etc.

并使用原始的maptest:

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

一种方法是事先显式构建列表:

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

或者,您可以拉入itertools模块。 itertools包含许多巧妙的功能,可帮助您在python中进行功能风格的延迟评估编程。在这种情况下,我们需要itertools.repeat,当您对其进行迭代时,它将无限期地输出其参数。最后一个事实意味着,如果您这样做:

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

map()只要参数之一仍在产生输出,您就会得到无穷的输出,因为它一直持续下去。但是,itertools.imap就像map(),但最短的可迭代停止就停止。

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

希望这可以帮助 :-)

(*)在python 3.0中有些不同。在那里,map()本质上返回一个生成器表达式。

Are you familiar with other functional languages? i.e. are you trying to learn how python does functional programming, or are you trying to learn about functional programming and using python as the vehicle?

Also, do you understand list comprehensions?

map(f, sequence)

is directly equivalent (*) to:

[f(x) for x in sequence]

In fact, I think map() was once slated for removal from python 3.0 as being redundant (that didn’t happen).

map(f, sequence1, sequence2)

is mostly equivalent to:

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

(there is a difference in how it handles the case where the sequences are of different length. As you saw, map() fills in None when one of the sequences runs out, whereas zip() stops when the shortest sequence stops)

So, to address your specific question, you’re trying to produce the result:

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

You could do this by writing a function that takes a single argument and prints it, followed by bars:

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

Alternatively, you could create a list that looks like this:

[bars, bars, bars, ] # etc.

and use your original maptest:

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

One way to do this would be to explicitely build the list beforehand:

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

Alternatively, you could pull in the itertools module. itertools contains many clever functions that help you do functional-style lazy-evaluation programming in python. In this case, we want itertools.repeat, which will output its argument indefinitely as you iterate over it. This last fact means that if you do:

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

you will get endless output, since map() keeps going as long as one of the arguments is still producing output. However, itertools.imap is just like map(), but stops as soon as the shortest iterable stops.

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

Hope this helps :-)

(*) It’s a little different in python 3.0. There, map() essentially returns a generator expression.


回答 2

这是您要寻找的解决方案:

>>> 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]部分)而不是使用地图,因为它避免了每次迭代时函数调用的开销(这可能非常重要)。如果只打算在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])

区别在于生成器理解迟缓地加载

更新 针对此评论:

当然,您知道您不复制栏,所有条目都是相同的栏列表。因此,如果您修改其中的任何一个(包括原始条),那么您将修改所有的它们。

我想这是一个正确的观点。我可以想到两种解决方案。最有效的可能是这样的:

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

由于元组是不可变的,因此这将防止通过此列表理解的结果(或通过该路线生成器理解)的结果来修改钢筋。如果确实需要修改每个结果,则可以执行以下操作:

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

但是,这在内存使用和速度方面都可能会有些昂贵,因此我建议您不要这样做,除非您确实需要添加每个内存。

Here’s the solution you’re looking for:

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

I’d recommend using a list comprehension (the [(x, bars) for x in foos] part) over using map as it avoids the overhead of a function call on every iteration (which can be very significant). If you’re just going to use it in a for loop, you’ll get better speeds by using a generator comprehension:

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

The difference is that the generator comprehension is lazily loaded.

UPDATE In response to this comment:

Of course you know, that you don’t copy bars, all entries are the same bars list. So if you modify any one of them (including original bars), you modify all of them.

I suppose this is a valid point. There are two solutions to this that I can think of. The most efficient is probably something like this:

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

Since tuples are immutable, this will prevent bars from being modified through the results of this list comprehension (or generator comprehension if you go that route). If you really need to modify each and every one of the results, you can do this:

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

However, this can be a bit expensive both in terms of memory usage and in speed, so I’d recommend against it unless you really need to add to each one of them.


回答 3

函数式编程是关于创建无副作用的代码。

map是功能列表转换的抽象。您可以使用它来获取一系列序列并将其转换为其他序列。

您正在尝试将其用作迭代器。不要那样做 :)

这是一个示例,说明如何使用地图构建所需的列表。有较短的解决方案(我只是使用理解力),但这将帮助您了解哪种地图效果更好:

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并不是要避免循环(您无法访问列表中的每个项目都无法对其进行检查)。这是关于避免副作用,从而减少错误。

Functional programming is about creating side-effect-free code.

map is a functional list transformation abstraction. You use it to take a sequence of something and turn it into a sequence of something else.

You are trying to use it as an iterator. Don’t do that. :)

Here is an example of how you might use map to build the list you want. There are shorter solutions (I’d just use comprehensions), but this will help you understand what map does a bit better:

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

new_list = map(my_transform, input_list)

Notice at this point, you’ve only done a data manipulation. Now you can print it:

for n,l in new_list:
    print n, ll

— I’m not sure what you mean by ‘without loops.’ fp isn’t about avoiding loops (you can’t examine every item in a list without visiting each one). It’s about avoiding side-effects, thus writing fewer bugs.


回答 4

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

回答 5

import itertools

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

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

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

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

回答 6

以下是该map(function, *sequences)函数的参数概述:

  • function 是函数的名称。
  • sequences是任意数量的序列,通常是列表或元组。 map同时迭代它们并将当前值提供给function。这就是为什么序列数应等于函数的参数数的原因。

听起来您正在尝试迭代某些function参数,但保持其他参数不变,但是不幸的map是不支持该参数。我发现一个向Python添加此类功能的旧建议,但是map构造是如此干净且完善,以至于我怀疑这样的东西是否会实现。

像其他人建议的那样,使用诸如全局变量或列表理解之类的解决方法。

Here’s an overview of the parameters to the map(function, *sequences) function:

  • function is the name of your function.
  • sequences is any number of sequences, which are usually lists or tuples. map will iterate over them simultaneously and give the current values to function. That’s why the number of sequences should equal the number of parameters to your function.

It sounds like you’re trying to iterate for some of function‘s parameters but keep others constant, and unfortunately map doesn’t support that. I found an old proposal to add such a feature to Python, but the map construct is so clean and well-established that I doubt something like that will ever be implemented.

Use a workaround like global variables or list comprehensions, as others have suggested.


回答 7

这样可以吗?

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)

Would this do it?

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)

回答 8

这个怎么样:

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

How about this:

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