12

Niedawno nauczyłem się Haskella i staram się przenosić czysty funkcjonalny styl do mojego drugiego kodu, jeśli to możliwe. Ważnym aspektem tego jest traktowanie wszystkich zmiennych jako niezmiennych, tzn. Stałych. Aby to zrobić, wiele obliczeń, które byłyby implementowane za pomocą pętli w stylu imperatywnym, musi być wykonane przy użyciu rekurencji, która zwykle powoduje karę pamięci z powodu przydzielenia nowej ramki stosu dla każdego wywołania funkcji. W specjalnym przypadku wywołania typu "tail" (gdy zwracana wartość wywoływanej funkcji jest natychmiast zwracana do wywołującego wywołanie), kara ta może zostać ominięta przez proces zwany optymalizacją wywołania końcowego (w jednej metodzie może to być wykonane przez zasadniczo zastępując połączenie jmp po poprawnym ustawieniu stosu). Czy MATLAB wykonuje domyślnie TCO, czy jest jakiś sposób, żeby to powiedzieć?Czy program MATLAB przeprowadza optymalizację połączeń?

+0

Unikanie iteracji tylko do cholery nie jest to dobry pomysł. Użyj tego, co jest bardziej odpowiednie dla danego problemu (i wykonalne - oczywiście iteracja nie jest praktyczna w czysto funkcjonalnym języku). – delnan

+0

Dzięki odpowiedniej optymalizacji ogniskowej "unikanie iteracji" staje się kwestią tego, jak myślisz o problemie, a nie jak działa twoje rozwiązanie. Jeśli MATLAB nie oferuje TCO, to oczywiście używam iteracji kiedy potrzebuję, ale jeśli to zrobi, będę mógł używać spójnego paradygmatu we wszystkich moich kodach. –

+1

Nie znam żadnego MATLAB-a w szczególności, mówię ogólnie. Jeśli kodujesz Pythona, a Python ma TCO, nadal nie powinieneś używać rekursji przez pętle, ponieważ nie jest to idiomatyczne, język i stdlib koncentrują się na jak najlepszym wykorzystaniu iteratorów, itp. I w odniesieniu do "spójnego paradygmatu": Każdy paradygmat ma swoje słabe punkty, więc używaj wszystkiego, co najlepiej rozwiązuje dany problem (definicja "najlepszego" dotyczy również tego, jak dobrze działa wraz z resztą, oczywiście). – delnan

Odpowiedz

10

Gdybym zdefiniować prosty ogon rekurencyjnej funkcji:

function tailtest(n) 
    if n==0; feature memstats; return; end 
    tailtest(n-1); 
end 

i nazywają to tak, że będzie przeszukanie dość głęboko:

set(0,'RecursionLimit',10000); 
tailtest(1000); 

to nie wygląda tak, jakby ramki stosu są jedząc dużo pamięci. Jednakże, jeśli zrobię to recurse znacznie głębiej:

set(0,'RecursionLimit',10000); 
tailtest(5000); 

następnie (na moim komputerze, dzisiaj) MATLAB po prostu wywala: proces bezceremonialnie umiera.

Nie sądzę, że jest to zgodne z tym, że MATLAB wykonuje jakiekolwiek TCO; przypadek, w którym funkcja "wywołuje" samą siebie, tylko w jednym miejscu, bez zmiennych lokalnych innych niż pojedynczy argument, jest tak prosty, na jaki każdy może liczyć.

Tak: Nie, wygląda na to, że MATLAB w ogóle nie wykonuje TCO, przynajmniej domyślnie. Nie szukałem (jak dotąd) opcji, które mogłyby to umożliwić. Byłbym zaskoczony, gdyby były jakieś.

W przypadku, gdy nie zdmuchniemy stosu, ile kosztuje rekursja? Zobacz mój komentarz do odpowiedzi Billa Cheathama: wygląda na to, że czas nad głową jest nietrywialny, ale nie szalony.

... Tyle że Bill Cheatham usunął odpowiedź po tym, jak opuściłem ten komentarz. OK. Tak więc podjąłem prostą, iteracyjną implementację funkcji Fibonacciego i prosty rekursywny ogonek, wykonując w zasadzie te same obliczenia w obu przypadkach i ustalając je w czasie na fib(60). Wdrożenie rekursywne trwało około 2,5 razy dłużej niż w wersji iteracyjnej. Oczywiście, względny narzut będzie mniejszy dla funkcji, które wykonują więcej pracy niż jedno dodawanie i jedno odejmowanie na iterację.

(Zgadzam się również z sentymentem delnan za:. Wysoce rekurencyjne kod rodzaju, że czuje naturalny w Haskell jest zazwyczaj prawdopodobnie unidiomatic w MATLAB)

+3

TCO może być trudne w Matlabie, ponieważ oczyszczanie zmiennych lokalnych z obszaru roboczego ramki stosu może mieć efekty uboczne z onCleanup() Funkcja. Matlab nie jest w stylu GCed Java; jest deterministyczny, używa liczników referencyjnych lub podobnych. Obsługuje RAII. Aby określić, czy elizacja ramek stosu była bezpieczna, musiałaby dokładnie przeszukać wszystkie zmienne lokalne, które nie zostały przekazane w wywołaniu ogonowym, aby sprawdzić, czy zawierały one jakiekolwiek Zderzenie. Drogie testy. Co najmniej jedna ramka rodzica może wymagać zachowania w przypadku, gdy zostanie wywołana assignin (..., 'caller') lub evalin (..., 'caller'). Zgoda; unidiomatic. –

Powiązane problemy