Как уже сказал Джон, ваша функция не является хвостовой рекурсивной. Основная проблема заключается в том, что ему необходимо рекурсивно вызывать себя несколько раз (один раз для каждого элемента в списке xs
, что выполняется в лямбда-функции, переданной в List.map
).
В случае, когда вам действительно нужно сделать несколько рекурсивных вызовов, возможно, единственными вариантами будут использование стиля передачи продолжения или, например, императивного стека. Идея продолжения заключается в том, что каждая функция будет принимать другую функцию (в качестве последнего аргумента), которая должна выполняться, когда станет доступен результат.
В следующем примере показаны обычная версия (слева) и продолжение на основе (справа)
let res = foo a b fooCont a b (fun res ->
printfn "Result: %d" res printfn "Result: %d" res)
Чтобы написать функцию в стиле передачи продолжения, вам также потребуется использовать fold
функцию, основанную на продолжении. Сначала вы можете избежать использования map
, переместив операцию, выполненную в map
, в лямбда-функцию fold
:
List.fold g acc (List.map (fun xxs -> myfunc f xxs) xs)
Становится:
List.fold (fun state xxs -> g state (myfunc f xxs)) acc xs
Затем вы можете переписать код следующим образом (обратите внимание, что и f
, и g
, которые вы не указали в своем вопросе, теперь являются функциями, основанными на продолжении, поэтому они принимают дополнительный аргумент, который представляет продолжение):
// The additional parameter 'cont' is the continuation to be called
// when the final result of myfunc is computed
let rec myfunc' f (T (x,xs)) cont =
if (List.isEmpty xs) then
// Call the 'f' function to process the last element and give it the
// current continuation (it will be called when 'f' calculates the result)
f x cont
else
// Using the continuation-based version of fold - the lambda now takes current
// element 'xxs', the accumulated state and a continuation to call
// when it finishes calculating
List.foldCont (fun xxs state cont ->
// Call 'myfunc' recursively - note, this is tail-call position now!
myfunc' f xxs (fun res ->
// In the continuation, we perform the aggregation using 'g'
// and the result is reported to the continuation that resumes
// folding the remaining elements of the list.
g state res cont)) acc xs cont
Функция List.foldCont
является версией fold
, основанной на продолжении, и может быть записана следующим образом:
module List =
let rec foldCont f (state:'TState) (list:'T list) cont =
match list with
| [] -> cont state
| x::xs -> foldCont f state xs (fun state ->
f x state cont)
Поскольку вы не опубликовали полный рабочий пример, я не смог действительно протестировать код, но я думаю, что он должен работать.
person
Tomas Petricek
schedule
29.05.2012