Sử dụng bản đồ python và các công cụ chức năng khác


127

Điều này khá khó khăn, nhưng tôi đang cố gắng học / hiểu lập trình chức năng trong python. Các mã sau đây:

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)

sản xuất:

1.0 1
2.0 2
3.0 3
4.0 None
5.0 None

Q. Có cách nào để sử dụng bản đồ hoặc bất kỳ công cụ chức năng nào khác trong python để tạo ra các mục sau mà không cần vòng lặp, v.v.

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]

Cũng như một lưu ý phụ, việc triển khai sẽ thay đổi như thế nào nếu có sự phụ thuộc giữa foo và bar. ví dụ

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

và in:

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

Tái bút: Tôi biết cách thực hiện một cách ngây thơ bằng cách sử dụng if, vòng lặp và / hoặc trình tạo, nhưng tôi muốn tìm hiểu cách đạt được điều tương tự bằng các công cụ chức năng. Đây chỉ là một trường hợp thêm một câu lệnh if vào maptest hoặc áp dụng một bản đồ lọc khác cho các thanh bên trong maptest?


Cảm ơn các bạn. Tôi phải thừa nhận tôi đang cố gắng học các khái niệm về lập trình chức năng thông qua python.

1
Một hướng dẫn hay cho nó ở đây: dreamyssoft.com/python-scripting-tutorial/,
Triton Man

Câu trả lời:


54

Cách dễ nhất sẽ không phải là barsthông qua các chức năng khác nhau, mà là truy cập trực tiếp từ 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)

Với maptestchức năng ban đầu của bạn, bạn cũng có thể sử dụng chức năng lambda trong map:

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

xấu khi các thanh đến từ danh sách
Phyo Arkar Lwin

59
Giải pháp này trực tiếp đi ngược lại các nguyên tắc lập trình chức năng mà OP muốn cố gắng học hỏi. Một quy tắc cơ bản trong lập trình hàm là mỗi khi bạn gọi một hàm có cùng đối số, bạn LUÔN LUÔN nhận được cùng một đầu ra. Điều này tránh một tổ ong vipers được giới thiệu bằng cách có trạng thái toàn cầu. Vì maptest phụ thuộc vào định nghĩa bên ngoài của các thanh, nguyên tắc này bị phá vỡ.
image_doctor

3
Kính gửi Stack tràn, vì bạn muốn đóng câu hỏi và nặng vừa phải, tại sao bạn không bỏ đánh dấu câu hỏi này thành câu trả lời và đánh dấu câu trả lời đúng là câu trả lời? Trân trọng, chúng tôi.
Bahadir Cambel

1
@image_doctor, trong FP hoàn toàn ổn khi truy cập vào hằng số toàn cầu (có liên quan như là sự hợp nhất vô giá trị)
Peter K

1
Đôi khi @BahadirCambel Kiểm duyệt tràn ngăn xếp có thể bị nặng tay, nhưng dấu kiểm luôn luôn và sẽ luôn thuộc về OP.
wizzwizz4

194

Bạn có quen thuộc với các ngôn ngữ chức năng khác? tức là bạn đang cố gắng học cách python lập trình chức năng, hay bạn đang cố gắng học về lập trình chức năng và sử dụng python làm phương tiện?

Ngoài ra, bạn có hiểu danh sách hiểu?

map(f, sequence)

tương đương trực tiếp (*) với:

[f(x) for x in sequence]

Trên thực tế, tôi nghĩ map()đã từng được dự kiến ​​loại bỏ khỏi python 3.0 là dư thừa (điều đó đã không xảy ra).

map(f, sequence1, sequence2)

hầu hết tương đương với:

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

(có một sự khác biệt trong cách xử lý trường hợp các chuỗi có độ dài khác nhau. Như bạn đã thấy, map()điền vào Không khi một trong các chuỗi hết, trong khi zip()dừng khi chuỗi ngắn nhất dừng lại)

Vì vậy, để giải quyết câu hỏi cụ thể của bạn, bạn đang cố gắng tạo ra kết quả:

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

Bạn có thể làm điều này bằng cách viết một hàm lấy một đối số và in nó, theo sau là các thanh:

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

Ngoài ra, bạn có thể tạo một danh sách giống như thế này:

[bars, bars, bars, ] # etc.

và sử dụng maptest ban đầu của bạn:

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

Một cách để làm điều này là xây dựng danh sách trước một cách rõ ràng:

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

Ngoài ra, bạn có thể kéo itertoolsmô-đun. itertoolschứa nhiều chức năng thông minh giúp bạn thực hiện lập trình đánh giá lười biếng theo kiểu chức năng trong python. Trong trường hợp này, chúng tôi muốn itertools.repeat, nó sẽ đưa ra đối số của nó vô thời hạn khi bạn lặp lại nó. Thực tế cuối cùng này có nghĩa là nếu bạn làm:

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

bạn sẽ nhận được đầu ra vô tận, vì cứ map()tiếp tục miễn là một trong các đối số vẫn đang tạo đầu ra. Tuy nhiên, itertools.imapcũng giống như map(), nhưng dừng lại ngay khi dừng lặp lại ngắn nhất.

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

Hi vọng điêu nay co ich :-)

(*) Nó hơi khác một chút trong python 3.0. Ở đó, map () về cơ bản trả về một biểu thức trình tạo.


Vì vậy, tôi có hiểu chính xác rằng, không giống như bản đồ, itertools.imap(f, sequence1, sequence2)thực sự tương đương với [f(x1, x2) for x1, x2 in zip(sequence1, sequence2)]?
Jon Coombs

