【问题标题】:Unwrapping a from IO (a)从 IO 展开 a (a)
【发布时间】:2017-08-31 00:14:39
【问题描述】:

过去两周我一直在学习 Haskell,并决定在 HackerRank 等地方尝试挑战。这需要学习IO。我在 StackExchange 上阅读了很多答案,一般要点是您不要打开 IO a,您只需在 IO 函数中操作该数据。在这种情况下,如果我不允许将数据从 main 发送给它们,那么所有纯函数的意义何在?这是一些读取多少个测试用例的代码,然后为每个测试用例读取 N 个有序对。

main = do
     test <- getLine
     replicateM (read test) doTest

doTest = do
    query<-getLine
    rs<-replicateM (read query) readPair
    return rs  -- just here to make the file compile

readPair :: IO (Int, Int)
readPair = do
   input <- getLine
   let a = words input in return (read (a!!0) :: Int, read (a!!1) ::Int)

此时我在rs 中有一个IO [(Int, Int)]。我想将该数据发送到此函数:

validFunction :: [(Int,Int)]->Bool
validFuntion [] = True
validFunction (x:[]) = True
validFunction (x:xs) = (not $ elem (snd x) (fmap snd xs))  && validFunction xs

但我似乎无法弄清楚如何做到这一点。任何有关如何使用我从用户那里读取的数据调用此函数的帮助或建议将不胜感激。或者,如果我从错误的角度出发,那么我应该做什么的指针也会起作用。


编辑:通过阅读这里的许多其他问题,我现在有了一个大致的想法,即一旦你进入IO,你就会被困在那里。但我似乎找不到使用IO 数据调用纯函数并取回IO 数据的语法。我尝试了以下一些方法:

fmap validFunction [rs] :: IO Bool  -- tried it with just rs without [] as well 
mapM validFunction [rs] :: IO Bool
validFunction rs        :: IO Bool

我能够让它工作:

 putStrLn . f . validFunction $ rs

虽然我仍然不清楚为什么这会让您将IO [(Int, Int)] 传递给validFunction

【问题讨论】:

  • 你不能把IO a变成a。您需要使用 fmap validFunction 之类的东西来获取 IO [(Int,Int)] -&gt; IO Bool 并改用它。一旦你执行了 IO,你永远不会有一个不显示 IO 正在执行的类型——按照设计。
  • 是的,我从我读过的所有答案中了解到这一点。我似乎找不到任何明确的例子来显示我需要的语法。这一切似乎都涉及留在主目录中的代码。我找不到使用您建议的语法从 do 块调用“纯函数”的任何代码。
  • 你想在哪里打电话给validFunctionrs 只绑定在doTest 的定义内,所以你可能想要像fmap validFunction doTest 这样的东西。如果将其称为 inside doTest,则将 return rs 替换为 fmap validFunction rs
  • @chepner: rs 已经是[(Int, Int)]。你的意思是return (validFunction rs)
  • 当人们谈论“在 IO monad 中处理值”时,他们并不意味着您的所有函数都必须使用 IO monad。他们的意思是你把一堆函数放在一起,最终结果(通常称为main)在 IO monad 中。

标签: haskell io-monad


【解决方案1】:

首先,如果你在do 中使用x &lt;- act,你本质上是有值的。除非您做了非常可疑的事情,否则x 不是IO something,而是something:所以使用起来完全没问题

foo :: Int -> Char
foo = …

bar :: IO Int
bar = …

fooDo :: IO Char
fooDo = do
   number <- bar
   return (foo number) -- apply foo directly on number

但是,IOFunctor 的一个实例,所以我们可以使用fmap提升 foo

liftedFoo :: IO Int -> IO Char
liftedFoo = fmap foo

所以我们可以这样写fooDo

fooDo = fmap foo readLn

虽然它的名称现在具有误导性,但它仍然和以前一样。但是让我们把这个命名巫术放在一边,你会如何解决这个问题?好吧,您的 doTest 类型正确:

doTest :: IO [(Int, Int)]
doTest = do
    query  <- getLine
    rs     <- replicateM (read query) readPair
    return rs

所以缺少的只是调用validFunction。我们可以像 fooDo 那样做:

doTest :: IO Bool
doTest = do
    query  <- getLine
    rs     <- replicateM (read query) readPair
    return (validFunction rs)
--         ^^^^^^^^^^^^^^^^^^
--         no IO  inside here
--  ^^^^^^ 
--   back 
--  to  IO

或者我们可以fmap 覆盖另一个IO 值,例如replicateM (read query) readPair

doTest :: IO Bool
doTest = do
    query  <- getLine
    fmap validFunction (replicateM (read query) readPair)

不过,后者更难阅读。但是你写你的 fooDo doTest 你想do

【讨论】:

  • 很好的解释。可悲的是,在我所做的所有挣扎中,我实际上确实返回了 validFunction rs,但是缺少 () 或 $ 让我受益匪浅。我总是忘记它的右联想。
  • @TonyChamberlain return 是一个函数,而不是关键字。这是必须牢记的关键(呵呵)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-23
  • 1970-01-01
相关资源
最近更新 更多