2011-02-23 19 views
7

你好偉大的程序員在那裏,哈斯克爾-問題:IO字符串 - > [INT]

我做我的第一個步驟,Haskell和有混淆我的函數:

import Data.List.Split 
getncheck_guesslist = do 
    line <- getLine 
    let tmp = splitOneOf ",;" line 
    map read tmp::[Int] 

splitOneOf是在Data.List.Split splitOneOf :: (Eq a)=> [a]->[a]->[[a]]

從錯誤中我得到它(我的陰謀分裂安裝安裝),有一些類型不正確 - 但不知道如何解決這個衝突,IO仍一個謎我

我想讀通過逗號或分號分隔的整數的輸入,並得到整數的列表,以便:

  • 我怎麼可以檢查用戶輸入型的詮釋
  • 如何我可以「翻譯」,其輸入的類型是「IO字符串」爲[INT]

謝謝提前的思想和暗示 - 你的ε/ 2

+6

如果IO是一個謎題,您有兩個選擇:要麼通過學習來揭示它,要麼沒有它,並且沒有IO地編寫函數。我會建議與第二個選項一段時間。 – 2011-02-23 12:19:37

回答

2

如果某物在IO monad內,則無法將其帶到純粹的外部世界進行進一步處理。相反,您將純函數傳遞給IO內的函數。最後,你的程序讀取一些輸入並寫入一些輸出,所以你的主要功能將與IO一起工作,否則你將不能輸出任何東西。而不是閱讀IO String並創建一個[Int],將消耗[Int]的函數傳遞到您的主函數中,並在do中使用它。

12

在編寫使用IO monad的函數時,要從函數返回的任何值都必須位於IO monad中。

這意味着,您不必返回類型爲[Int]的值,而必須返回類型爲IO [Int]的東西。爲此,您使用return函數,該函數將值「折回」爲IO(它實際上適用於任何 monad)。

只需更改最後一行與return來包裝你的價值,像這樣:

getncheck_guesslist = do 
    line <- getLine 
    let tmp = splitOneOf ",;" line 
    return (map read tmp :: [Int]) 
2
import Data.List.Split 
import Control.Applicative 

getncheck_guesslist :: IO [Int] 
getncheck_guesslist = map read . splitOneOf ",;" <$> getLine 

每次碼沼澤下來foo >>= return . bar,這你不通過脫糖的DO-塊(和糾正類型錯誤),您沒有使用monad的monadic特性,而只使用它的函數部分,也就是說,你不是在搞類型的部分,而是用aIO a

(<$>) :: (Functor f) => (a -> b) -> f a -> f b 

fmap將是<$>的非中綴名稱。兩者都與map密切相關。)

上面的代碼是特殊的代碼非常地道,但乾淨的代碼看起來像

import Data.Maybe 

maybeRead :: Read a => String -> Maybe a 
maybeRead = fmap fst . listToMaybe . reads 
           -- That fmap is using "instance Functor Maybe" 

parseGuessList :: String -> [Int] 
parseGuessList = catMaybes . map maybeRead . splitOneOf ",;" 

getncheck_guesslist = parseGuessList <$> getLine 

,或者,如果你不想忽略非INT輸入,但錯誤出,

parseGuessList xs = if success then Just . catMaybes $ ys else Nothing 
    where ys :: String -> [Mabye Int] 
     ys = map maybeRead . splitOneOf ",;" $ xs 
     success = all isJust ys 

(請注意,雖然,我只是證明代碼是正確的,沒有真正嘗試過。)

如果它得到任何比這更復雜的,你會想用適當的解析天秤座ry,我想。

+0

這只是我的頭。我不會失望,但我有一種感覺,它也超過了原來的海報頭。 – Zach 2011-07-15 05:38:58

7

在Haskell中實現它的「正確方法」是將IO與其他任何東西分開。你的代碼的直接翻譯是這樣的:

getncheck_guesslist :: IO [Int] 
getncheck_guesslist = do line <- getLine    -- get 
         return (check_guesslist line) -- check 

check_guesslist :: String -> [Int] 
check_guesslist line = let tmp = splitOneOf ",;" line 
         in map read tmp 

注意getncheck_guesslist僅僅是一個IO動作。該功能沒有輸入參數,即使它確實需要(IO)輸入getLine

另請注意,getncheck_guesslist是對getLine IO操作的簡單修改。是不是有一個組合器,可以讓我推動一個函數來處理monad中的值?停止。 Hoogle時間!

我有一個功能(a -> b)。我有一個輸入類型的值,但它卡在一個monad m a。我想在monad中執行這個函數,所以結果將不可避免地卡在monad中。m b。把這一切放在一起,我們hoogle (a -> b) -> m a -> m b。瞧,fmap就是我們要找的。

get_guesslist = check_guesslist `fmap` getLine 
-- or, taking it a step further 
get_guesslist = (map read . splitOneOf ",;") `fmap` getLine :: IO [Int] 

最後要注意的,只要你有類似名稱somethingAndSomethingElse代碼的方法,它通常是更好的編碼風格來寫和調用somethingsomethingElse作爲兩個獨立的方法。對於最終版本,我只是將其重命名爲get_guesslist,因爲從概念上講,這就是它的作用。它將猜測作爲Ints列表。

作爲最後的最後說明,我已經離開了barsoap開始的地步。 ;)fmap<$>相同。