List.Generate, накопленная сумма, списки, записи и какие-то метаданные

#АнатомияФункций – List.Generate



Всем привет!

В своё время мы уже разбирали использование List.Generate и считали накопленную сумму.

Стандартный расчёт выглядит так (описание работы смотрим по ссылке):

let

lst = List.Buffer({1..250000}),

n=List.Count(lst),

gen = List.Generate(()=>[i=0,s=lst{i}],

(x)=>x[i]<n,

(x)=>[i=x[i]+1,s=x[s]+lst{i}],

(x)=>x[s])

in

gen




И тут был задан вопрос – а обязательно ли использовать запись (record) при вычислении?

Ну вообще-то необязательно, давайте примотаем списки:

let

lst = List.Buffer({1..250000}),

n=List.Count(lst),

gen = List.Generate(()=>{0,lst{0}},

(x)=>x{0}<n,

(x)=>{x{0}+1,x{1}+lst{x{0}+1}},

(x)=>x{1})

in

gen


т.е. вместо записи через вычисления тащим список из двух значений, приходится дважды вычислять счётчик, но в целом это работает.



Однако, пока писал ответ, возникла другая мысль – а давайте примотаем метаданные:

let

lst = List.Buffer({1..250000}),

n = List.Count(lst),

gen = List.Generate(()=>0 meta [s=lst{0}],

(x)=>x<n,

(x)=>Value.ReplaceMetadata(x+1,[s=Value.Metadata(x)[s]+lst{x+1}]),

(x)=>Value.Metadata(x)[s])

in

gen


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



Поэтому делаем выводы: накопленная сумма – это тот случай, когда НА ЗАПИСЯХ быстрее, а вариант с метаданными, пусть чуть медленнее, зато весьма экзотичен.

Решайте задачи разными путями – это бывает интересно 😉



Надеюсь, было полезно.

Всех благ!

@buchlotnik