0

我是函数式编程和 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 () } }
4

2 回答 2

3

你的代码有很多问题。

从下往上,首先,length [l1]没有意义。任何[ ]中间只有一个项目的只是:一个只有一个项目的列表,所以length总是1. 您当然是指length l1这里,即列表的l1长度,而不是列表的长度 ᴄᴏɴᴛᴀɪɴɪɴɢ onlyl1

接下来,您拥有它iotxt并尝试使其修改“全局变量” l1。你不能那样做,Haskell 没有任何诸如可变全局变量之类的东西——这是有充分理由的;即使在命令式语言中,全局可变状态也被认为是邪恶的。Haskell局部变量,通过IORefs,但使用那些没有充分理由的变量是不受欢迎的。你真的不需要它们来做这样的事情。

正确的做法是废弃这个全局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不会修改 list l1,而是创建一个与旧元素相同的新元素,但其中包含新元素。然后将其传递给下一个循环调用,因此您可以有效地为每个递归调用获取更新的列表。

另请注意,您可能不应该insert在这里使用:这专门用于将新元素放置在有序列表中的正确位置。如果您只想以某种方式累积值,则可以简单地使用

          _    -> go $ x : l1
于 2013-11-11T23:48:49.003 回答
2

在 haskell 中,没有变量,一切都是不可变的。所以你不应该在l1以后定义和更改它的值,这是行不通的。

相反,您应该考虑如何iotxt正确编写并让它收集元素并将输入列表传递回您的main.

在您的iotxt中,您可以考虑以下两种情况:

  • 如果输入是-1,那么我们可以只传回一个空列表[],将其包装在IObyreturn []
  • 如果输入不是-1,我们可以先将这个值存储在某个地方,比如说result。之后,我们应该iotxt递归调用并让这个内部iotxt处理其余的输入。最后,我们将得到返回值rest,然后我们只是简单地放在result一起rest

此外,您可以想象IO a一些返回类型值的程序(可能具有从输入读取或写入输出等副作用)a。根据 your 的定义iotxt,这是一个读取输入直到遇到 a-1并给你返回列表的程序,类型签名iotxt应该是IO [Float]

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

最后,您不需要通过括号对函数参数进行分组,只需简单地说f x是否要f使用x.

我对你的代码做了一些修改,希望能给你一些帮助。

import Data.List

-- iotxt reads from stdin, yields [Float]
-- stop reading when "-1" is read
iotxt :: IO [Float]

main :: IO ()

iotxt = do
    a <- getLine
    -- read in numbers that are not equal to -1
    let result = (read a) :: Float
    if (not (result == -1.0)) 
        then do
            -- read rest of the list from stdin
            rest <- iotxt
            -- put head & tail together
            return $ result:rest
        else do return []

main = do
    putStrLn("Enter a number [-1 to quit]")
    l1 <- iotxt
    -- size of the list 
    print $ length l1
    -- sum 
    print $ sum l1
于 2013-11-11T23:58:16.557 回答