SORU
6 Ocak 2013, Pazar


Okuyucu Amaç Monad

Haskell için çaylağım. Okuyucu Monad birkaç kez, ama yine de Okuyucu Monad amacı ne anlamıyorum okudum. Okuyucu Monad çok karmaşık ve işe yaramaz gibi görünüyor. Java veya C gibi zorunlu dil , okuyucu monad eğer yanılmıyorsam () için eşdeğer bir terim yok. Bana basit bir örnek ver ve bana biraz daha açık konuşabilir misin. Benim cehalet için özür dilerim.

CEVAP
6 Ocak 2013, Pazar


Korkma! Okuyucu monad aslında o kadar da karışık değil ve kullanımı kolay gerçek bir yardımcı vardır.

Bir monad yaklaşan iki yolu vardır: sorabiliriz

  1. Bu monad nediryapın? İşlemleri ile donatılmış nedir? Bunun neresi güzel ki?
  2. Monad nasıl uygulanır? Nereden doğar?

İlk yaklaşım, okuyucu monad soyut bir türüdür

data Reader env a

bu . böyle

-- Reader is a monad
instance Monad (Reader env)

-- and we have a function to get its environment
ask :: Reader env env

-- finally, we can run a Reader
runReader :: Reader env a -> env -> a

Bunu nasıl kullanırız? Peki, okuyucu monad geçen (örtülü) için iyi bir hesaplama ile bilgi yapılandırma.

Her zaman bir "sabit" bir hesaplama ihtiyacınız olan en çeşitli noktalarda, ama gerçekten senin gibi yapabilmek aynı hesaplama ile farklı değerleri, daha sonra kullanmak bir okuyucu monad.

Okuyucu monadlar da OO insanlar dependency injection çağrı ne için kullanılır. Örneğin, negamax algoritma sık (çok iyi optimize edilmiş formları), iki kişilik oyunda bir pozisyon değerini hesaplamak için kullanılır. Algoritma kendisi olsa umurunda değil ne oyun oynuyor, bunun dışında ihtiyaç belirleme ne "ileri" pozisyon oyunu ve ihtiyacınız yapabilmek için geçerli konumu bir zafer pozisyon.

 import Control.Monad.Reader

 data GameState = NotOver | FirstPlayerWin | SecondPlayerWin | Tie

 data Game position
   = Game {
           getNext :: position -> [position],
           getState :: position -> GameState
          }

 getNext' :: position -> Reader (Game position) [position]
 getNext' position
   = do game <- ask
        return $ getNext game position

 getState' :: position -> Reader (Game position) GameState
 getState' position
   = do game <- ask
        return $ getState game position


 negamax :: Double -> position -> Reader (Game position) Double
 negamax color position
     = do state <- getState' position 
          case state of
             FirstPlayerWin -> return color
             SecondPlayerWin -> return $ negate color
             Tie -> return 0
             NotOver -> do possible <- getNext' position
                           values <- mapM ((liftM negate) . negamax (negate color)) possible
                           return $ maximum values

Bu daha sonra herhangi bir sonlu ile, deterministik çalışacak, iki kişilik bir oyun.

Bu desen bile gerçekten bağımlılık enjeksiyon olmayan şeyler için yararlıdır. Size mali işlerde sanırım, tüm iyi ve güzel olan bir varlık (türev) söylemek, fiyatlandırma için biraz karmaşık mantık tasarımı ve kokuşmuş bir monadlar olmadan yapabilirsiniz. Ama sonra, birden fazla para birimi ile başa çıkmak için programınızı değiştirin. Sinek de para birimleri arasında dönüştürme gerekir. İlk denemen üst düzey bir fonksiyon tanımlamaktır

type CurrencyDict = Map CurrencyName Dollars
currencyDict :: CurrencyDict

