Haskell 输入返回元组

发布于 2024-09-05 23:37:47 字数 335 浏览 2 评论 0原文

我想知道 IO() 函数是否可以返回元组,因为我想从这个函数中获取这些元组作为另一个函数的输入。

investinput :: IO()->([Char], Int)
investinput = do
 putStrLn "Enter Username : "
 username <- getLine

 putStrLn "Enter Invest Amount : "
 tempamount <- getLine
 let amount = show  tempamount
 return (username, amount)

请帮忙。

谢谢。

i wonder can a IO() function return tuple because i would like to get these out of this function as input for another function.

investinput :: IO()->([Char], Int)
investinput = do
 putStrLn "Enter Username : "
 username <- getLine

 putStrLn "Enter Invest Amount : "
 tempamount <- getLine
 let amount = show  tempamount
 return (username, amount)

Please help.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

音栖息无 2024-09-12 23:37:47

Haskell 中的 IO 与您习惯的语言中的 IO 不同。 Haskell 中的所有函数都必须是纯函数:也就是说,如果使用参数 x 调用函数 f ,则调用它一次、两次或一次之间一定没有区别。百倍。不过,考虑一下这对 IO 意味着什么。天真地说,getLine 应该具有 getLine :: String 类型,或者可能 getLine :: () ->;字符串。 (() 是单位类型,其唯一值是 ();它有点像类 C 语言中的 void 类型,但有一个值)但这意味着每次您编写 getLine 时,它都必须返回相同的字符串,这不是您想要的。这就是IO类型的目的:封装动作。这些动作与功能不同;它们代表不纯计算(尽管它们本身是纯的)。 IO a 类型的值表示执行时返回 a 类型的值的操作。因此,getLine 的类型为 getLine :: IO String:每次评估操作时,都会生成一个 String(通过从用户读取) 。类似地,putStr 的类型为 putStr :: String -> IO();它是一个函数,它接受一个字符串并返回一个操作,该操作在运行时不会返回任何有用的信息……但是,作为副作用,会在屏幕上打印一些内容。

您正在尝试编写 IO () -> 类型的函数([字符],整数)。这将是一个将操作作为输入并返回一个元组的函数,这不是您想要的。您需要一个 IO (String, Int) - 一个操作,运行时会生成一个由字符串(这是 [Char] 的同义词)和一个元组组成的元组。整数。您当前的代码也快完成了!这就是您需要的:

investinput :: IO (String, Int)
investinput = do
  putStrLn "Enter Username : "
  username <- getLine
  putStrLn "Enter Invest Amount : "
  tempamount <- getLine
  let amount = read tempamount
  return (username, amount)

请注意,我只进行了两项更改(并删除了一个空行)。首先,我改变了函数的类型,就像我上面所说的那样。其次,我将 show 更改为 readshow 函数的类型为 Show a =>;一个-> String:它是一个函数,它接受任何可以显示的内容并生成一个表示它的字符串。 想要read,其类型为Read a =>;字符串 -> a:给定一个字符串,它解析它并返回一些可读的值。

您询问的另一件事是返回一个元组 (String, Int) 而不是操作 IO (String, Int)。没有纯粹的方法可以做到这一点;换句话说,不存在纯函数IO a ->一个。这是为什么呢?因为IO a代表了一个依赖于现实世界的不纯粹的动作。如果我们有这样一个函数 impossibleRunIO :: IO a -> a,那么我们希望出现 impossibleRunIO getLine == irresibleRunIO getLine 的情况,因为该函数必须是纯函数。但这是没有用的,因为我们希望impossibleRunIO能够真正与现实世界交互!因此,这个纯函数是不可能的。进入IO的所有东西都永远无法离开。这就是 return 的作用:它是一个函数,在本例中为1,类型为 return :: a ->; IO a,它使您能够将纯值放入 IO 中。对于任何 xreturn x 是一个在运行时始终生成 x 的操作。这就是为什么您必须以 return 结束 do 块:username 是您从操作中提取的纯值,因此是仅在 do 块内可见。您需要将其提升到 IO 中,然后外界才能看到它。 amount/tempamount 也是如此。

为了完整起见:这背后有一些总体理论将其联系在一起。但对于开始 Haskell 编程来说根本没有必要。我建议你将大部分代码构建为纯函数,这些函数会折叠、旋转和破坏你的数据。然后构造一个薄(尽可能薄)的IO前层,与所述函数交互。您会惊讶地发现您需要的 IO 如此之少!

1:它实际上有一个更通用的类型,但目前不相关。

