Как подсчитать количество раз, когда была вызвана функция, способ FP

В настоящее время я работаю через SICP с Haskell. Упражнение 1.15 спрашивает, сколько раз вызывается функция. Идея, вероятно, в том, что вы должны использовать метод подстановки, но я хотел бы знать, как это сделать в коде.

На императивном языке можно хранить глобальную переменную и увеличивать ее каждый раз, когда вызывается функция. Но как бы вы это сделали в Haskell (или чисто функциональном)?

Ответы

Ответ 1

Вы можете использовать монаду Writer, чтобы выполнить это, при условии, что все вызовы рассматриваемой функции могут быть сгруппированы в блок do:

import Control.Monad.Writer

myFunc :: Int -> Int -> Writer (Sum Int) Int
myFunc a b = tell (Sum 1) >> return (a + b)

callMyFunc :: ((Int, Int, Int), Sum Int)
callMyFunc = runWriter $ do a <- myFunc 2 3
                            b <- myFunc 8 7
                            c <- myFunc 3 5
                            return (a, b, c)

main = putStrLn $
    "myFunc was called "
        ++ show (getSum $ snd callMyFunc)
        ++ " times and produced "
        ++ show (fst callMyFunc)

Какие выходы:

myFunc was called 3 times and produced (5,15,8)

Ответ 2

Мне кажется, что вам нужно иметь какой-то счетчик, независимо от того, идете ли вы с функциональным или нефункциональным способом. В Haskell вы можете использовать State Monad для отслеживания состояния:

import Control.Monad.State

someFunc x = do
    num <- get
    put (num + 1)
    return $ x * x

runSomeFuncs = do
    someFunc 1
    someFunc 2
    someFunc 3

main = do
    let (res, state) = runState runSomeFuncs 0
    putStrLn ("result: " ++ (show res))
    putStrLn ("# of calls: " ++ show state)

Здесь вы хотите отслеживать, сколько раз было вызвано someFunc, поэтому мы передаем целое число в качестве состояния и приращиваем целое число при каждом вызове функции с помощью:

num <- get
put (num + 1)

а затем увеличьте его на 1 и put обратно. Если вы запустите этот script, он должен распечатать

result: 9
# of calls: 3