Просто для удовольствия, давайте сделаем некоторую отладку в стиле printf
:
> aggregateList (fun acc x -> printf "%i " x; acc + x) 0 [1..10];;
10 9 8 7 6 5 4 3 2 1 val it : int = 55
Похоже, что функция эквивалентна List.foldBack
(или fold_right
на других языках): она обходит каждый элементв списке справа налево и вызывает для них функцию f
.
Давайте перепишем функцию несколькими различными способами:
// functional version
let rec foldBack f seed = function
| [] -> seed
| x::xs -> let res = foldBack f seed xs in f res x
// imperative version
let foldBack f seed xs =
let mutable result = seed
for x in List.rev xs do
result <- f result x
result
// C# equivalent
public static U FoldBack<T, U>(Func<T, U> f, U seed, IEnumerable<T> xs) {
foreach(T x in xs.Reverse())
seed = f(seed, x);
return seed;
}
Вы бы использовали функциюкак это:
let sum = foldBack (+) 0 [1..10] // returns 55
let sumOfSquares = foldBack (fun acc x -> acc + x * x) 0 [1..10];; // 385
Я не понимаю, во второй ветви в этом сопоставлении с образцом: f rem hd.Может ли кто-нибудь мне помочь?
Итак, давайте начнем с того, что мы уже знаем о функциях F #:
f
- это функция типа int -> int -> int
.Вы передаете функции, как если бы они были любой другой переменной, например, int или строкой. - Вы вызываете функции, передавая разделенный пробелами список аргументов.
f rem hd
вызывает функцию f
с двумя аргументами, rem
и hd
. - Последнее выражение, вычисленное в функции, обрабатывается как возвращаемое значение функции.
Возвращаясь к исходной функции:
let rec aggregateList (f:int->int->int) init list =
match list with
| [] -> init
| hd::tl ->
let rem = aggregateList f init tl // 1
f rem hd // 2
В строке 1 мы вызываем aggregateList
рекурсивно с tl
.Так как список становится все меньше и меньше, в конечном итоге мы собираемся перейти к нулевому регистру, который возвращает init
.
В строке 2 f rem hd
- это возвращаемое значение функции.Однако, поскольку мы возвращались к стеку, пока шли к концу списка, мы будем вызывать эту функцию по одному для каждого элемента (в порядке справа налево), когда мы возвращаемся к трассировке стека.
Учитывая aggregateList (+) 0 [1..10]
, нулевой регистр возвращает 0
, поэтому мы вызываем:
- возвращаемое значение = f rem hd = f 0 10 = 0 +10 = 10
- возвращаемое значение = f rem hd = f 10 9 = 9 + 10 = 19
- возвращаемое значение = f rem hd =f 19 8 = 19 + 8 = 27
- возвращаемое значение = f rem hd = f 27 7 = 27 + 7 = 34
- возвращаемое значение = f rem hd = f 34 6 = 34 + 6 = 40
- возвращаемое значение = f rem hd = f 40 5 = 40 + 5 = 45
- возвращаемое значение = f rem hd = f 45 4 = 45 + 4 = 49
- возвращаемое значение = f rem hd = f 49 3 = 49 + 3 = 52
- возвращаемое значение = f rem hd = f 52 2 = 52 + 2 = 54
- возвращаемое значение = f rem hd = f 54 1 =54 + 1 = 55
В списке больше нет элементов, поэтому вся функция возвращает 55
.
Как вы можете себе представить, вложенные вызовы в aggregateList
оцениваются таким образом для списка длины n
:
f (f (f (f (f (f (f (f init hd n ) hd n-1 ) hd n-2 ) hd) n-3 ) ... HD 2 ) HD 1 ) HD 0