从输入中读取数字 Haskell

Reading numbers from input Haskell

我想要一个函数读取任意 int 直到插入数字“0”,然后将插入的数字呈现在有序列表中。

为此我写了这个函数:

import Data.List

readIntegers :: IO()
readIntegers = do
    putStrLn "insert a number: "
    num<-getLine
    let list = ordList ((read num :: Int):list)
    if (read num == 0)  
    then print list 
    else readIntegers
  where ordList ::[Int]->[Int]
        ordList [] = []
        ordList xs = sort xs

这编译得很好,但是当我插入数字“0”时,它给我这个错误:

*** Exception: <<loop>>

我做错了什么?

let list = ordList ((read num :: Int):list)

这基本上是 [x, x, ...] 形式列表的递归定义(就像你写了一个等式 x = 1 + x)。这本身就很好,因为 Haskell 是懒惰的;但是,如果您尝试 print list(又名 "solve the equation"),它将失败,因为它会尝试打印无限多的数字。

您可能对 (:) 运算符的工作原理有误解。 Haskell 函数永远不会执行赋值操作并通过更改将 num 连接到 list 上,就像在命令式语言中一样。只有 函数。

如果你想累加所有数字,你应该尝试提出 readIntegers 的递归定义,将其状态(list)保存在一个附加参数中(还有更多复杂的方法,隐藏状态传递,但对于初学者来说使用起来更复杂)。

正如@phg 指出的那样,您本质上是在构建一个无限列表,而实际对其进行评估会导致循环错误。解决这个问题的一个简单实现是定义一个辅助函数,它接受一个额外的参数——一个列表来存储从屏幕读入的所有输入,像这样:

readInteger :: IO ()
readInteger = readInteger' []
    where
        readInteger' x = do
        putStrLn "insert a number: "
        num<-getLine        
        if ((read num :: Int) == 0)  
        then print $ ordList x 
        else readInteger' $ (read num :: Int):x
        where ordList ::[Int]->[Int]
              ordList [] = []
              ordList xs = sort xs

请注意,以上基本上只是@phg 答案的一个实现,但对您的原始逻辑进行了一些更改。首先,由于 0 是标记值,我们不应该将它附加到我们的列表中。其次,我们不需要每次向列表中添加值时都对列表进行排序。在printing/passing到另一个函数时排序一次就足够了。

Demo

如果您想在不提示用户输入的情况下读取未指定数量的整数并在遇到 0 时将其切断,您可能最好使用 getContents,它将读取标准中的所有内容懒惰地输入为单个字符串。

然后,将它解析为一个数字列表并用它做你想做的事就很简单了,就像这样:

readIntegers :: ()
readIntegers = do
   a <- getContents
   let b = ordList $ takeWhile (/= 0) $ map (\x -> read x :: Int) $ words a          
   mapM (putStrLn . show) b
 where ordList ::[Int]->[Int]
       ordList [] = []
       ordList xs = sort xs 

对于更复杂的解决方案,请注意这是一个 展开,您可以使用 Control.Monad.Loops 中的 unfoldM 来实现它:

import Control.Monad.Loops (unfoldM)

readInts :: IO [Int]
readInts = unfoldM $ fmap (check . read) getLine
  where check x = if x == 0 then Nothing else Just x

这有一个很好的 属性,它 returns 列表是按阅读顺序排列的。