我希望有一个函数读取任意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>>
我做错了什么?
答案 0 :(得分:7)
正如@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是一个标记值,我们不应该将它附加到我们的列表中。其次,我们不需要每次向其添加值时对列表进行排序。在打印/传递到另一个功能时排序一次就足够了。
如果你想在没有提示用户输入的情况下读取未指定数量的整数并在遇到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
答案 1 :(得分:4)
let list = ordList ((read num :: Int):list)
这基本上是[x, x, ...]
形式列表的递归定义(就像你写了一个方程式x = 1 + x
)。由于Haskell是懒惰的,所以这本身就很好。但是,如果你尝试print list
(又名&#34;解决等式&#34;),它将会失败,因为它将尝试打印无限多个数字。
您可能对(:)
运营商的运作存在误解。 Haskell函数永远不会执行赋值操作,并通过更改它num
连接到list
,就像在命令式语言中一样。只有纯函数。
如果你想累积所有数字,你应该尝试提出readIntegers
的递归定义,将其状态(list
)保存在一个附加参数中(还有更复杂的方法) ,隐藏状态传递,但对初学者使用更复杂)。
答案 2 :(得分:3)
对于更复杂的解决方案,请注意这是展开,您可以使用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
这有一个很好的属性,它按照读取顺序返回列表。