我有一个伪代码,它可以计算用户给出的数字的阶乘。
Process without_title
Define i, tmp, x As Integers;
tmp<-1;
Write "Enter a number to calculate its factorial";
Read x;
For i <- 1 Until x Do
tmp<-tmp * i;
EndTo
Write tmp;
EndProcess
现在我想在Haskell中使用列表解析实现相同的代码
import Data.IORef
factorial::Int -> Int
factorial x = [tmp|i<-[1..x], modifyIORef tmp (*i)]
抛出错误,指出tmp
变量未定义
如何定义tmp
变量,以便使用它?
3条答案
按热度按时间v7pvogib1#
要使用像这样的可变
IORef
来实现阶乘函数,您需要在IO monad中工作,并使用newIORef
操作来创建IORef
,然后使用forM_
、traverse_
或sequence_
或类似的函数来对该IORef
进行一组IO操作。它可能看起来像:或者,如果你更喜欢使用列表解析,那么它看起来更像你上面的尝试,类似于:
(使用严格的
modifyIORef'
比使用懒惰的modifyIORef
提供更好的性能。这可以这样命名:
这通常会比更直接的“纯”解决方案慢,例如:
我的基准测试表明,基于
IORef
的版本比纯基于product
的版本慢大约两倍。czfnxgou2#
为了补充现有的答案:一个问题是,如果你开始涉及IO,你就不能为你的函数使用像
factorial :: Int -> Int
这样的无IO类型签名。另一个(更容易处理的)问题是,如果你直接返回一个列表解析表达式,那么你的函数的结果类型必须是某种列表。它不能是标量值,如
Int
。一种可能的折衷方法是使用Haskell state monad,并将
tmp
产品作为状态。您可以使用合适的列表解析表达式生成所需乘法运算的列表,然后使用sequence_
库函数将它们合并组合起来。就像这样:
根据你的符号使用
tmp
作为状态,即使使用st
会更习惯。gk7wooem3#
好吧,这是非常 un-Haskellish的工作与突变变量。这是您需要使用
IO Ref
s的原因之一:因为ref本身不改变,并且修改其值被视为副作用。因此,如果你想手动实现阶乘,递归被用作重复某个任务的方式,通过向参数传递不同的值可以被视为使用修改后的值执行此迭代,所以:
但是由于Haskell有一些更高级别的实用程序函数,例如我们可以生成一个项目列表,然后确定这些项目的乘积。