2015-09-10 14 views
5

Haskell'deki monad sistemini anlamaya çalışıyorum. Önceki programlama deneyimimin yaklaşık% 80'i C'dedir, ancak ironik olarak Haskell'in zorunlu kısmı anlamak en zor olanıdır. Liste manipülasyonu ve tembel değerlendirme çok daha açıktı. Her neyse, ghc'yi bu kodu kabul etmek istiyorum. Kodun hiç mantıklı olmadığını biliyorum. En açıkçası, Bool'u geçiyorum, burada IO Bool bekleniyor. Ama bu tek sorun değil. Bunun aptalca bir soru olduğunu biliyorum, ama lütfen bana Haskell dilini daha iyi anlamama yardımcı olun. İştezorunlu döngü

import Control.Monad 

while :: Monad m => m Bool -> m() -> m() 
while cond action = do 
    c <- cond 
    when c $ do 
    action 
    while cond action 

main :: IO() 
main = do 
    i <- 0 
    while (i < 10) $ do 
    i <- i + 1 
    print i 

Sonunda öyle yapmıştım. allocaArray'un gerekli olmadığını biliyorum, ama kullanmak çok eğlenceliydi. Haskell'in gerçekten hiçbir sınırı yok, çok güçlü.

import Control.Monad 
import Data.IORef 
import Foreign.Ptr 
import Foreign.Storable 
import Foreign.Marshal.Array 

while :: Monad m => m Bool -> m() -> m() 
while cond action = do 
    c <- cond 
    if c then do 
    action 
    while cond action 
    else return() 

main :: IO() 
main = do 
    let n = 10 
    allocaArray n $ \p -> do 
    i <- newIORef 0 
    while (liftM (< n) (readIORef i)) $ do 
     i2 <- readIORef i 
     poke (advancePtr p i2) i2 
     modifyIORef i (+ 1) 
    writeIORef i 0 
    while (liftM (< n) (readIORef i)) $ do 
     i2 <- readIORef i 
     (peek $ advancePtr p i2) >>= print 
     modifyIORef i (+ 1) 
+2

Açık olanla başlayalım. Değişken bir değişken değil ve sadece bir monadınız olduğu için bir tane haline gelmiyor. 'i <- i + 1 'iki farklı' i 'anlamına gelir. –

+1

Haskell'de "while" konstrüksiyonu oldukça nadiren kullanılır, bence tam olarak Haskell'de, "değişkenleri" doğal olarak, dilleri zorunlu kılan insanlar için kullanmanıza izin vermez. * Aynı şeyi daha garip bir şekilde yapabilirsin, ama 'Data.IORef' veya 'Control.Concurrent.MVar' gibi değişken referansları kullanmalısın. Gerçekten değişebilir bir güncellemeye ihtiyacınız olmadıkça, işlevsel olarak ifade etmek genellikle daha iyidir. –

+1

Bu şekilde bir "IORef" kullanılmasının döngü sayacının "kutulu" olmasına yol açtığını unutmayın, böylece her bir yinelemede yeni bir "Int" kutusu tahsis edilir ve sayaca erişilmesi işaretçi indirmeyi içerir. Daha işlevsel bir sayıcıyla uğraştığınızda, GHC genellikle daha hızlı kodlara yol açarak kutuyu kaldırabilir. – dfeuer

cevap

6

typechecking verdiği kodu tutmak iki şey vardır:

  1. Sizin while işlevi bir IO Bool beklediğini ancak bunu tip Bool bir ifadesidir i < 10 verir. Bool'u IO Bool'a dönüştürmek için return'u kullanın.

  2. i <- 0 yazdığınızda, literal sıfırı, kullanılmayan bir monadik değer olarak kullanmayı denersiniz.

    main = do 
        i <- 0 
        ... 
    

    Bunu düzeltmek için, ayrıca return aracılığıyla 0 teşvik edebilir

    main = 0 >>= \i -> do ... 
    

denk olduğunu unutmayın.

Dolayısıyla, size Ancak, bu yine de ne yapmak niyetinde yapmayacağım

main :: IO() 
main = do 
    i <- return 0 
    while (return (i < 10)) $ do 
     i <- return (i + 1) 
     print i 

ile bitirmek: nedeni i <- return (i + 1) ilk (en soldaki) ii daha farklı olmasıdır i <- return 0. , numaralı gölgelendirme değişkenini oluşturuyorsunuz, aynı ada sahip yeni bir değişken oluşturuyorsunuz, sonra yazdırıyorsunuz. Yani aslında hiç bir karşı vuruş yapmazsın.

Eğlenceyi bozmak istemiyorum, ancak gerçekten takılırsanız: whileM işlevi dahil olmak üzere bir çift kullanışlı monadik döngü işlevi ortaya çıkaran bir monad-loops paketi var.

+0

hakkında harika bir yazıdır OP'deki 'while', bu paketin' whileM_' ile eşdeğerdir (biraz daha az genel tip ancak daha önemlisi); Sorunun nerede olduğunu sanmıyorum. –

+0

Çevresinde ['döngüler '(https://hackage.haskell.org/package/loops) ve [' control-monad-loop'] (https: //hackage.haskell) gibi başka döngü kütüphaneleri vardır. OP'nin arzuları ile daha iyi uyumlu tesisler sunan org/package/control-monad-loop). 'monad-döngüler 'bazı serin eşzamanlılık araçlarına sahiptir, ancak aksi halde daha az genel görünmektedir. – dfeuer

9

Bu yaklaşımdaki sorun, i'un değiştirilebilen bir değişken olmamasıdır. IORef'u kullanabilirsiniz, ancak her iterasyonda mevcut durumu geçmek için daha işlevsel bir yaklaşım olacaktır.

sonra
whileM :: Monad m => (a -> Bool) -> (a -> m a) -> a -> m() 
whileM test act init = 
    when (test init) $ (act init) >>= whileM test act 

Eğer

whileM (< 10) (\i -> print i >> return (i + 1)) 0 
3

yerel devlet (Devlet ve ilişkili monad trafosu) ile bir çözüm yapabilirsiniz küresel aksine,: Geçerli değerini almak için whileM vücudu ve koşulları yazabilirsiniz durumu (IORef ve arkadaşları):

import Control.Monad 
import Control.Monad.State 

while :: Monad m => m Bool -> m() -> m() 
while cond action = do 
    c <- cond 
    when c $ do 
    action 
    while cond action 

main :: IO() 
main = do 
    runStateT (while cond body) 1 
    return() 

body :: StateT Integer IO() 
body = do 
    x <- get 
    liftIO $ print x 
    put (x + 1) 
    return() 

cond :: StateT Integer IO Bool 
cond = do 
    x <- get 
    return (x < 10) 

döngü gövdesi ve döngü koşulu açık ve netlik için adlandırılmış olan; ör. while (liftM (< 10) get) body.