2013-08-25 81 views
0

我對Haskell的IO有點新,雖然我經常閱讀它,但我的代碼仍然無法工作。閱讀文件的行,排序並返回中間元素

我希望應用程序做什麼:

  1. 讀取文件的所有行(FILE1.TXT,FILE2.TXT,...),其中都含有數字在每行(浮像1.12345)
  2. 排序所有這些行(字符串排序或浮動的排序並不重要,我想串排序是更快?)
  3. 獲取列表的中間元素,並打印出來

這是我的代碼到目前爲止。我可以保證函數「middle」在傳遞[String]時工作正常。

middle :: [a] -> a 
middle xs = (drop ((l - 1) `div ` 2) xs) !! 0 
      where l = length xs 

getSortedMiddleElement :: Int -> String 
getSortedMiddleElement i = do 
    dat <- readFile $ "file" ++ (show i) ++ ".txt" 
    return $ middle $ sort $ lines dat 

我從一個「內部 - >內容」,呼籲getSortedMiddleElement功能(我用耶索德),其中數字正在通過URL傳遞和中間元素應該返回給用戶。爲了讓內容脫離字符串,它需要是「字符串」,而不是「IO字符串」......如何輕鬆實現?

在此先感謝!

+2

一種可能性是將類型更改爲「Int - > IO String」,然後像處理該函數一樣處理,例如, 'readFile'或任何其他IO功能。 – kqr

+0

謝謝,這聽起來不錯!我該如何處理'getTestR :: Int - > Handler RepPlain',它會返回HTTP響應?'getTestR :: Int - > IO Handler RepPlain'不起作用:( – sibbl

+0

我希望我可以告訴你,但我絕不是Yesod的專家。:( – kqr

回答

5

您的類型簽名表示您的函數是純的(即它需要一個Int並返回一個String),但在裏面,您正在執行IO! Haskell不會讓你編寫這樣的函數。你從文件中讀取的任何內容都會永久卡在IO monad中,這就是(當然,除了不安全的函數)。

在這種情況下,結果並不那麼糟糕,因爲Yesod是一個嚴重的基於IO的框架。所有的網絡流量都停留在IO monad中!

當你在一個monad變壓器堆棧中時,你可以在堆棧的每個級別訪問monadic計算,但只有其中一個直接。您可以使用lift將計算從堆棧中的單層移動到已轉換的monad。如果IO位於堆棧中,無論層數有多少,都可以通過liftIO直接訪問其操作。

所以,如果你有type T = ReaderT String IO那麼你可能有一個功能foo :: Int -> T String。在此函數中,您將在T monad中操作,它將IO monad與Reader monad功能進行轉換。在這種情況下,您可以說lift readFile,而不是獲得IO String結果,您將得到T String結果!這只是IO String包裝的ReaderT類型,所以不要認爲我們做了任何棘手的事情,比如逃脫IO monad。這可能是一個有點混亂,所以讓我們來看一個例子:

import Control.Monad.Reader (ReaderT) 
import Control.Monad.Writer (WriterT) 
import Control.Monad.Trans (lift, liftIO) 

type T = ReaderT String IO 
getSortedMiddleElement :: Int -> IO String 

foo :: Int -> T String 
foo n = do 
    str <- lift $ getSortedMiddleElement n --str holds a pure String now 
    lift $ putStrLn str     --get `putStrLn` from IO and pass the String 
    return str        --let's wrap it back in T now 

但是,如果我們不止一層離IO?讓我們試試看:

type W = WriterT String T -- WriterT String (ReaderT String IO) 

-- This doesn't work; lift only gives you access to the next layer's actions 
-- but IO is now more than one layer away! 
-- 
--bar n = do 
-- str <- lift $ getSortedMiddleElement n 

-- Instead, we need liftIO, which will access IO across many transformer layers 
bar :: Int -> W String 
bar n = do 
    str <- liftIO $ getSortedMiddleElement n 
    liftIO $ putStrLn str 
    return str 
+0

非常感謝這個詳細的答案!'LiftIO'正是我所需要的。 – sibbl