【问题标题】:haskell-problem: io string -> [int]haskell 问题:io 字符串 -> [int]
【发布时间】:2011-07-02 17:23:01
【问题描述】:

你好伟大的程序员,

我在 haskell 中迈出了第一步,并且有一个让我感到困惑的功能:

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

splitOneOf 在 Data.List.Split 中(我使用 cabal install split 安装了它) splitOneOf :: (Eq a)=&gt; [a]-&gt;[a]-&gt;[[a]]

从错误中我知道存在一些类型不正确 - 但不知道如何解决这种冲突,因为 IO 对我来说仍然是个谜

我想读取以逗号或分号分隔的整数输入并获取整数列表:

  • 如何检查用户输入是否为 Int 类型
  • 如何将“IO String”类型的输入“翻译”为 [Int]

提前感谢您的想法和提示 - 你的 ε/2

【问题讨论】:

  • 如果 IO 是个谜,你有两个选择:要么通过学习来揭开它,要么在没有它的情况下工作,在没有 IO 的情况下编写函数。我建议暂时使用第二个选项。

标签: string haskell io int


【解决方案1】:

在 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_guesslistgetLine IO 操作的简单修改。是不是有一个组合器可以让我推送一个函数来作用于 monad 中的值?停止。胡歌时间!

我有一个函数(a -&gt; b)。我有一个输入类型的值,但它被困在一个单子m a 中。我想在 monad 内部执行函数,所以结果也不可避免地会卡在 monad 中m b。综上所述,我们 hoogle (a -&gt; b) -&gt; m a -&gt; 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&lt;$&gt; 相同。

【讨论】:

    【解决方案2】:
    import Data.List.Split
    import Control.Applicative
    
    getncheck_guesslist :: IO [Int]
    getncheck_guesslist = map read . splitOneOf ",;" <$> getLine
    

    每次代码陷入foo &gt;&gt;= return . bar,您的代码通过对do-block进行脱糖(并更正类型错误)来实现,您没有使用monad的monadic特性,而只是它的函子部分,即,直白的说,你不是在弄类型的IO部分,而是IO aa

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

    fmap 将是 &lt;$&gt; 的非中缀名称。两者都与 map 密切相关。)

    上面的代码对于 ad-hoc 代码来说几乎是惯用的,但是干净的代码看起来像

    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
    

    (但请注意,我只是证明了代码是正确的,并没有实际尝试过。)

    如果它变得比您想要使用适当的解析库更复杂,我想。

    【讨论】:

    • 这简直超出了我的想象。我不会投反对票,但我觉得它也超出了原始海报的范围。
    【解决方案3】:

    当你编写一个使用 IO monad 的函数时,你想从函数返回的任何值也必须在 IO monad 中。

    这意味着,您必须返回 IO [Int] 类型的值,而不是返回类型为 [Int] 的值。为此,您使用return 函数,它将值“包装”到IO 中(它实际上适用于任何 monad)。

    只需将最后一行更改为用return 包装您的值,如下所示:

    getncheck_guesslist = do
        line <- getLine
        let tmp = splitOneOf ",;" line
        return (map read tmp :: [Int])
    

    【讨论】:

      【解决方案4】:

      如果IO monad 中有东西,你不能把它带到纯粹的外部世界做进一步的处理。相反,您将纯函数传递给 IO 内的函数。

      最后,您的程序会读取一些输入并写入一些输出,因此您的 main 函数将使用 IO,否则您将无法输出任何内容。与其读取IO String 并创建[Int],不如将​​使用[Int] 的函数传递给您的主函数并在do 中使用它。

      【讨论】:

        猜你喜欢
        • 2011-10-01
        • 2021-10-18
        • 2016-06-18
        • 2013-05-01
        • 2011-09-17
        • 1970-01-01
        • 2014-06-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多