haskell 在列表解析格式函数中定义变量

a8jjtwal  于 2023-10-19  发布在  其他
关注(0)|答案(3)|浏览(138)

我有一个伪代码,它可以计算用户给出的数字的阶乘。

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变量,以便使用它?

v7pvogib

v7pvogib1#

要使用像这样的可变IORef来实现阶乘函数,您需要在IO monad中工作,并使用newIORef操作来创建IORef,然后使用forM_traverse_sequence_或类似的函数来对该IORef进行一组IO操作。它可能看起来像:

import Control.Monad
import Data.IORef

factorial :: Int -> IO Int
factorial x = do
  tmp <- newIORef 1
  forM_ [1..x] $ \i -> modifyIORef' tmp (*i)
  readIORef tmp

或者,如果你更喜欢使用列表解析,那么它看起来更像你上面的尝试,类似于:

import Data.IORef

factorialIO :: Int -> IO Int
factorialIO x = do
  tmp <- newIORef 1
  sequence_ [modifyIORef' tmp (*i) | i <- [1..x]]
  readIORef tmp

(使用严格的modifyIORef'比使用懒惰的modifyIORef提供更好的性能。
这可以这样命名:

main :: IO ()
main = do
  result <- factorial 10
  print $ result

这通常会比更直接的“纯”解决方案慢,例如:

factorial :: Int -> Int
factorial x = product [1..x]

我的基准测试表明,基于IORef的版本比纯基于product的版本慢大约两倍。

czfnxgou

czfnxgou2#

为了补充现有的答案:一个问题是,如果你开始涉及IO,你就不能为你的函数使用像factorial :: Int -> Int这样的无IO类型签名。
另一个(更容易处理的)问题是,如果你直接返回一个列表解析表达式,那么你的函数的结果类型必须是某种列表。它不能是标量值,如Int
一种可能的折衷方法是使用Haskell state monad,并将tmp产品作为状态。您可以使用合适的列表解析表达式生成所需乘法运算的列表,然后使用sequence_库函数将它们合并组合起来。
就像这样:

import  Control.Monad.State (state, runState, sequence)

factorial :: Int -> Int
factorial x = finalTmp  where
    tmp0           = 1  -- our initial state
    actions        = [ state (\tmp -> ((), i*tmp)) | i <- [1..x] ]
    (rs, finalTmp) = runState  (sequence_ actions)  tmp0

根据你的符号使用tmp作为状态,即使使用st会更习惯。

gk7wooem

gk7wooem3#

好吧,这是非常 un-Haskellish的工作与突变变量。这是您需要使用IO Ref s的原因之一:因为ref本身不改变,并且修改其值被视为副作用。
因此,如果你想手动实现阶乘,递归被用作重复某个任务的方式,通过向参数传递不同的值可以被视为使用修改后的值执行此迭代,所以:

factorial::Int -> Int
factorial 0 = 1
factorial n = n * (factorial (n-1))

但是由于Haskell有一些更高级别的实用程序函数,例如我们可以生成一个项目列表,然后确定这些项目的乘积。

相关问题