Thử nghiệm một chút, tôi thấy rằng nó trả về một đối tượng itertools.imap, vì vậy có lẽ điều này sẽ 'tương đương' hơn:list(itertools.imap(f, sequence1, sequence2))
Jon Coombs

Đây phải là câu trả lời được phê duyệt.
Rob Grant

30

Đây là giải pháp bạn đang tìm kiế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])]

Tôi khuyên bạn nên sử dụng mức độ hiểu danh sách ( [(x, bars) for x in foos]phần) so với sử dụng bản đồ vì nó tránh được chi phí của một lệnh gọi hàm trên mỗi lần lặp (có thể rất quan trọng). Nếu bạn chỉ sử dụng nó trong một vòng lặp for, bạn sẽ có được tốc độ tốt hơn bằng cách sử dụng trình hiểu máy phát điện:

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

Sự khác biệt là sự hiểu biết về máy phát điện được tải một cách lười biếng .

CẬP NHẬT Đáp lại bình luận này:

Tất nhiên bạn biết, rằng bạn không sao chép thanh, tất cả các mục là cùng một danh sách thanh. Vì vậy, nếu bạn sửa đổi bất kỳ một trong số chúng (bao gồm các thanh gốc), bạn sửa đổi tất cả chúng.

Tôi cho rằng đây là một điểm hợp lệ. Có hai giải pháp cho vấn đề này mà tôi có thể nghĩ ra. Hiệu quả nhất có lẽ là một cái gì đó như thế này:

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

Vì các bộ dữ liệu là bất biến, điều này sẽ ngăn các thanh bị sửa đổi thông qua kết quả của việc hiểu danh sách này (hoặc hiểu về trình tạo nếu bạn đi theo lộ trình đó). Nếu bạn thực sự cần sửa đổi từng kết quả, bạn có thể làm điều này:

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

Tuy nhiên, điều này có thể hơi tốn kém cả về việc sử dụng bộ nhớ và tốc độ, vì vậy tôi khuyên bạn nên chống lại nó trừ khi bạn thực sự cần phải thêm vào từng người trong số họ.


1
Tất nhiên bạn biết, rằng bạn không sao chép thanh, tất cả các mục là cùng một danh sách thanh. Vì vậy, nếu bạn sửa đổi bất kỳ một trong số chúng (bao gồm các thanh gốc), bạn sửa đổi tất cả chúng.
vartec

20

Lập trình chức năng là về việc tạo mã không có hiệu ứng phụ.

bản đồ là một trừu tượng chuyển đổi danh sách chức năng. Bạn sử dụng nó để lấy một chuỗi của một cái gì đó và biến nó thành một chuỗi của một cái gì đó khác.

Bạn đang cố gắng sử dụng nó như một trình vòng lặp. Đừng làm vậy. :)

Dưới đây là một ví dụ về cách bạn có thể sử dụng bản đồ để xây dựng danh sách bạn muốn. Có những giải pháp ngắn hơn (tôi chỉ sử dụng khả năng hiểu), nhưng điều này sẽ giúp bạn hiểu bản đồ nào tốt hơn một chút:

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

new_list = map(my_transform, input_list)

Lưu ý tại thời điểm này, bạn chỉ thực hiện một thao tác dữ liệu. Bây giờ bạn có thể in nó:

for n,l in new_list:
    print n, ll

- Tôi không chắc ý của bạn là 'không có vòng lặp.' fp không phải là về việc tránh các vòng lặp (bạn không thể kiểm tra từng mục trong danh sách mà không truy cập từng mục). Đó là về việc tránh các tác dụng phụ, do đó viết ít lỗi hơn.


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

Đây là cách dễ nhất và đúng chức năng. vui lòng chấp nhận câu trả lời này
Phyo Arkar Lwin

1
Đây chỉ là mã. Không có lời giải thích. Nhiều người dùng không hiểu câu trả lời này có nghĩa gì. @PhyoArkarLwin
Chương trình nhanh nhất

6

Dưới đây là tổng quan về các tham số cho map(function, *sequences)chức năng:

  • function là tên của chức năng của bạn.
  • sequenceslà bất kỳ số lượng trình tự, thường là danh sách hoặc bộ dữ liệu. mapsẽ lặp lại chúng đồng thời và đưa ra các giá trị hiện tại function. Đó là lý do tại sao số lượng chuỗi phải bằng số lượng tham số cho hàm của bạn.

Nghe có vẻ như bạn đang cố gắng lặp lại một functionsố tham số nhưng không thay đổi các tham số khác và không may mapkhông hỗ trợ điều đó. Tôi đã tìm thấy một đề xuất cũ để thêm một tính năng như vậy vào Python, nhưng cấu trúc bản đồ rất sạch sẽ và được thiết lập tốt đến mức tôi nghi ngờ điều gì đó như thế sẽ được thực hiện.

Sử dụng một cách giải quyết như các biến toàn cục hoặc danh sách hiểu, như những người khác đã đề xuất.


0

Điều này sẽ làm điều đó?

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
Bạn có thể muốn gọi param cho maptest2 () một cái gì đó như 'thanh'. Số ít barngụ ý rằng nó nhận được một giá trị lặp, khi bạn thực sự muốn toàn bộ danh sách.
Nikhil Chelliah

1
Nó thực sự đang nhận được một giá trị lặp đi lặp lại tôi tin.
Chris

0

Còn cái này thì sao:

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))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.