【问题标题】:Haskell, can i call function without IO output working with monads?Haskell,我可以在没有 IO 输出的情况下调用函数吗?
【发布时间】:2012-04-01 03:58:33
【问题描述】:

为什么我不能这样做? 它禁止在这个问题中使用'do':/ 我如何在列表中调用 words 并同时产生 IO? 谢谢..这是我的实际代码:/

main :: IO()
main = 
     putStr "Name of File: " >>
     getLine >>=
     \st ->
    openFile st ReadMode >>=
    \handle ->
        hGetContents handle >>=
        \y ->
        words y >>=
        \strings ->
            strings !! 1 >>=
            \string->
                 putStr string

[编辑]解决方案:

main :: IO()
main = 
     putStr "Name of File: " >>
     getLine >>=
     \st ->
    openFile st ReadMode >>=
    \handle ->
        hGetContents handle >>=
        \y ->
        return (words y) >>=
        \strings ->
            return (strings !! 1) >>=
            \string->
                 putStr string

【问题讨论】:

    标签: haskell io monads


    【解决方案1】:

    使用return (words y) 而不仅仅是words yreturn 将一个纯值(例如 words 返回的 [String])包装到一个 monad 中。

    从你的措辞来看,这个问题听起来像是家庭作业。如果是这样,它应该被标记为这样。

    【讨论】:

      【解决方案2】:

      (这并不能直接回答问题,但它会使您的代码更加地道,因此更易于阅读。)

      您经常使用\x -> f x >>= ... 模式,这可以(并且应该)被消除:它(大部分)是不必要的噪音,掩盖了代码的含义。我不会使用您的代码,因为它是家庭作业,但请考虑这个示例(请注意,我正在使用 return,正如其他答案所建议的那样):

      main = getLine >>= 
               \fname -> openFile fname ReadMode >>= 
                 \handle -> hGetContents handle >>= 
                    \str -> return (lines str) >>= 
                      \lns -> return (length lns) >>= 
                        \num -> print num
      

      (它从用户那里读取一个文件名,然后打印该文件中的行数。)

      最简单的优化是我们计算行数的部分(这对应于您将单词分开并获得第二个单词的部分):字符串中的行数str 只是length (lines str) (与length . lines $ str 相同),因此我们没有理由将length 的调用和lines 的调用分开。我们的代码现在是:

      main = getLine >>= 
               \fname -> openFile fname ReadMode >>= 
                 \handle -> hGetContents handle >>= 
                    \str -> return (length . lines $ str) >>= 
                      \num -> print num
      

      现在,下一个优化是在\num -> print num。这可以写成print。 (这称为eta conversion)。 (您可以将其视为“一个接受参数并在其上调用 print 的函数,与 print 本身相同”)。现在我们有:

      main = getLine >>= 
               \fname -> openFile fname ReadMode >>= 
                 \handle -> hGetContents handle >>= 
                    \str -> return (length . lines $ str) >>= print
      

      我们可以做的下一个优化是基于monad laws。使用第一个,我们可以将return (length . lines $ str) >>= print 转换为print (length . lines $ str)(即“创建一个包含值的容器(这是由return 完成的),然后将该值传递给print,这与传递值print")。同样,我们可以删除括号,所以我们有:

      main = getLine >>= 
               \fname -> openFile fname ReadMode >>= 
                 \handle -> hGetContents handle >>= 
                    \str -> print . length . lines $ str
      

      然后看!我们可以进行 eta 转换:\str -> print . length . lines $ str 变为 print . length . lines。这会留下:

      main = getLine >>= 
               \fname -> openFile fname ReadMode >>= 
                 \handle -> hGetContents handle >>= print . length . lines
      

      此时,我们可能会停下来,因为该表达式比我们原来的表达式要简单得多(如果我们愿意,我们可以继续使用>=>)。由于它非常简单,因此也更容易调试(想象一下,如果我们忘记使用lines:在原始main 中不会很清楚,在最后一个很明显。)

      在您的代码中,您可以也应该这样做:您可以使用 sections(这意味着 \x -> x !! 1(!! 1) 相同),以及我在上面使用的 eta 转换和 monad 法则。

      【讨论】:

      • 感谢您的完整解释。我将优化应用于我的代码,并将其减少了十多行 ehhehe。你的帖子对我很有用..再次感谢:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-18
      • 2021-08-03
      • 2017-12-31
      • 2019-01-31
      相关资源
      最近更新 更多