spot fiyatlar almak için. Daha sonra kodunuzda bu sözlük çağırabilirsiniz....ama bekleyin! İşe yaramaz! Para birimi sözlük sabittir ve bu yüzden sadece aldığı zaman programınızı hayat için, ama aynı olmalıderlenmiş! Sen ne yapıyorsun Peki? İyi bir seçenek Okuyucu monad kullanmak olacaktır:

 computePrice :: Reader CurrencyDict Dollars
 computePrice
    = do currencyDict <- ask
         --insert computation here

Belki de-case kullanmak en klasik tercümanlar uygulanması. Ama bakmadan önce, başka bir işlevi tanıtmak istiyoruz

 local :: (env -> env) -> Reader env a -> Reader env a

Tamam, Haskell ve diğer fonksiyonel diller lambda calculus dayanmaktadır. Lambda calculus gibi görünen bir sözdizimi vardır

 data Term = Apply Term Term | Lambda String Term | Var Term deriving (Show)

ve bu dil için bir değerlendiricisi yazmak istiyoruz. Bunu yapmak için, bağlama maddeleri (aslında statik ölçüm yapmak istiyorum çünkü kapaklar) ile ilgili bir liste olan bir ortam, takip etmek gerekir.

 newtype Env = Env ([(String,Closure)])
 type Closure = (Term,Env)

İşimiz bittiğinde bir değer (veya hata) dışarı çıkalım:

 data Value = Lam String Closure | Failure String

Yani, yorumlayıcı yazmak sağlar:

interp' :: Term -> Reader Env Value
--when we have lambda term, we can just return it
interp' (Lambda nv t) 
   = do env <- ask
        return $ Lam nv (t,env)
--when we run into a value we look it up in the environment
interp' (Var v) 
   = do (Env env) <- ask
        case lookup (show v) env of
          -- if it is not in the environment we have a problem
          Nothing -> return . Failure $ "unbound variable: "    (show v)
          -- if it is in the environment, than we should interpret it
          Just (term,env) -> local (const env) $ interp' term
--the complicated case is an application
interp' (Apply t1 t2)
   = do v1 <- interp' t1
        case v1 of
           Failure s -> return (Failure s)
           Lam nv clos -> local (\(Env ls) -> Env ((nv,clos):ls)) $ interp' t2
--I guess not that complicated!

Son olarak, önemsiz bir çevre geçirerek kullanabiliriz:

interp :: Term -> Value
interp term = runReader (interp' term) (Env [])

İşte bu kadar. Lambda calculus için tamamen işlevsel bir tercüman.


Yani, bunu düşünmek için başka bir yol sormak: nasıl uygulanıyor? İyi cevap okuyucu monad aslında tüm monadlar ve en gösterişli en basit biridir.

newtype Reader env a = Reader {runReader :: env -> a}

Okuyucu sadece fonksiyonlar için süslü bir isim. Zaten runReader API diğer parçalar ne olacak tanımladık? Her Monad Functor: bir

instance Functor (Reader env) where
   fmap f (Reader g) = Reader $ f . g

Şimdi, bir monad almak için:

instance Monad (Reader env) where
   return x = Reader (\_ -> x)
   (Reader f) >>= g = Reader $ \x -> runReader (g (f x)) x

o kadar korkunç değil. ask gerçekten basittir:

ask = Reader $ \x -> x

local o kadar da kötü değil.

local f (Reader g) = Reader $ \x -> runReader g (f x)

Tamam, okuyucu monad sadece bir fonksiyonudur. Neden Okuyucu var mı? Güzel soru. Aslında, gerek yok!

instance Functor ((->) env) where
   fmap = (.)

 instance Monad ((->) env) where
   return = const
   f >>= g = \x -> g (f x) x

Bu daha basit. Daha fazla, ask id local sadece diğer sırada işlevi kompozisyon!

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • jagadambarecords

    jagadambarec

    13 AĞUSTOS 2008
  • TheFlightsuit

    TheFlightsui

    22 HAZİRAN 2009
  • Thom Hall

    Thom Hall

    24 Kasım 2006