2012-06-24 31 views
7

Trong khi cố gắng viết một câu trả lời cho một câu hỏi SO khác, điều gì đó thực sự kỳ lạ đã xảy ra.là lambdas python được thực hiện khác với chức năng tiêu chuẩn?

tôi về cơ bản đã đưa ra một gcd lót và nói it maybe slower because of recursion
gcd = lambda a,b : a if not b else gcd(b, a % b)

heres một thử nghiệm đơn giản:

assert gcd(10, 3) == 1 and gcd(21, 7) == 7 and gcd(100, 1000) == 100 

đây là một số tiêu chuẩn:

timeit.Timer('gcd(2**2048, 2**2048+123)', setup = 'from fractions import gcd').repeat(3, 100) 
# [0.0022919178009033203, 0.0016410350799560547, 0.0016489028930664062] 
timeit.Timer('gcd(2**2048, 2**2048+123)', setup = 'gcd = lambda a,b : a if not b else gcd(b, a % b)').repeat(3, 100) 
# [0.0020480155944824219, 0.0016460418701171875, 0.0014090538024902344] 

Well thats thú vị tôi dự kiến ​​sẽ chậm hơn nhiều nhưng thời gian là khá gần,? có thể nhập mô-đun là vấn đề ...

>>> setup = ''' 
... def gcd(a, b): 
...  """Calculate the Greatest Common Divisor of a and b. 
... 
...  Unless b==0, the result will have the same sign as b (so that when 
...  b is divided by it, the result comes out positive). 
...  """ 
...  while b: 
...   a, b = b, a%b 
...  return a 
... ''' 
>>> timeit.Timer('gcd(2**2048, 2**2048+123)', setup = setup).repeat(3, 100) 
[0.0015637874603271484, 0.0014810562133789062, 0.0014750957489013672] 

nope vẫn còn khá gần timings ok cho phép thử các giá trị lớn hơn.

timeit.Timer('gcd(2**9048, 2**248212)', setup = 'gcd = lambda a,b : a if not b else gcd(b, a % b)').repeat(3, 100) [2.866894006729126, 2.8396279811859131, 2.8353509902954102] 
[2.866894006729126, 2.8396279811859131, 2.8353509902954102] 
timeit.Timer('gcd(2**9048, 2**248212)', setup = setup).repeat(3, 100) 
[2.8533108234405518, 2.8411397933959961, 2.8430981636047363] 

thú vị Tôi tự hỏi điều gì đang diễn ra?
Tôi luôn luôn giả định đệ quy chậm hơn vì chi phí của việc gọi một hàm, là lambdas ngoại lệ? và tại sao tôi không đạt đến giới hạn đệ quy của mình?
Nếu thực hiện sử dụng def tôi đánh nó ngay lập tức, nếu tôi tăng độ sâu đệ quy một cái gì đó giống như 10**9 Tôi thực sự có được một segmentation fault lẽ một chồng tràn ...

Cập nhật

>>> setup = ''' 
... import sys 
... sys.setrecursionlimit(10**6) 
... 
... def gcd(a, b): 
...  return a if not b else gcd(b, a % b) 
... ''' 
>>> 
>>> timeit.Timer('gcd(2**9048, 2**248212)', setup = 'gcd = lambda a,b:a if not b else gcd(b, a%b)').repeat(3, 100) 
[3.0647969245910645, 3.0081429481506348, 2.9654929637908936] 
>>> timeit.Timer('gcd(2**9048, 2**248212)', setup = 'from fractions import gcd').repeat(3, 100) 
[3.0753359794616699, 2.97499680519104, 3.0096950531005859] 
>>> timeit.Timer('gcd(2**9048, 2**248212)', setup = setup).repeat(3, 100) 
[3.0334799289703369, 2.9955930709838867, 2.9726388454437256] 
>>> 

thậm chí nhiều hơn khó hiểu ...

+0

Tôi nghĩ rất có thể trình biên dịch Python đang tối ưu hóa biểu thức lambda của bạn thành một vòng lặp * cho bạn * (giống như cách một Lisp điển hình) triển khai sẽ tối ưu hóa các cuộc gọi đuôi đệ quy). Tuy nhiên điều này sẽ là một chi tiết thực hiện của CPython, không nhất thiết phải đúng đối với tất cả các trình thông dịch Python. – Felix

+0

'đệ quy đuôi gọi 'yeah thats những gì Im cũng nghĩ, vẫn có thể chúng tôi luôn áp dụng nó, không có nghĩa là đệ quy là tốt hơn bằng cách sử dụng lambdas sau đó tiêu chuẩn' def' nó thực sự khó hiểu đặc biệt khi bạn xem xét nó ưu tiên khả năng đọc trên tốc độ hoặc biểu cảm 'lấy từ http://en.wikipedia.org/wiki/Portal:Python_programming –

+2

@FelixBonkoski, Python không tối ưu hóa việc đệ quy đuôi.Mã này chỉ có một chút sử dụng ngăn xếp :) – astynax

Trả lời

-1

Loại lambda giống hệt như loại của bất kỳ chức năng nào khác và trong trường hợp cả hai, nếu được xác định trong phạm vi cục bộ khác, việc chụp môi trường xảy ra.

Sự khác biệt duy nhất là các hàm được xác định bằng cú pháp lambda không tự động trở thành giá trị của một biến trong phạm vi xuất hiện và cú pháp lambda đó yêu cầu thân thể là một biểu thức (có thể ghép). trong đó được trả về từ hàm.

Với tốc độ đệ quy - có một khoản phí nhỏ, nhưng dường như không nhiều. Chi phí cuộc gọi sẽ xuất hiện chủ yếu bằng chi phí phân bổ khung ngăn xếp.

+0

Bạn thậm chí có nhìn vào những lần anh ấy đang hiển thị không? Bạn không ** thấy _effect_ rằng thư viện function fractions.gcd() nhanh hơn, anh ta đang cố gắng cho thấy rằng đó là một sự lộn xộn, và anh ta bối rối bởi điều đó. -1 vì không đọc câu hỏi. – Felix

+1

@Marcin Tôi cũng đã định nghĩa hàm không đệ quy bằng cách sử dụng python thường xuyên và Timings vẫn không khác biệt, một cái gì đó khá đặc biệt đang xảy ra, và tại sao tôi không đạt đến giới hạn đệ quy? –

+0

@ samy.vilar Bạn đang tạo chỉ một int mới cho mỗi khung stack, do đó, bộ nhớ tiêu thụ không phải là một vấn đề. không có bí ẩn ở đây. mit, tại sao bạn mong đợi rằng bạn sẽ nhấn nó với ví dụ này? – Marcin

6
counter = 0 

def gcd(a, b): 
    global counter 
    counter += 1 
    return a if not b else gcd(b, a % b) 

gcd(2**9048, 2**248212) 
print counter 

In 3. Tất nhiên không có nhiều chi phí cho một đệ quy về độ sâu 3.

+0

yes Im khá ý thức về điều đó bây giờ, tôi nên đã sử dụng số Fibonacci để chạy thử nghiệm của tôi, học một cái gì đó mới mỗi ngày ... –

Các vấn đề liên quan