你好伟大的程序员,
我正在使用haskell的第一步,并且有一个令我困惑的功能:
import Data.List.Split
getncheck_guesslist = do
line <- getLine
let tmp = splitOneOf ",;" line
map read tmp::[Int]
splitOneOf在Data.List.Split中(我用cabal安装拆分安装它)
splitOneOf :: (Eq a)=> [a]->[a]->[[a]]
从错误中我得知有一些类型不正确 - 但不知道如何解决这些冲突,因为 IO 对我来说仍然是个谜。
我想读取用逗号或分号分隔的整数输入,并得到一个整数列表,所以:
提前感谢您的想法和提示 - 你的ε/ 2
答案 0 :(得分: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])
答案 1 :(得分: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操作。该函数没有输入参数,即使它确实需要来自getLine
的(IO)输入。
另请注意,getncheck_guesslist
是对getLine
IO操作的简单修改。是不是有一个组合器可以让我推动一个函数来对monad中的值进行操作?停止。 Hoogle时间!
我有一个函数(a -> b)
。我有一个输入类型的值,但它被卡在monad m a
中。我想在monad中执行函数,所以结果也不可避免地会被卡在monad m b
中。把这些全部放在一起,我们就是(a -> b) -> m a -> m b
。瞧,fmap
正是我们所寻找的。 p>
get_guesslist = check_guesslist `fmap` getLine
-- or, taking it a step further
get_guesslist = (map read . splitOneOf ",;") `fmap` getLine :: IO [Int]
最后一点,无论何时使用somethingAndSomethingElse
这样的名称编写方法,编写和调用something
和somethingElse
作为两个单独的方法通常会有更好的编码风格。对于最终版本,我只是将其重命名为get_guesslist
,因为从概念上讲它就是它的作用。它得到了Ints列表的猜测。
作为最后的最后一点,我已经离开了baroap开始的地方。 ;)fmap
与<$>
相同。
答案 2 :(得分:2)
如果某些内容属于IO
monad,则无法将其带到纯粹的外部世界进行进一步处理。相反,您将纯函数传递给IO
中的函数内部。
最后,你的程序会读取一些输入并写入一些输出,因此你的主要功能是使用IO
,否则你将无法输出任何内容。而不是阅读IO String
并创建[Int]
,而是将使用该[Int]
的函数传递到主函数中,并在do
中使用它。
答案 3 :(得分:2)
import Data.List.Split
import Control.Applicative
getncheck_guesslist :: IO [Int]
getncheck_guesslist = map read . splitOneOf ",;" <$> getLine
每次代码陷入foo >>= return . bar
时,你都会通过去掉do-block(并纠正类型错误),你没有使用monad的monadic功能,而只使用它的functor部分,直言不讳地说,你没有弄乱类型的IO
部分,而是使用a
的{{1}}:
IO a
((<$>) :: (Functor f) => (a -> b) -> f a -> f b
将是fmap
的非中缀名称。两者都与<$>
密切相关。)
上面的代码对于ad-hoc代码来说非常惯用,但干净的代码看起来像
map
,或者,如果您不想忽略非int输入但是错误输出,
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
(但要注意,我只证明代码是正确的,而不是实际尝试过。)
如果它比你想要使用正确的解析库更复杂,我想。