下面是我使用Data.IORef编写臭名昭著的阶乘的尝试。
import Data.IORef
xgo accRef nRef = go where
go = do
acc <- readIORef accRef
n <- readIORef nRef
writeIORef accRef (acc * n)
writeIORef nRef (n - 1)
if (n > 2) then go else readIORef accRef
facM n = do
accRef <- newIORef 1
nRef <- newIORef n
xgo accRef nRef这段代码被认为是关于如何使用IORefs的假人的一个例子,所以我当然想保持它简单--例如,没有元组,也没有镜头。但整个xgo的事情似乎都是人为的。我们可以在xgo中定义facM吗?编写这样的IORef代码最惯用的方法是什么(当然,我不是指避免IORef,我们在这里都知道fac n = prod [1..n]解决方案)。
Upd:我拿到了:
facM n = do
accRef <- newIORef 1
nRef <- newIORef n
let xgo = go where {
go = do
acc <- readIORef accRef
n <- readIORef nRef
writeIORef accRef (acc * n)
writeIORef nRef (n - 1)
if (n > 2) then go else readIORef accRef
}
xgo因此,不再存在第二个accRef/nRef,但仍然存在xgo/go分离。不知怎么的,如果没有中间的xgo = do { ... },我就不能让go工作
发布于 2023-02-21 19:10:44
首先,有一个bug,如果不是一个非常重要的- 0!表示为1,但当前代码返回0。在你做任何乘法之前,检查n>1就可以绕过这个问题了。
第二,由于acc只用于更新accRef,所以我认为使用modifyIORef而不是readIORef后面跟着writeIORef可能会更干净。但是,这不适用于nRef,因为n实际上在多个地方使用
此外,我希望看到显式类型签名添加到任何顶级函数(facM,以及原始代码中的xgo)中。我发现,知道一个值的确切类型通常会使我们更容易理解它是什么以及为什么。
最后,您提到能够将单独的xgo函数合并到facM中,但最终还必须有一个有效相同的go函数才能工作。我想那可能是你碰到了哈斯克尔那出了名的混乱的缩进规则。例如,以下内容不起作用:
import Data.IORef
facM :: (Num a, Ord a) => a -> IO a
facM n = do
accRef <- newIORef 1
nRef <- newIORef n
let go = do
n <- readIORef nRef
if n > 1 then do
modifyIORef accRef (* n)
writeIORef nRef (n - 1)
go
else readIORef accRef
go因为该do块的内容位于它绑定到(go)的名称的左侧。但是,下列任何一种方法都可以工作,因为该块的缩进比其绑定的名称更大:
import Data.IORef
facM :: (Num a, Ord a) => a -> IO a
facM n = do
accRef <- newIORef 1
nRef <- newIORef n
let
go = do
n <- readIORef nRef
if n > 1 then do
modifyIORef accRef (* n)
writeIORef nRef (n - 1)
go
else readIORef accRef
goimport Data.IORef
facM :: (Num a, Ord a) => a -> IO a
facM n = do
accRef <- newIORef 1
nRef <- newIORef n
let go = do
n <- readIORef nRef
if n > 1 then do
modifyIORef accRef (* n)
writeIORef nRef (n - 1)
go
else readIORef accRef
go发布于 2023-02-22 12:13:13
不确定您想要完成什么,但是您可以使用来自mfix的Control.Monad.Fix替换显式循环。
import Data.IORef
import Control.Monad.Fix (mfix)
facM :: Int -> IO Int
facM n = do
accRef <- newIORef 1
nRef <- newIORef n
mfix
(\loop _ -> do
m <- readIORef nRef
acc <- readIORef accRef
modifyIORef accRef (* m)
modifyIORef nRef (subtract 1)
if m > 2 then loop else readIORef accRef
)
undefined这有可怕的undefined (它可以被()取代),所以对于虚拟人来说绝对不是一个好的例子。
或者像这样,如果你想让他们更困惑:
facM :: Int -> IO Int
facM n
= newIORef (n, 1)
>>= mfix (\loop ref -> do
(m, acc) <- readIORef ref
writeIORef ref (m - 1, acc * m)
if (m > 2) then loop else pure (acc * m)
)https://codereview.stackexchange.com/questions/283470
复制相似问题