重复函数时如何 "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# 这样的语言中使用。
此外,该程序将无法处理任何错误的输入,例如 foo
、exit
,甚至 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 来解析输入,并选择正常失败。因此,任何不解析的输入(例如 foo
、exit
或 EXIT_PROGRAM
)都将退出循环。
它首先使用 getLine
读取输入,使用 readMaybe
解析,然后 pattern-matches Maybe
值 m
。
如果 m
是 Nothing
,则操作会干净地退出 ()
。
如果 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
现在我正在尝试制作一个程序,给出迄今为止输入的所有数字的总和。目前我有这个代码开头:
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# 这样的语言中使用。
此外,该程序将无法处理任何错误的输入,例如 foo
、exit
,甚至 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 来解析输入,并选择正常失败。因此,任何不解析的输入(例如 foo
、exit
或 EXIT_PROGRAM
)都将退出循环。
它首先使用 getLine
读取输入,使用 readMaybe
解析,然后 pattern-matches Maybe
值 m
。
如果 m
是 Nothing
,则操作会干净地退出 ()
。
如果 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