IO in Haskell doesn't work like IO in the languages you're used to. All functions in Haskell must be pure: that is, if a function f is called with the argument x, there must be no difference between calling it once, twice, or a hundred times. Consider what this means for IO, though. Naïvely, getLine should have the type getLine :: String, or perhaps getLine :: () -> String. (() is the unit type, whose only value is (); it's sort of like a void type in a C-like language, but there is a single value of it.) But this would mean that every time you wrote getLine, it would have to return the same string, which is not what you want. This is the purpose of the IO type: to encapsulate actions. These actions are distinct from functions; they represent impure computation (though they themselves are pure). A value of type IO a represents an action which, when executed, returns a value of type a. Thus, getLine has type getLine :: IO String: every time the action is evaluated, a String is produced (by reading from the user). Similarly, putStr has type putStr :: String -> IO (); it is a function which takes a string and returns an action which, when run, returns no useful information… but, as a side effect, prints something to the screen.

You are attempting to write a function of type IO () -> ([Char], Int). This would be a function which took as input an action and returned a tuple, which is not what you want. You want an IO (String, Int)—an action which, when run, produces a tuple consisting of a string (which is a synonym for [Char]) and an integer. You're almost there with your current code, too! This is what you'll need instead:

investinput :: IO (String, Int)
investinput = do
  putStrLn "Enter Username : "
  username <- getLine
  putStrLn "Enter Invest Amount : "
  tempamount <- getLine
  let amount = read tempamount
  return (username, amount)

Notice that I've only made two changes (and removed a blank line). First, I've changed the type of the function, like I said above. Second, I changed show into read. The show function has the type Show a => a -> String: it is a function which takes anything which can be shown and produces a string representing it. You wanted read, which has the type Read a => String -> a: given a string, it parses it and returns some readable value.

The other thing you asked about is returning a tuple (String, Int) instead of an action IO (String, Int). There is no pure way to do this; in other words, there is no pure function IO a -> a. Why is this? Because IO a represents an impure action which depends on the real world. If we had such a function impossibleRunIO :: IO a -> a, then we would want it to be the case that impossibleRunIO getLine == impossibleRunIO getLine, since the function must be pure. But this is useless, as we would want impossibleRunIO to be able to actually interact with the real world! Thus, this pure function is impossible. Everything that enters IO can never leave. This is what return does: it is a function with, in this case1, the type return :: a -> IO a, which enables you to place pure values into IO. For any x, return x is an action which, when run, always produces x. This is why you have to end your do block with the return: username is a pure value you extracted from an action, and as such is only visible within the do block. You need to lift it into IO before the outside world can see it. The same is true of amount/tempamount.

And just for completeness's sake: there is some overarching theory behind this which ties it together. But it's not necessary at all for beginning Haskell programming. What I would recommend doing is structuring most of your code as pure functions which fold, spindle, and mutilate your data. Then construct a thin (as thin as possible) IO front layer which interacts with said functions. You'll be surprised how little IO you need!

1: It actually has a more general type, but that's not relevant for the moment.

江心雾 2024-09-12 23:37:47

是的,你已经快到了,但我认为你想要签名:

investinput :: IO ([Char], Int)

...然后从调用函数中你可以做类似的事情:

main = do
    (username, amount) <- investinput
    ....

我认为你想读取 tempamount 而不是显示。

Yeah, you're almost there but I think you want the signature:

investinput :: IO ([Char], Int)

... then from the calling function you can do something like:

main = do
    (username, amount) <- investinput
    ....

I think you want to read tempamount rather than show though.

非要怀念 2024-09-12 23:37:47

生成元组的 IO 函数的类型为 IO (a, b),在本例中:

investinput :: IO ([Char], Int)

IO () -> 的签名([Char], Int) 意味着该函数采用 IO () 类型的参数并从中生成一个元组,这不是您想要的。

一般来说,IO 函数(或不同 monad 中的函数)可以返回的类型没有限制,您可以选择您喜欢的类型。

An IO function that produces a tuple would have type IO (a, b), in this case:

investinput :: IO ([Char], Int)

A signature of IO () -> ([Char], Int) would mean that the function takes a parameter of type IO () and produces a tuple from that, which is not what you want.

Generally there are no restrictions on the types an IO function (or a function in a different monad) can return, you can chose the types however you like.

ペ泪落弦音 2024-09-12 23:37:47

关于返回 (String, Int) 而不是 IO (String, Int) 的问题的答案很简单:你不能。一旦进入 IO,您就会被困在那里。这就是人们说 Haskell 是一种“纯粹”语言时的部分含义。

您想要做的事情与您已经在此处使用 getLine 所做的事情类似。 getLine的类型是IO String。当您编写 username <- getLine 时,您实际上是从 IO String 中取出 String,但这只是可能的,因为您位于 do 表达式内。

您可以使用 investinput 执行与使用 getLine 完全相同的操作。以下是如何在 main 函数中使用 investinput 的示例:

main = do
  (name, amount) <- investinput
  putStrLn $ name ++ " invested $" ++ show amount ++ "."

由于您在评论中提到了 liftM,因此这是一个完整的工作版本使用 liftM 和反向绑定运算符 (=<<) 而不是 do 表示法实现同样的效果:

import Control.Monad (liftM)

investinput :: IO (String, Int)
investinput = do
   putStrLn "Enter Username : "
   username <- getLine
   putStrLn "Enter Invest Amount : "
   amount <- liftM read getLine -- We can also use liftM to get rid of tempamount.
   return (username, amount)

summary :: (String, Int) -> String
summary (name, amount) = name ++ " invested $" ++ show amount ++ "."

main = putStrLn =<< liftM summary investinput

这显示了如何使用 < code>investinput 与“另一个需要元组的函数”。

The answer to your question about returning (String, Int) rather than IO (String, Int) is simple: you can't. Once you're in IO you're stuck there. That's part of what it means when people say that Haskell is a "pure" language.

What you want to do is similar to what you're already doing here with getLine. The type of getLine is IO String. When you write username <- getLine, you're in effect taking the String out of the IO String, but this is only possible because you're inside the do expression.

You can do exactly the same kind of thing with investinput as with getLine. Here's an example of how you could use investinput in your main function:

main = do
  (name, amount) <- investinput
  putStrLn $ name ++ " invested $" ++ show amount ++ "."

Since you mention liftM in a comment, here's a full working version that does the same thing using liftM and the reverse bind operator (=<<) instead of do notation:

import Control.Monad (liftM)

investinput :: IO (String, Int)
investinput = do
   putStrLn "Enter Username : "
   username <- getLine
   putStrLn "Enter Invest Amount : "
   amount <- liftM read getLine -- We can also use liftM to get rid of tempamount.
   return (username, amount)

summary :: (String, Int) -> String
summary (name, amount) = name ++ " invested $" ++ show amount ++ "."

main = putStrLn =<< liftM summary investinput

This shows how you could use investinput with "another function which expects a tuple".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文