在Haskell中将列表作为参数传递

时间:2013-11-11 23:17:27

标签: haskell

我是函数式编程的初学者,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 () } }

2 个答案:

答案 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,然后我们只需将resultrest放在一起。

此外,您可以将IO a视为某些程序(可能具有从输入读取或写入输出等副作用),它返回类型a的值。根据{{​​1}}的定义,这是一个在满足iotxt之前读取输入并为您提供回复列表的程序,-1的类型签名应为{{1} }。

您的代码中有一个iotxt,它构建一个只包含一个元素的列表(即IO [Float]),因此length [l1]将始终返回l1。您可以说length [l1],而不是这样做,它会计算1的长度。

最后,您不需要按括号对函数参数进行分组,只需简单地说length l1,如果您想使用l1调用f x

我已经修改了一下你的代码,希望它可以给你一些帮助。

f