2015-07-25 30 views
6

, bir tane olmadan aşağıdaki kodu yazdım. Standart girdi çizgisini satır satır okur ve boş bir çizgi karşılaşıncaya kadar her satırı tersine çevirir. Ayrıca State kullanarak satırları sayar ve sonunda toplam sayısını görüntüler.Kullanım iki monads monad transformatörleri nasıl kullanılacağını anlamak için

import Control.Monad.State 

main = print =<< fmap (`evalState` 0) go where 
    go :: IO (State Int Int) 
    go = do 
     l <- getLine 
     if null l 
     then return get 
     else do 
      putStrLn (reverse l) 
      -- another possibility: fmap (modify (+1) >>) go 
      rest <- go 
      return $ do 
       modify (+1) 
       rest 

Her satırdan önce geçerli satır numarasını eklemek istedim. Ben StateT ile yapmak başardı:

import Control.Monad.State 

main = print =<< evalStateT go 0 where 
    go :: StateT Int IO Int 
    go = do 
     l <- lift getLine 
     if null l 
     then get 
     else do 
      n <- get 
      lift (putStrLn (show n ++ ' ' : reverse l)) 
      modify (+1) 
      go 

Sorum şu: nasıl monad transformatörleri olmadan sürümünde aynı şeyi?

cevap

2

Sadece her satırda biriken devlet hesaplama çalıştırmak gerekiyordu. Bu O (n²) zamandır, ancak ilk programınız O (n) alanını zaten kullandığından, bu çok da korkunç değildir. Tabii ki, StateT yaklaşımı her yönden üstün! Bunu gerçekten "el ile" yapmak ve bir verimlilik bedeli ödememek istiyorsanız, bir eyalet dönüştürücüsünü kurmak yerine devleti elinizle yönetmeniz yeterlidir. İlk programda Int yerine State'u kullanarak herhangi bir fayda elde edemiyorsunuz.

+2

Bunu farkettim. Benim verimliliğe aşkına bir monad trafosu olmayan versiyonu aramıyorum, sadece benziyor ve umarım monad transformatörleri ihtiyacının daha iyi takdir elde, iki karşılaştırarak şeyler öğrenmek olacağını görmek istiyorum. – ByteEater

+0

Ayrıca, her satırda biriken devlet hesaplamasını çalışan ben kabul edilir ve tam da bu nedenle reddedildi taşıyor çünkü monads kullanmak doğru yolu görünmüyor, basit 'Int' daha iyi bir seçim olacaktır. Üstelik, artımlı olarak bir fark yaratmaz, fakat kavramsal olarak yanlış olur, çünkü 'State' hesaplaması' change (+1) 'eylemlerini hazırlayarak yapılır; '(+ uzunluk l)' değiştir, gerektiği gibi çalışmayacak. – ByteEater

+0

@ByteEater, bunu bir monad trafosu olmadan yapmanın yolu, sadece "Int" (el sıkıcı) etrafından geçmek ya da bir "IORef" kullanmak (IO 'benzeri şeylerle sınırlı olmak ve potansiyel olarak verimsiz olmaktır) kutu kaçınılmaz veya güncellemeler nadirdir). Başka ne aradığını bilmiyorum. – dfeuer

1

Belki bu Aradığınız nedir?

main = print =<< fmap (`evalState` 0) (go get) where 
    go :: State Int Int -> IO (State Int Int) 
    go st = do 
    l <- getLine 
    if null l 
    then return (st >>= \_ -> get) 
    else do 
      let ln = evalState st 0 
      putStrLn(show ln ++ ' ' : reverse l) 
      go (st >>= \_ -> modify (+1) >>= \_ -> get) 

Buradaki fikir, o zaman her adımda değerlendirebilir eyalet hesaplama, kadar bina go kuyruk özyinelemeli yapmaktır.

önceki devlet hesaplama zorlandığında tembel değerlendirme altında, biz olmadan yeniden gerekir rağmen bu sürümü, sabit bir boyuta devlet hesaplama boyutunu bağlı olacaktır

DÜZENLEME yeniden değerlendirilmesi o yüzden ... bunlar temelde aynı olduğunu

main = print =<< fmap (`evalState` 0) (go get) where 
    go :: State Int Int -> IO (State Int Int) 
    go st = do 
    l <- getLine 
    if null l 
    then return st 
    else do 
      let ln = evalState st 0 
      putStrLn(show ln ++ ' ' : reverse l) 
      go (modify (\s -> s+ln+1) >>= \_ -> get) 
+0

Nitekim, bu durum "Durum Int" cinsinden hesaplanan geçerli değerin, doğrusal bir sayıyı yeniden hesaplamanın maliyetine (referans olarak şeffaf bir dille ve akıllı bir derleyici ile bu maliyetlerden kaçınılmış olabilir) kullanılmasına izin verir. Bunu düşünmüştüm, ancak bunun bir transformatör kullanan ikinci programımın 'State' ve' IO 'monadları ile kanonik eşdeğeri olduğuna ikna olmamıştım. – ByteEater

+0

Bunun, ilk denememden daha fazla "kanonik" olduğundan şüphe duyuyorum, ancak ikinci düzenlemem, önceki durum hesaplamasını atlayıp, mevcut durumu son kullanıcının sonucuna artırarak sabit bir boyuta eyalet hesaplamasını bağlayabilir. 1. Daha iyi? – Matt

+0

Daha iyi, tarif ettiğiniz şekilde. Ancak, koddaki hangi iyileştirmenin (üstün biçime göre yeniden oluşturulacak) gerçekte bir monad transformatörüyle gerçekleştirilebileceğini açıkça görebilmek için StateT kullanan sürümle kolayca karşılaştırılamaz. – ByteEater

10

yaşadığınız sorun StateT s IO a elle seçemeden s -> IO (s, a) değil IO (s -> (s, a)) olmasıdır tahmin ediyorum! Bu içgörüye sahip olduğunuzda, bunu nasıl yapacağınızı görmek oldukça kolay:

go :: Int -> IO (Int, Int) 
go s = do 
    l <- getLine 
    if null l 
    then return (s, s) 
    else do 
     putStrLn (show s ++ ' ' : reverse l) 
     go (s+1)