문제

내가 본 다른 게시물에 대한 이, 지만,거기에 깨끗한 방법을 이용?

으로서 2 부,수것도 할 수 없이 만드는 기능 monadic?

도움이 되었습니까?

해결책

이것은 크게 다음과 같이 http://www.haskell.org/haskellwiki/Memoization.

당신이 원하는 기능의 유형(a>b).지 않는 경우 자체를 호출,다음 그냥 쓸 수 있는 간단한 래퍼 캐시 하는 값을 반환합니다.이 를 저장하는 가장 좋은 방법이 매핑에 따라 달라지는 속성의 수 exploit.주문은 꽤 많은 최소화합니다.와 정수 을 구성할 수 있습니다 무한한 게으른 목록하거나 나무를 들고 값입니다.

type Cacher a b = (a -> b) -> a -> b

positive_list_cacher :: Cacher Int b
positive_list_cacher f n = (map f [0..]) !! n

integer_list_cacher :: Cacher Int b
integer_list_cacher f n = (map f (interleave [0..] [-1, -2, ..]) !!
    index n where
        index n | n < 0  = 2*abs(n) - 1
        index n | n >= 0 = 2 * n

그래서,그것은 재귀적입니다.당신은 그것을 필요로 전화하지 않지만, 이 memoized 버전을 전달하는 대신:

f_with_memo :: (a -> b) -> a -> b
f_with_memo memoed base = base_answer
f_with_memo memoed arg  = calc (memoed (simpler arg))

이 memoized 버전은 물론,우리가 무엇을 하려고 하다.

하지만 우리가 시작할 수 있을 만드는 기능을 캐시 입력:

우리를 구성할 수 있습 중 하나에 의해 수준에서 전달하는 기능을 만듭니다 구조 캐시하는 값입니다.를 제외하고 만들어야 하는 버전을 f 그 캐시 기능을 전달합니다.

덕분에 게으름,이것은 아무 문제 없습니다:

memoize cacher f = cached where
         cached = cacher (f cached)

그런 다음 우리에게 필요한 것은 그것을 사용:

exposed_f = memoize cacher_for_f f

이 문서에서는 방법에 대한 힌트를 제공합 형식을 사용하려면 클래스를 선택에 입력 기능을 위보다는 선택을 명시적 캐싱 기능입니다.이는 정말 좋은이 될 수 있습니다--보다는 명시적으로 건설 캐시한 각각의 조합을 입력 유형,우리가 할 수 있습을 암시적으로 결 캐시 유형 a 와 b 으로 기능을 복용하고 b.

마지막 주의:를 사용하여 이 게으른 기술을 의미 캐시 축소하지 않, 그것은 단지 자랍니다.당신이 대신 사용하 IO 슷하지만 데이터베이스를 관리할 수 있습니다 이지만, 그 일을 현명하게 사용에 따라 다릅니다.

다른 팁

Hackage의 패키지 데이터-모모기는 많은 재사용 가능한 메모 화 루틴을 제공합니다. 기본 아이디어는 다음과 같습니다.

type Memo a = forall r. (a -> r) -> (a -> r)

즉 a에서 모든 함수를 메모 할 수 있습니다. 그런 다음 모듈은 몇 가지 프리미티브를 제공합니다 (예 : unit :: Memo () 그리고 integral :: Memo Int) 및보다 복잡한 메모 테이블을 구축하기위한 콤비네이터 ( pair :: Memo a -> Memo b -> Memo (a,b) 그리고 list :: Memo a -> Memo [a]).

UnsafeperFormio를 사용하여 Jonathan의 솔루션을 수정하여 기능의 "순수한"메모 링 버전을 만들 수 있습니다.

import qualified Data.Map as Map
import Data.IORef
import System.IO.Unsafe

memoize :: Ord a => (a -> b) -> (a -> b)
memoize f = unsafePerformIO $ do 
    r <- newIORef Map.empty
    return $ \ x -> unsafePerformIO $ do 
        m <- readIORef r
        case Map.lookup x m of
            Just y  -> return y
            Nothing -> do 
                    let y = f x
                    writeIORef r (Map.insert x y m)
                    return y

이것은 재귀 기능과 함께 작동합니다.

fib :: Int -> Integer
fib 0 = 1
fib 1 = 1
fib n = fib_memo (n-1) + fib_memo (n-2)

fib_memo :: Int -> Integer
fib_memo = memoize fib

Altough이 예제는 하나의 정수 매개 변수가있는 함수입니다. Memoize 유형은 비슷한 유형을 취하는 모든 함수와 함께 사용할 수 있음을 알려줍니다. 둘 이상의 매개 변수가있는 함수가있는 경우 메모 리즈를 적용하기 전에 튜플로 그룹화하십시오. FI :

f :: String -> [Int] -> Float
f ...

f_memo = curry (memoize (uncurry f))

보다 명령적인 언어에서 직접 번역을하면서 나는 이것을 생각해 냈습니다.

memoize :: Ord a => (a -> IO b) -> IO (a -> IO b)
memoize f =
  do r <- newIORef Map.empty
     return $ \x -> do m <- readIORef r
                       case Map.lookup x m of
                            Just y  -> return y
                            Nothing -> do y <- f x
                                          writeIORef r (Map.insert x y m)
                                          return y

그러나 이것은 어떻게 든 불만족 스럽습니다. 또한, data.map 매개 변수를 인스턴스로 제한합니다 주문.

당신의 주장이 자연 숫자가 될 경우, 당신은 간단하게 할 수 있습니다.

memo f = let values = map f [0..]
     in \n -> values !! n

그러나 이는 실제로 스택이 넘쳐나는 데 도움이되지 않으며 재귀적인 통화와는 작동하지 않습니다. 더 멋진 솔루션을 볼 수 있습니다 http://www.haskell.org/haskellwiki/memoization.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top