【问题标题】:Haskell using IO on a function that doesn't expect itHaskell 在不期望的函数上使用 IO
【发布时间】:2014-12-22 11:38:05
【问题描述】:

问题:

如何将“IO SDL.Surface”赋予需要“SDL.Surface”的函数?

我宁愿重新考虑我的整个方法,也不愿诉诸于使用“unsafePerformIO”之类的东西,除非这实际上是使用它的正确时间(我对此表示怀疑)。

更多信息:

我有一个包含数字和文件路径的文件,我已经解析了这个文件并将位于这些路径的图像加载到列表 [(Int, IO SDL.Surface)] 中。问题是,SDL.blitSurface 函数需要一个正常的 SDL.Surface。

错误信息:

Couldn't match type `IO SDL.Surface'
              with `GHC.ForeignPtr.ForeignPtr SDL.SurfaceStruct'
Expected type: SDL.Surface
Actual type: IO SDL.Surface

我不确定是否需要源代码来回答这个问题,但我还是会提供一些以防万一:

要加载我使用的图像文件:

loadImage :: FilePath -> IO SDL.Surface
loadImage [] = error "empty list"
loadImage a =
  SDL.loadBMP a

要创建我使用的数字和图像列表:

createIDImageList :: [Tiletype] -> [(Int, IO SDL.Surface)]
createIDImageList a =
  if null a then []
  else [(tiletypeid $ a !! 0, loadImage (C8.unpack ( tiletypeimage ( a !! 0))))] ++ createIDImageList (tail a)

为了从这个列表中检索正确的图片,我使用了这个函数:

imageFromID :: Int -> [(Int, IO SDL.Surface)] -> Maybe (IO SDL.Surface)
imageFromID a b =
  if null b then Nothing
  else if a == (fst $ b !! 0) then Just (snd $ b !! 0)
  else imageFromID a (tail b)

最后我使用带有 SDL.blitSurface 的 imageFromID 来绘制图像,但由于 IO 的原因我不能。

【问题讨论】:

    标签: haskell sdl io-monad


    【解决方案1】:

    任何时候你最终得到[IO Foobar],你可能想要的实际上是IO [Foobar]sequence 函数将一个转换为另一个。或者,您可以在首先创建列表时使用mapM 而不是map

    在您的示例中,它有点复杂,因为我们有[(Int, IO Surface)]。让我看看我能提出什么建议......

    loadImage 是一个 I/O 操作。它接受一个文件名并返回一个 IO 操作来加载图像。你的createIDImageList 功能真的很

    createIDImageList = map f
      where
        f a = (tiletypeid a, loadImage (C8.unpack ( tiletypeimage a) ) )
    

    您可能想要做的是将f 更改为具有IO (Int, Surface) 类型而不是(Int, IO Surface)。然后您可以mapM f,生成一个返回内容列表的单个 I/O 操作。

    createIDImageList :: [Tiletype] -> IO [(Int, SDL.Surface)]
    createIDImageList = mapM f
      where
        f a = do
          surface <- loadImage (C8.unpack (tiletypeimage a) )
          return (tiletypeid a, surface)
    

    关于imageFromID:你可能想要做的事情是这样的:

    main = do
      images <- createIDImageList (...)
      ...
      let image5 = imageFromID 5 images
      SDL.blitSurface image5 ...
    

    imageFromID 的类型则变为

    imageFromID :: `Int -> [(Int, SDL.Surface)] -> Maybe SDL.Surface
    

    (由于images 现在有类型[(Int, SDL.Surface)],其中没有IO,感谢&lt;-。)

    您在这里所做的是createIDImageList 实际上是从磁盘加载所有内容,然后您可以在任何时候想要获得您感兴趣的表面时使用imageFromID(其中没有 I/O)在。

    【讨论】:

    • 感谢MathematicalOrchid,这似乎确实是正确的方向。我也需要更改 imageFromID 函数,不是吗?
    • @user3638162 是的,没错。你能解决吗,还是需要提示?
    • 不幸的是,我需要一个提示。我知道这个函数需要变成imageFromID :: Int -&gt; IO [(Int, SDL.Surface)] -&gt; Maybe (SDL.Surface),但我不知道怎么写。我需要为此使用地图吗?
    • 是的,确实如此。非常感谢,这正是我所需要的。
    • 拥有[(Int, IO SDL.Surface)] 类型的中间值可能会在未来带来好处:您可以在将列表传递给sequence 之前执行过滤列表等操作。
    【解决方案2】:
    do image <- loadImage "imagefile"
       blitSurface image rect1 dest rect2
    

    【讨论】:

      猜你喜欢
      • 2013-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-04
      • 1970-01-01
      • 1970-01-01
      • 2016-03-20
      • 1970-01-01
      相关资源
      最近更新 更多