我是函数式编程的初学者,Haskell是一种编程语言。在从命令行输入数字后,我想将这些数字放入列表中,然后将该列表作为参数传递以计算其总和。这就是我正在做的事情:
import Data.List
iotxt :: IO ()
main :: IO ()
l1 = []
iotxt = do a <- getLine
-- read in numbers that are not equal to -1
insert (read a) l1
if (not ((read a) == -1.0))
then iotxt
else do return ()
main = do
putStrLn("Enter a number [-1 to quit]")
iotxt
-- size of the list
print(length [l1])
-- sum
但是当我尝试将值放在列表中时,我得到了这个错误:
Couldn't match expected type `IO a0' with actual type `[a1]'
In the return type of a call of `insert'
In a stmt of a 'do' block: insert (read a) l1
In the expression:
do { a <- getLine;
insert (read a) l1;
if (not ((read a) == - 1.0)) then iotxt else do { return () } }
答案 0 :(得分:3)
你的代码有很多问题。
从下往上开始,首先,length [l1]
没有意义。任何只有一个项目的[
]
就是:包含单个项目的列表,因此length
始终为1
。您当然在这里表示length l1
,即列表的长度l1
,而不是列表的长度ᴄᴏɴᴛᴀɪɴɪɴɢl1
。
接下来,您拥有此iotxt
并尝试修改“全局变量”l1
。你不能这样做,Haskell没有任何可变的全局变量 - 有充分的理由;即使在命令式语言中,全球可变状态也被认为是邪恶的。 Haskell 种类具有局部变量,通过IORef
s,但是没有充分理由使用它们是不受欢迎的。你真的不需要这样的东西。
正确的做法是废弃这个全局l1
绑定,忘记改变变量。这让我们想到了如何传递iotxt
中获得的信息。好吧,明显的功能要做的事情是,返回它。我们需要在类型中明确指出(这又是一件好事,所以我们实际上知道如何使用该函数):
ioTxt :: IO [Int]
这样的功能可以很好地用于main
:
main :: IO ()
main = do
putStrLn "Enter a number [-1 to quit]"
l1 <- ioTxt
print $ length l1
您看到:与您的方法几乎相同,但在适当明确地介绍l1
您需要的地方,而不是完全不同的地方。
剩下要做的是实施ioTxt
。现在这也需要一个本地l1
变量,因为我们已经废弃了全局变量。当您将循环实现为这样的递归调用时,您需要将更新的版本传递给每个实例。递归本身应该在本地定义的函数上完成。
ioTxt :: IO [Int]
ioTxT = go [] -- Start with empty list. `go` is a widespread name for such simple "loop functions".
where go l1 = do
a <- getLine
let x = read a :: Int
case x of
(-1) -> return l1
_ -> go (insert x l1)
在最后一行中,请注意insert
不会修改列表l1
,而是创建一个等于旧列表的新列表,除了在其中包含新元素。然后将其传递给下一个循环调用,这样就可以有效地获得每个递归调用的更新列表。
另请注意,您可能不应在此使用insert
:这是专门用于将新元素放置在有序列表中的正确位置。如果您只想以某种方式累积值,可以使用
_ -> go $ x : l1
答案 1 :(得分:2)
在haskell中,没有变量,一切都是不可变的。因此,您不应该定义l1
并稍后更改其值,这不起作用。
相反,您应该考虑如何正确编写iotxt
并让它收集元素并将输入列表传递回main
。
在iotxt
中,您可以考虑以下两种情况:
-1
,那么我们只需将空列表[]
传回,将IO
包裹在return []
-1
,我们可以先将此值存储在某处,例如result
。在那之后,我们应该递归地调用iotxt
并让这个内部iotxt
处理其余的输入。最后,我们将获得返回值rest
,然后我们只需将result
和rest
放在一起。此外,您可以将IO a
视为某些程序(可能具有从输入读取或写入输出等副作用),它返回类型a
的值。根据{{1}}的定义,这是一个在满足iotxt
之前读取输入并为您提供回复列表的程序,-1
的类型签名应为{{1} }。
您的代码中有一个iotxt
,它构建一个只包含一个元素的列表(即IO [Float]
),因此length [l1]
将始终返回l1
。您可以说length [l1]
,而不是这样做,它会计算1
的长度。
最后,您不需要按括号对函数参数进行分组,只需简单地说length l1
,如果您想使用l1
调用f x
。
我已经修改了一下你的代码,希望它可以给你一些帮助。
f