重复函数时如何 "save" 一个变量?

How can I "save" a variable when repeating a function?

现在我正在尝试制作一个程序,给出迄今为止输入的所有数字的总和。目前我有这个代码开头:

import System.Exit

main :: IO()
main = do
    a <- getLine
    if a == "EXIT_PROGRAM"
    then
        exitWith ExitSuccess
    else
        print(a)
    main

它现在工作正常,但你可能会说这不是我想要的。为此,我必须保存之前的总和,然后将其添加到 a,然后保存并重复该函数。我怎样才能做到这一点?谢谢。

找到方法了,应该早点想出来吧。我制作了一个单独的函数,与 main 不同,它可以接受输入。然后我把产品放进去。

import System.Exit

multiplier :: Integer -> IO()
multiplier prevProduct = do 
    a <- getLine
    if a == "EXIT_PROGRAM"
    then
        exitWith ExitSuccess
    else
        return()
    let b = (read a :: Integer) * prevProduct
    print(b)
    multiplier b

main :: IO()
main = do
    multiplier 1

我还更改了它,使其成倍增加而不是增加

中建议的实现具有正确的行为,但让我觉得可能不完全是惯用的。使用 exitWith 给我的印象是使用异常进行流量控制——这种技术在大多数语言中都是不受欢迎的(不仅仅是 Haskell)。

这也导致需要对 else 分支使用 return () 才能不执行任何操作。在这里,return () 几乎像 continue 在 C# 这样的语言中使用。

此外,该程序将无法处理任何错误的输入,例如 fooexit,甚至 EXIT PROGRAM。在所有这些情况下,它都会崩溃:

> multiplier 1
2
2
3
6
EXIT PROGRAM
*** Exception: Prelude.read: no parse

正如 chi 在评论中建议的那样,使用递归会更加惯用,但在 Haskell 中,您通常通过拥有一个干净利落的基本情况来做到这一点。

期望的行为几乎是 Read Eval Print Loop (REPL),除了评估函数是预先给出的。因此,也许 readPrintLoop 是一个合适的名称。这是一种方法:

readPrintLoop :: (Show a, Read b) => (a -> b -> a) -> a -> IO ()
readPrintLoop eval acc = do
  s <- getLine
  let m = readMaybe s
  case m of
    Just x -> do
      let y = eval acc x
      print y
      readPrintLoop eval y
    Nothing -> return ()

此操作使用 Text.Read 中的 readMaybe 来解析输入,并选择正常失败。因此,任何不解析的输入(例如 fooexitEXIT_PROGRAM)都将退出循环。

它首先使用 getLine 读取输入,使用 readMaybe 解析,然后 pattern-matches Maybem

如果 mNothing,则操作会干净地退出 ()

如果 m 是一个 Just 值,它使用 eval(一个函数参数)来计算新的累加值,打印它,并用新的累加器递归。

由于 OP 描述了累加总和,而同一用户的答案累加了一个乘积,因此我通过允许调用者定义所使用的函数来使 readPrintLoop 变得灵活。

您甚至可以用它来累加小数(因为任何 Read 实例都可以):

> readPrintLoop (+) 0.0
1.1
1.1
2.3
3.4
exit
>

另一方面,如果您希望 main 累加整数乘积,您可以这样定义它:

main :: IO ()
main = do
  readPrintLoop (*) 1