首页 > 解决方案 > 在警戒条件下使用 unsafePerformIO

问题描述

我有这样的功能:

jac :: Int -> Int -> [Int] -> [Int] -> IOArray (Int,Int) Double -> IO Double
jac m k mu nu arr
  | nu!!0 == 0 = return 1
  | length nu > m && nu!!m > 0 = return 0
  | m == 1 = return $ x!!0^(nu!!0) * theproduct (nu!!0)
  | k == 0 && CONDITION = XXX
  | otherwise = YYY

必须检查数组的CONDITION那个元素是否不同于 0。但是要获得这个元素,必须做(1,1)arr

element <- readArray arr (1,1)

我不明白该怎么做。除了unsafePerformIO. 在这里使用它安全吗?我是说:

  | k == 0 && unsafePerformIO (readArray arr (1,1)) /= 0 = XXX

不然我怎么办?

标签: arrayshaskelliomonadsio-monad

解决方案


让我们为您的问题制作一个简化版本。

假设我们要创建以下功能。它告诉我们这两个Int值是否等于0。问题是,它包含一个IO. 您当前的方法是这样的:

-- THIS IS BAD CODE! This could easily cause unexpected behaviour.
areBothZero :: Int -> IO Int -> IO Bool
areBothZero a b
    | a == 0 && unsafePerformIO b == 0 = return True
    | otherwise                        = return False

这表明对单子的误解。在 Haskell 中,unsafePerformIO作为一般规则不应该使用,除非你想达到纯计算无法达到的某种效果。然而,这种情况使用 monad 操作是完全可以实现的,与 不同的是,它是unsafePerformIO完全安全的。

这就是我们实现这一目标的方式。首先,在 的上下文之外编写逻辑IO

areBothZeroLogic :: Int -> Int -> Bool
areBothZeroLogic a b
  | a == 0 && b == 0 = True
  | otherwise        = False

然后,我们将其传递到IO我们想要的逻辑:

areBothZeroIO :: Int -> IO Int -> IO Bool
areBothZeroIO a mb = do
    b <- mb -- Use do-notation to work with the value 'inside' the IO:
    return $ areBothZeroLogic a b

立即,这IO逻辑与纯逻辑分开。这是 Haskell 中的基本设计原则,您应该始终尝试遵循。


现在,到你的问题上。

您的问题更加混乱,并且还有其他几个问题,这向我表明您尚未考虑如何最好地将问题分解为更小的部分。然而,一个更好的解决方案可能看起来像这样,也许有更好的名字:

--                              Look here! vvvvvv    vvvvvv
jacPure :: Int -> Int -> [Int] -> [Int] -> Double -> Double
jacPure m k mu nu arrVal
  | nu!!0 == 0                 = 1
  | length nu > m && nu!!m > 0 = 0
  | m == 1                     = x!!0^(nu!!0) * theproduct (nu!!0)
  | k == 0 && arrVal /= 0      = XXX
  | otherwise                  = YYY

jac :: Int -> Int -> [Int] -> [Int] -> IOArray (Int,Int) Double -> IO Double
jac m k mu nu arr = do
    arrVal <- readArray arr (1,1) -- Use do-notation to work with the value 'inside' the IO:
    return $ jacPure m k mu nu arrVal 

您应该立即明白为什么这要好得多。在实现逻辑时,谁在乎IO领域中发生了什么?在应该是纯逻辑的内容中包含一个IO就像告诉作者他们的书将要印刷的纸张的酸度一样——这与他们的工作无关。总是分开逻辑和IO

当然还有其他方法可以做到这一点,有些方法很可能比我建议的方法更好。但是,不可能通过您提供的代码知道最佳路径。您应该致力于更多地了解 monad 并更好地使用它们,这样您就可以自己做出这个判断。


我怀疑这个问题是由于对 Monads 和 monadic 操作缺乏了解。如果您是初学者,我建议您阅读相关的 LYAH 章节,我发现它对初学者也很有帮助。


推荐阅读