Haskell: аккумулятор с длиной, суммирование, суммирование квадрата списка

Я новичок в Haskell и пытаюсь реализовать функцию с аккумулятором, но не знаю, как правильно ее использовать.

Вот функция, использующая список чисел и возвращающая тройку (Int, Int, Int) с длиной, суммой и суммой квадратов списка с помощью встроенной функции:

stats1 :: [Int] -> (Int,Int,Int)
stats1 xs = (length xs, sum xs, sumsq xs)

sumsq :: [Int] -> Int
sumsq [] = 0
sumsq (x:xs) = (^2) x + sumsq xs

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

stats2 :: [Int] -> (Int,Int,Int)
stats2 [] = (0,0,0)
stats2 (x:xs) = (len+1, acc+x, sumsquare + x*x ) (stats2 xs)
  where len = 0
        acc  = 0
        sumsquare = 0

Я получил сообщение об ошибке:

Couldn't match expected type ‘(Int, Int, Int) -> (Int, Int, Int)’
                with actual type ‘(Integer, Int, Int)’
    The function ‘(len + 1, acc + x, sumsquare + x * x)’
    is applied to one argument,
    but its type ‘(Integer, Int, Int)’ has none
    In the expression:
      (len + 1, acc + x, sumsquare + x * x) (stats2 xs)
    In an equation for ‘stats2’:
        stats2 (x : xs)
          = (len + 1, acc + x, sumsquare + x * x) (stats2 xs)
          where
              len = 0
              acc = 0
              sumsquare = 0

Как я мог достичь той же цели из stats1 с помощью аккумулятора? Спасибо.


person Woden    schedule 28.05.2021    source источник


Ответы (2)


Чтобы использовать этот стиль передачи аккумулятора, вам сначала нужно объявить рекурсивную функцию, которая принимает параметр дополнительного накопления. Это можно сделать в предложении where, в моем примере с recurse. При первоначальном вызове recurse кортеж инициализируется с помощью (0,0,0). На каждом шаге (второй шаблон recurse) значения накапливаются, и базовый вариант (первый шаблон recurse) возвращает результирующий кортеж.

stats2 :: [Int] -> (Int,Int,Int)
stats2 l = recurse l (0,0,0) where
        recurse [] tuple = tuple
        recurse (x:xs) (lenr,sumr,sumsq) = recurse xs (lenr+1, sumr+x, sumsq + x*x )

позвонить с:

> stats3 [1,2,3]
(3,6,14)

Проблема с вашей попыткой заключается в том, что вы пытаетесь рекурсивно вызвать stats2 с кортежем в качестве дополнительного параметра, но вы изменили это, так что кортеж является фактической функцией в этой конструкции (которая не принимает параметров, отсюда и сообщение об ошибке). Кроме того, если бы это работало, значения инициализировались бы нулями на каждом шаге и в базовом случае.

person jf_    schedule 28.05.2021
comment
Не могли бы вы рассказать немного о l = recurse l (0,0,0) части? Или любую документацию, которой я мог бы следовать? Спасибо за решение. - person Woden; 28.05.2021
comment
recurse — это просто функция, определенная здесь как часть предложения where. В stats2 эта функция вызывается с начальным значением аккумулятора, т.е. (0,0,0). Список l является единственным параметром stats2, который просто передается recurse для работы. - person jf_; 28.05.2021

Аккумулятор — это переменная, которую необходимо передать в функцию.

stats_h :: (Int, Int, Int) -> [Int] -> (Int, Int, Int)
stats_h p [] = p
stats_h (len, sum, sumsq) (x:xs) =
  stats_h (len+1, sum + x, sumsq + x*x) xs
 
stats :: [Int] -> (Int, Int, Int)
stats = stats_h (0, 0, 0)
λ stats_h (0, 0, 0) [1, 2, 3]
(3,6,14)
λ stats [1, 2, 3]
(3,6,14)
person rajashekar    schedule 28.05.2021