2012-03-29 10 views
14

Obecnie pracuję poprzez SICP z Haskell. W ćwiczeniu 1.15 pytamy, ile razy wywoływana jest funkcja. Prawdopodobnie powinieneś użyć metody zastępowania, ale chciałbym wiedzieć, jak to zrobić w kodzie.Jak policzyć liczbę razy, gdy funkcja została wywołana, sposób FP

W języku imperatywnym można przechowywać zmienną globalną i zwiększać ją za każdym razem, gdy wywoływana jest funkcja. Ale jak byś się tym zajął w Haskell (czy w czysto funkcjonalny sposób)?

+0

jeśli tylko do debugowania, można użyć '' trace' Debug.Trace' użytkownika funkcja. Łamie referencyjną przejrzystość, ale jeśli debugujesz lub odkrywasz, to powinno być w porządku. – GManNickG

+3

Ahem, myślę, że SICP rzeczywiście oczekuje, że liczba będzie wynosić 0 lub 1 - najważniejsza jest "czy funkcja jest oceniana czy nie?". Nie ma czysto funkcjonalnego sposobu liczenia ile razy funkcja jest wywoływana. Poniższe odpowiedzi dostarczają dobrych monadycznych "rozwiązań", ale zależą one od funkcji, która ma być liczona jako "uruchomiona" w obrębie odpowiedniej monady. Jest to ważny wybór - nie liczą ogólnych funkcji. –

Odpowiedz

18

Można użyć Writer monady do osiągnięcia tego celu, pod warunkiem, że wszystkie połączenia do funkcji w pytaniu mogą być grupowane w do bloku:

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) 

które wyjścia:

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

Wydaje mi się, że potrzebna jest jakaś licznik, niezależnie od tego, czy chodzi o funkcjonalny czy niefunkcjonalny sposób. W Haskell, można użyć State monada śledzić stan:

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) 

Tutaj, chcesz śledzić, ile razy someFunc został sprawdzony, tak mijamy liczbę całkowitą jako państwa i zwiększamy całkowitą każdym razem, gdy funkcja jest wywoływana za pomocą:

num <- get 
put (num + 1) 

a następnie zwiększać ją o 1 i put go z powrotem. Po uruchomieniu tego skryptu, należy go wydrukować

result: 9 
# of calls: 3 
+14

Lub 'modify (+ 1)' zamiast 'get'ting i' put'ting. – dave4420

+7

Użycie opcji "Stan" pozwala również na zależność od licznika i powoduje niepotrzebne uzależnienie danych (na przykład ograniczenie równoległości). Zamiast tego możesz użyć 'Writer',' Sum Int' jako 'Monoid', a następnie' tell (Sum 1) ', aby zwiększyć licznik. To ładnie wykorzystuje asocjatywność dodawania i pozwala nawet CSE przy wielokrotnym wywoływaniu tego samego obliczenia Writera. – Peaker

+0

@Peaker Rozwiń to i dodaj jako odpowiedź. – dave4420

Powiązane problemy