당신은 어떻게 일반,그러나 다른 클라이언트 기능을 하스켈?
-
02-07-2019 - |
문제
내가 본 다른 게시물에 대한 이, 지만,거기에 깨끗한 방법을 이용?
으로서 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.