在 Haskell 中为 块 (for block) 进行分配

Assignment in Haskell within for block

提问人:Alejandro Caro 提问时间:9/11/2023 最后编辑:Alejandro Caro 更新时间:9/12/2023 访问量:115

问:

我正在尝试制作一个函数来计算一个数字的阶乘。它在作业中标记了一个错误,我不知道如何更正它。

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)
函数 for-loop haskell

评论

3赞 Noughtmare 9/11/2023
我建议避免使用 IO,现在,只需使用递归编写一个纯阶乘函数。然后你可以像这样将其插入 main: .formain = do x <- getLine; print (factorial (read x))
0赞 willeM_ Van Onsem 9/11/2023
请不要使用 ,也不起作用,因为变量是不可变的:一旦分配了一个值,你就不能分配一个不同的值。fortmp

答:

4赞 leftaroundabout 9/11/2023 #1

首先,关于循环的说明。我假设for_。Haskell没有任何循环关键字,但它的monad范式足够灵活,因此它们可以作为库函数实现。

但是,Haskell 没有可变变量。所以这样的事情永远没有意义。
(注意,你实际上可以写,但不要这样做。它没有任何用处;它没有更新变量,而是定义了一个变量,但用一个无意义的循环定义来引用它自己的结果,即这只是一个非终止计算。
tmp <- tmp*ilet tmp = tmp*i

好吧,关于没有可变变量的说法并不完全正确。这些也可以从标准库中获得,它们称为 IORefs。但是,它们不能使用语法或语法进行更新,而是使用模块中定义的操作进行更新。以下方法确实有效:<-=IOData.IORef

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

请注意,没有 .在 Haskell 中,return 不是一个关键字(注意到一个主题?),而只是一个将纯值(在本例中为 )注入到单子动作中的函数(在本例中为 )。但是阅读本身已经是一个不纯粹的动作,因此你应该简单地用它作为最后一行。returnIntIO IntIORef

不过,返回值可能不是您想要的(返回值被忽略);我想你是想打印它: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

好了,这就是你问题的字面修复。

但是,强烈建议不要用于此类操作。一般来说,Haskell的整个理念是避免突变等副作用。相反,您应该通过递归来定义函数,并且仅在最后用于打印完整结果。这方面的例子数不胜数,一个幽默的例子是 Haskell 程序员的演变IORefIO

评论

0赞 leftaroundabout 9/11/2023
@chi是的,很公平。
0赞 leftaroundabout 9/11/2023
@AlejandroCaro好吧,我最初包括Control.Monad.forM_,但 chi 指出现在更喜欢Data.Foldable.for_。当然,进口需要相应调整。
0赞 Alejandro Caro 9/12/2023
做一些小的修改,代码就起作用了。我已经在您的回答中发布了这些修改
0赞 leftaroundabout 9/12/2023
有这样的定义并非没有道理,但是在(或较旧的)的情况下,真的没有意义,因为标准版本是完全建立的,并且只花费一个字符。for_forM_
0赞 Alejandro Caro 9/12/2023
另一方面,这个正在工作(onecompiler.com/haskell/3zm9ktbwv)
4赞 willeM_ Van Onsem 9/11/2023 #2

Haskell是一种函数式语言。如果这种语言的方面是变量是不可变的:一旦分配了一个值,你就永远无法改变该值。

因此,Haskell没有内置循环等。它的“主力”是递归:我们递归,直到确定某个值。对于阶乘情况,如下所示:for

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

然后主要可以如下所示:

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

或更短:

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