Всем привет,
Разбираюсь с continuation-passing style в качестве упражнения, на небольшой утилите для сенсоров, которую использую в работе с данными. loadReadings генерирует список и заменяет загрузчик CSV, который я ещё не написал. Дальше я считаю сумму значений двумя способами. Обе функции написаны так, что рекурсивный вызов — последнее действие, насколько я это понимаю.
let rec sumK (xs: int list) (k: int -> int) =
match xs with
| [] -> k 0
| x :: rest -> sumK rest (fun s -> k (s + x))
let rec sumCheckedK (xs: Reading list) (k: int -> int) =
match xs with
| [] -> k 0
| r :: rest ->
try
if r.Value < 0 then
failwithf "faulty reading from %s" r.Sensor
sumCheckedK rest (fun s -> k (s + r.Value))
with ex ->
log ex.Message
sumCheckedK rest k
В main я вызываю sumK values id, затем sumCheckedK readings id, по умолчанию с 1 000 000 показаний (каждое 250000-е — битое, -1).
dotnet run(Debug): почти сразу падает сStack overflow.и огромным повторяющимся трейсом, до вывода какого-либо итога.dotnet run -c Release: итогsumKвыводится корректно, потом сноваStack overflow., с повторяющимся фреймом, где упоминаетсяsumCheckedK.- С
-- 10000обе сборки работают.
Сначала я подозревал построение списка, но запуск в Release показывает, что с ним всё нормально. Потом задумался, не сдаётся ли JIT на глубоко вложенных замыканиях. Ничего особенного в fsproj я не задаю (net8.0, .NET 8 SDK, Rider на Mac с M-серией).
Это одна проблема или две? Хотелось бы разобраться, а не просто увеличить размер стека потока.