Чтобы упростить вещи, я собираюсь отделить умножение списков от отображения функции.Таким образом, nestedApply
будет выглядеть следующим образом:
let nestedApply f lsts = mult lsts |> List.collect f
Где mult
выполняет умножение списков и возвращает все комбинации.
Обычно я считаю, что выполнять хвостовую рекурсию лучшечтобы начать сначала с простой рекурсии:
let rec mult lsts =
match lsts with
| [ ] -> [[]]
| h :: rest -> let acc = mult rest
h |> List.collect (fun e -> acc |> List.map (fun l -> e :: l ) )
Итак, эта версия mult
выполняет свою работу, но не использует хвостовую рекурсию.Он служит шаблоном для создания версии хвостовой рекурсии, и я могу проверить, что оба возвращают одно и то же значение:
let mult lsts =
let rec multT lsts acc =
match lsts with
| h :: rest -> h
|> List.collect (fun e -> acc |> List.map (fun l -> e :: l ) )
|> multT rest
| [ ] -> acc
multT (List.rev lsts) [[]]
Версия хвостовой рекурсии multT
использует параметр внутреннего аккумулятора.Чтобы скрыть это, я вкладываю рекурсивную часть в функцию mult
.Я также переворачиваю список, потому что эта версия работает в обратном направлении.
Много раз, когда у вас есть хвостовая рекурсивная функция, вы можете устранить рекурсию, используя функцию fold
:
let mult lsts =
List.rev lsts
|> List.fold (fun acc h ->
h
|> List.collect (fun e -> acc |> List.map (fun l -> e :: l ) )
) [[]]
или foldBack
:
let mult lsts =
List.foldBack (fun h acc ->
h
|> List.collect (fun e -> acc |> List.map (fun l -> e :: l ) )
) lsts [[]]
Обратите внимание на сходство.
Вот решение в скрипке:
https://dotnetfiddle.net/sQOI7q