【问题标题】:How to add to a List from IO in Haskell.如何从 Haskell 中的 IO 添加到列表。
【发布时间】:2018-02-05 21:02:17
【问题描述】:

我正在尝试编写一个简单的函数,它一次读取一行(我知道这将是整数),然后将它们存储到一个列表中。然而,对于我的生活来说,这个列表似乎是空的。

import System.IO
import Control.Monad

type Int2 = [Int]

valueTab = [] :: [Int]

app [ ] list = list 
app (h:t) list = h:(app t list)

main :: IO ()
main = do
    hSetBuffering stdout NoBuffering -- DO NOT REMOVE

    -- Auto-generated code below aims at helping you parse
    -- the standard input according to the problem statement.

    input_line <- getLine
    let n = read input_line :: Int
    let value = [] :: [Int]

    replicateM n $ do
        input_line <- getLine
        let pi = read input_line :: Int
        hPutStrLn stderr (show input_line)
        hPutStrLn stderr (show valueTab)
        return $ app valueTab [pi]


    -- hPutStrLn stderr "Debug messages..."

    -- Write answer to stdout


    --putStrLn input_line
    return ()

所以当我运行它时 8 6 4 3 各行其道, 它打印 6, [], 4, [], 3 []。

这是我的打印、我的列表声明、我存储它们的方式的问题吗?我有 value 和 valueTab 来检查它是否是范围问题。

注意:该代码是在其平台上对其进行测试的编码网站上的样板代码。假设 replicateM 只是一个循环运行代码 x 次。

【问题讨论】:

  • Haskell 上的变量是不可变的。您正在评估app valueTab [pi](顺便说一下,可能写成valueTab ++ [pi]),但这不会改变valueTab 的值。这不可以。 valueTab 永远是一个空列表。
  • OTOH replicateM 函数的结果(它是一个返回值的函数)可能很有趣,但你永远不会用它做任何事情。也许尝试探索这种可能性。
  • 感谢您对 haskell 的见解。我看到我在这个问题上想太多 Java 了哈哈。

标签: list haskell io


【解决方案1】:

您在codinggame.com 上似乎遇到了问题。其他编码网站似乎使用他们的 Haskell 模板做得更好,特别是对于初学者练习 - 模板通常会处理 所有 输入和输出,您只需要提供缺少的纯函数。相比之下,codinggame.com 的 Haskell 模板似乎假定初学者对 IO monad 有相当牢靠的掌握,并遗漏了许多基本细节(例如,replicateM 操作的结果实际上应该绑定到一个变量),您需要填写。

原始模板中的replicateM 调用可能类似于:

replicateM n $ do
    input_line <- getLine
    let pi = read input_line :: Int    -- maybe this line was there, maybe not
    return ()

此表达式创建一个复合 IO 操作,执行时将重复以下子操作n 次:

  • 读取一行,将读取的字符串绑定到input_line
  • 准备将该行转换为整数值pi(尽管这从未完成,因为未使用pi
  • 不管读取的字符串是什么,都返回“unit”(值())作为子动作的结果

复合动作的值是子动作返回的值的列表。由于这些都是单位,复合动作的最终值是一个列表[(),(),()...()],每行读取一个()。但是,由于此复合操作的值从未绑定到变量(即,因为在 replicateM 表达式之前没有 result &lt;- ...),因此该列表被丢弃。

因此,此模板提供了一种不必要的冗长方法,可以读取n 输入行并且对它们什么都不做。

幸运的是,要让这个模板做一些有用的事情,你需要做的就是让子动作返回一个有用的值(例如,整数 pi),这将导致复合动作返回一个读取的整数列表,然后确保使用 &lt;- 表示法将结果列表绑定到变量。

换句话说,你想写:

main = do
    ...

    pis <- replicateM n $ do
        input_line <- getLine
        let pi = read input_line :: Int
        return pi

    hPutStrLn stderr (show pis)

您不需要辅助函数app,也不需要预先声明一个列表valueTab 来包含结果。结果由replicateM自动生成,您只需命名即可使用。

完整的工作程序如下所示:

import System.IO
import Control.Monad

type Int2 = [Int]

main :: IO ()
main = do
    hSetBuffering stdout NoBuffering -- DO NOT REMOVE

    -- Auto-generated code below aims at helping you parse
    -- the standard input according to the problem statement.

    input_line <- getLine
    let n = read input_line :: Int
    let value = [] :: [Int]

    pis <- replicateM n $ do
        input_line <- getLine
        let pi = read input_line :: Int
        return pi

    hPutStrLn stderr (show pis)

    -- hPutStrLn stderr "Debug messages..."
    -- Write answer to stdout

    return ()

【讨论】:

  • 非常感谢您的解释。我不敢相信它是如此简单,同时,我永远无法通过看它来弄清楚这一点。但是从你的解释来看,我对这里的概念的把握肯定要强一些。现在我可以真正开始尝试解决这个难题了!再次感谢您花时间回答。此外,很好的工作将它钉在了来自codingame的头上!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-26
  • 1970-01-01
相关资源
最近更新 更多