2013-08-26 21 views
7

W poniższym kodzie:Python sprawność lista krojenie

def listSum(alist): 
    """Get sum of numbers in a list recursively.""" 
    sum = 0 
    if len(alist) == 1: 
     return alist[0] 
    else: 
     return alist[0] + listSum(alist[1:]) 
    return sum 

jest nową listę utworzoną za każdym razem, kiedy robię listSum(alist[1:])?

Jeśli tak, czy jest to zalecany sposób, czy mogę zrobić coś bardziej wydajnego? (Nie dla specyficzna funkcja -To służy jako Przykładzie, lecz gdy chcę przetworzyć konkretną część listy w ogóle.)

Edit:

Przepraszam, jeśli kogoś mylić, nie jestem zainteresowani wydajną implementacją sum, posłużyło to jako przykład do zastosowania takiego cięcia.

Odpowiedz

5

Tak, za każdym razem tworzy nową listę. Jeśli możesz uniknąć używania iteracji, możesz użyć itertools.islice lub żonglować iter(list) (jeśli potrzebujesz tylko pominąć niektóre elementy na początku). Ale to staje się kłopotliwe, gdy musisz ustalić, czy argument jest pusty, czy ma tylko jeden element - musisz użyć try i złapać StopIteration. Edycja: Można również dodać dodatkowy argument, który określa, od czego zacząć. W przeciwieństwie do wersji @ marcadian, powinieneś uczynić ją domyślnym argumentem, aby nie obciążać dzwoniącego i unikać błędów pochodzących z niewłaściwego indeksu przekazywanego z zewnątrz.

Często lepiej jest nie wchodzić w tego rodzaju sytuacje - albo zapisz swój kod tak, aby umożliwić for radzenie sobie z iteracją (czytaj: nie używaj takiej rekursji). Alternatywnie, jeśli plasterek jest dość mały (prawdopodobnie dlatego, że lista jako całość jest mała), ugryź kulę i pokrój w każdym razie - jest łatwiej, a podczas krojenia jest czas liniowy, stały czynnik jest naprawdę mały.

+0

Zakładając jako przykład zabawkę, że mam dość dużą listę liczb, a ja chcę dodać 2 do połowy z nich. Czy coś takiego jak 'for number in mylist [middle:]: number + = 2' jest dopuszczalne? (Czy nie uważa się za dobrą praktykę wycinanie naprawdę dużej listy?) –

+1

@i_am_finally_learning_python Pomijanie tego przykładu nie działa - nie zmienia zawartości 'mylist' (i najprostszy sposób na zrobienie tego wymaga nic nie kroi) - to zależy. Krojenie ma duży plus, że jest zwykle prostsze i ładniejsze niż alternatywa. Ale w zależności od tego, co "dość duże", jak często ten kod wykonuje, i inne czynniki, może być denerwująco powolny, a mniej czytelna alternatywa bez plasterka jest tego warta. Trudno powiedzieć ogólnie. – delnan

+0

Och, oczywiście, myślę, że byłem podekscytowany tym pytonem dla pętli !! To oczyściło trochę rzeczy, dzięki. –

2

mogę myśleć o niektórych opcji:

  1. użycia wbudowanego sumy funkcji dla tego konkretnego przypadku
  2. Jeśli naprawdę potrzebujesz rekurencji (z jakiegoś powodu), przechodzą w tym samym liście do wywołania funkcji, a także indeks bieżącego elementu, więc nie trzeba kroić listy (która tworzy nową listę)

opcję 2 działa tak:

def f_sum(alist, idx=0): 
    if idx >= len(alist): 
     return 0 
    return alist[idx] + f_sum(alist, idx + 1) 


f_sum([1, 2, 3, 4]) 
a = range(5) 
f_sum(a) 
0

Jest to inny sposób, jeśli musisz użyć rekursji (rekursywny ogon). W wielu innych językach jest to bardziej wydajne niż regularne rekurencje pod względem złożoności przestrzeni. Niestety nie jest tak w przypadku Pythona, ponieważ nie ma wbudowanej obsługi optymalizacji połączeń typu "ogon". Dobrą praktyką jest zwrócenie uwagi na to, czy uczymy się rekurencji.

def helper(l, s, i): 
    if len(l) == 0: 
     return 0 
    elif i < len(l) - 1: 
     return helper(l, s + l[i], i + 1) 
    else: 
     return s + l[i] 


def listSum(l): 
    return helper(l, 0, 0) 
+0

Z tym że żadna główna implementacja Pythona nie wykonuje, lub prawdopodobnie nigdy nie wykona, eliminacji rekurencji ogona. Te dwie funkcje można również połączyć w jedną za pomocą domyślnych argumentów. – delnan

+0

Dobrze, Zanotuję to w mojej edycji. – Joohwan