Haskell中for块内的赋值

vsaztqbk  于 9个月前  发布在  其他
关注(0)|答案(2)|浏览(69)

我在试着做一个函数来计算一个数的阶乘。它在作业中标出了一个错误,我不知道如何改正。

for list action = mapM_ action list

main :: IO Int
main = do
    x <- getLine
    for [1..read x] (\ i -> do        
        let tmp = 0                    
        tmp <- tmp*i                   
                                       
        )
    return (tmp)
f5emj3cl

f5emj3cl1#

首先,关于循环的说明。我猜是for_ Haskell没有任何循环关键字,但它的monad范式足够灵活,因此它们可以作为库函数实现。
但是,Haskell没有可变变量。所以像tmp <- tmp*i这样的东西永远都没有意义。
(N.B.,你可以写let tmp = tmp*i,但不要这样做。它没有完成任何有用的事情;它不是更新变量,而是 * 定义 * 一个变量,但是用一个无意义的循环定义来引用它自己的结果,即这只是一个非终止计算。
好吧,关于没有可变变量的说法并不完全正确。这些也可以从标准库中获得,它们被称为IORef s。它们不能用<-=语法更新,但可以用Data.IORef模块中定义的IO操作更新。以下方法确实有效:

import Data.Foldable (for_)
import Data.IORef

main :: IO ()
main = do
    x <- getLine
    tmp <- newIORef 1
    for_ [1 .. read x] (\ i -> do        
        modifyIORef tmp (*i)
        )
    readIORef tmp

请注意,没有return。在Haskell中,return不是一个关键字(* 注意到主题了吗?* )而只是一个函数,它将一个纯值(在本例中为Int)注入到一个一元动作(在本例中为IO Int)中。但是阅读IORef本身已经是一个不纯的动作,因此您应该简单地将其用作最后一行。
main返回一个值可能不是你想要的(返回值被忽略);我想你应该把它印出来:

import Data.Foldable (for_)
import Data.IORef

main :: IO ()
main = do
    x <- getLine
    tmp <- newIORef 1
    for_ [1 .. read x] (\ i -> do        
        modifyIORef tmp (*i)
        )
    result <- readIORef tmp
    print result

好吧,这就是你的问题的字面解决方案。

然而,强烈建议 * 不要 * 使用IORef来处理类似的事情。一般来说,Haskell的整个哲学是避免像突变这样的副作用。相反,您应该通过递归定义函数,并且只在最后使用IO来打印完整的结果。有很多这样的例子,一个幽默的例子是The Evolution of a Haskell Programmer

66bbxpm5

66bbxpm52#

Haskell是一种函数式语言。这些语言的一个方面是变量是 * 不可变的 *:一旦赋值,你就永远不能改变这个值。
因此,Haskell没有内置的for循环等。它的“主力”是 recursion:我们递归直到确定某个值。对于阶乘情况,这看起来像:

factorial :: Int -> Int
factorial 0 = 1
factorial n = …

然后main可以看起来像这样:

main :: IO ()
main = do
    n <- readLn
    print (factorial n)

或更短:

main :: IO ()
main = readLn >>= print . factorial

相关问题