2013-12-01 4 views
1

Я только начал изучать эрланг, и рекурсия хвоста убивает меня медленно; Я не могу обдумать это. Я пытаюсь сделать программу, которая удваивает все остальные числа в списке, и я пытаюсь сделать это с помощью рекурсии хвоста.Рекурсия хвоста Эрланг

Вот мой код до сих пор

stripAndDoubleOdds([H|T]) -> stripAndDoubleOdds([H|T],1,[H|T]). 

    stripAndDoubleOdds(F, _, []) -> F; 

    stripAndDoubleOdds(_,Index,[H1|T1]) -> 

    F = [] ++ 2*lists:nth(Index, [H1|T1]), 

stripAndDoubleOdds(F, Index +2, T1). 

Когда я дать ему список номеров я не получаю никакого вывода вообще, но если я дам ей один номер может удвоить его без проблем.

Индекс предназначен для сохранения положения текущего элемента и его увеличения на 2, чтобы получить каждое другое целое число и удвоить его. Мое текущее решение включает в себя извлечение головы, удвоение ее, добавление ее в список, а затем передачу хвоста через процесс снова и снова, пока я не получу пустой список, и на этом этапе я должен получить свой список, F обратно. например если я вхожу [1,2,3,4,5], я просто хочу, чтобы он дал мне список с [2,6,, 10].

ответ

1

Так первое тело рекурсивной (это уже не так медленно):

stripAndDoubleOdds([H, _|T]) -> [2*H | stripAndDoubleOdds(T)]; 
stripAndDoubleOdds([H]) -> [2*H]; 
stripAndDoubleOdds([]) -> []. 

Теперь хвостовая рекурсия

stripAndDoubleOdds(L) -> lists:reverse(stripAndDoubleOdds(T, [])). 

stripAndDoubleOdds([H, _|T], Acc) -> stripAndDoubleOdds(T, [2*H|Acc]); 
stripAndDoubleOdds([H], Acc) -> [2*H|Acc]; 
stripAndDoubleOdds([], Acc) -> Acc. 

Вы также можете сделать список понимание версии, которая не очень эффективна ни хороший

stripAndDoubleOdds(L) -> 
    [ 2*X || {X, I} <- lists:zip(L, lists:seq(1,length(L))), I rem 2 =:= 1 ]. 
+0

Спасибо, что делает мою жизнь лучше; Я отследил его, используя бумагу, так что теперь яснее. –

+0

Функция не работает с списком ввода, который имеет нечетную длину, последний член отсутствует. – Pascal

+0

Хорошо, я исправлю это. –

1

Чтобы построить хвостовую рекурсивную функцию, которая работает над списком, шаблон всегда является sa me: вызовите функцию с параметром аккумулятора в дополнение к исходному списку. В зависимости от выполняемой функции, аккумулятор может быть целым числом, пустым списком или любым начальным значением, необходимым для вашего алгоритма.

Для примера, вы хотите создать список из первоначального. Аккумулятор будет пустым списком, который вы будете заполнять во время последовательного вызова.

Что касается любого алгоритма рекурсии, вам необходимо определить конечный регистр, который остановит рекурсию и позволит вернуть результат, в вашем случае это когда исходный список опустеет.

Я предлагаю вам это решение, которое проще читать (ИМО)

strd(L) -> strd(L,1,[]). % as you did, I use an integer to detect the odd indices 

strd([],_,Acc) -> lists:reverse(Acc); 
% if the order is important, you should reverse the resulr at the end rather than keeping the right order during 
% the evaluation using the ++ operator. The ++ operator force a copy of the whole list while the construction with 
% [A|B] does not need to copy B. 
strd([_|Q],0,Acc) -> strd(Q,1,Acc); 
strd([H|Q],1,Acc) -> strd(Q,0,[2*H|Acc]). 
% I simply toggle the integer betwwen 1 and 0 to select the right operation using pattern matching 

Hynek пример можно будет работать для любого списка аф длины, добавив второй конец случай:

stripAndDoubleOdds(L) -> stripAndDoubleOdds(L, []). 

stripAndDoubleOdds([H, _|T], Acc) -> stripAndDoubleOdds(T, [2*H|Acc]); 
stripAndDoubleOdds([H], Acc) -> lists:reverse([2*H|Acc]); 
stripAndDoubleOdds(_, Acc) -> lists:reverse(Acc). 
+0

Мне нравятся вам объяснения относительно рекурсии и связанных с ней шаблонов. спасибо –

Смежные вопросы