我正在尝试写入文件中的随机整数列表。这里的writeFile
似乎有问题。当我使用我的函数时randomFile
它说no instance for (Show (IO a0))
.我看到writeFile
没有打印任何内容到屏幕,而是IO()
,所以当我调用函数时randomFile 1 2 3
它说no Instance for Show (IO a0)
但实际上我只想执行该函数而不必打印任何东西,但我如何避免这个问题。我可能在这里犯了很多错误。任何帮助。
import Control.Monad
import Control.Applicative
import System.Random
randNo mind maxd = randomRIO (mind,maxd)
randomFile mind maxd noe = do
let l=(replicate (fromInteger(noe ^ noe)) ( mind `randNo` maxd))
writeFile "RFile.txt" (show l)
我认为您对IO是什么有误解。如果你还没有这样做,我强烈建议你通过学习一个哈斯克尔的输入和输出部分。
IO
不一定与print
有关。在Haskell中,内存中由您自己的代码创建的每个条目都被认为是"纯的",而任何涉及计算机其余部分的条目都存在于IO中(随着时间的推移,您将了解一些例外(。
我们使用称为Monad的东西对IO进行建模。你做哈斯克尔的时间越长,你就会学到更多关于这一点的信息。为了理解这一点,让我们看一些使用 IO 和不使用 IO 的代码示例:
noIOused :: Int -> Int
noIOused x = x + 5
usesIO :: Int -> IO Int
usesIO x = print x >> return (x + 5)
usesIO2 :: Int -> IO Int
usesIO2 x = do
print x
return (x + 5)
第一个函数是"纯"。第二个和第三个功能具有IO"效果",以打印到屏幕的形式出现。 usesIO
和usesIO2
只是做同一件事的两种不同方式(它是相同的代码,但语法不同(。我将使用第二种格式,从这里开始称为do
表示法。
以下是一些其他可能具有 IO 效果的方法:
add5WithFile :: Int -> IO Int
add5WithFile x = do
writeFile "someFile.txt" (show x)
return (x + 5)
请注意,在该函数中,我们没有打印任何内容,而是编写了一个文件。但是写入文件有副作用,并与系统的其余部分交互。因此,我们返回的任何值都必须包含在 IO 中。
addRandom :: Int -> IO Int
addRandom x = do
y <- randomRIO (1,10)
return (x + y)
addRandom
年,我们称randomRIO (1,10)
.但问题是 randomRIO 不会返回 Int
。它返回一个IO Int
。为什么?因为为了获得真正的随机性,我们需要以某种方式与系统交互。为了解决这个问题,我们必须暂时剥离IO。这就是这行的用武之地:
y <- randomRIO (1,10)
<-
箭头告诉我们,我们希望在 IO 之外获得一个y
值。只要我们保留在do
语法中,y
值将是"纯的"。现在我们可以像使用任何其他值一样使用它。
例如,我们不能这样做:
let w = x + (randomRIO (1,10))
因为那将试图为IO Int
添加Int
.不幸的是,我们的+
函数不知道该怎么做。因此,首先我们必须将randomRIO
的结果"绑定"到y
,然后才能将其添加到x
。
现在让我们看一下您的代码:
let l=(replicate (fromInteger(noe ^ noe)) ( mind `randNo` maxd))
writeFile "RFile.txt" (show l)
l
的类型实际上是IO a0
.这很a0
,因为您还没有告诉编译器您想要什么样的数字。所以它不知道你想要一个分数,一个双精度,一个大整数还是其他什么。
所以第一个问题是让编译器更多地了解你想要什么样的随机数。我们通过添加类型注释来做到这一点:
randNo :: Int -> Int -> IO Int
randNo mind maxd = randomRIO (mind,maxd)
现在,您和编译器都知道randNo
是什么样的值。
现在我们需要将该值"绑定"在 do
表示法中以暂时转义 IO。您可能会认为这很简单,如下所示:
randomFile mind maxd noe = do
l <- replicate (fromInteger(noe ^ noe)) ( mind `randNo` maxd)
writeFile "RFile.txt" (show l)
这肯定会将IO Int
"绑定"到l
对吗?不幸的是不是。这里的问题是replicate
是形式Int -> a -> [a]
的函数。也就是说,给定一个数字和一个类型,它将为您提供该类型的列表。
如果你给replicate
一个IO Int
它会[IO Int]
.这实际上看起来更像这样:List (IO Int)
除了我们使用[]
作为列表的语法糖。不幸的是,如果我们想将IO
值"绑定"到具有<-
的东西上,它必须是最外层的类型。
所以你需要的是一种将[IO Int]
变成IO [Int]
的方法。有两种方法可以做到这一点。如果我们把[IO a] -> IO [a]
放到Hoogle中,我们会得到这个:
sequence :: Monad m => [m a] -> m [a]
正如我之前提到的,我们将IO推广到称为Monad的东西。这没什么大不了的,我们可以假装sequence
有这个签名:sequence :: [IO a] -> IO [a]
,这将是专门用于IO的相同东西。
现在你的函数将像这样完成:
randomFile mind maxd noe = do
l <- sequence (replicate (fromInteger(noe ^ noe)) ( mind `randNo` maxd))
writeFile "RFile.txt" (show l)
但是,sequence
之后是replicate
是人们必须一直做的事情。所以有人去做了一个叫做replicateM
的函数:
replicateM :: Monad m => Int -> m a -> m [a]
现在我们可以像这样编写你的函数:
randomFile mind maxd noe = do
l <- replicateM (fromInteger(noe ^ noe)) ( mind `randNo` maxd)
writeFile "RFile.txt" (show l)
对于一些真正的Haskell魔法,你可以在一行中编写所有3行代码,如下所示:
randomFile mind maxd noe = randomRIO >>= writeFile "RFile.txt" . replicateM (fromInteger(noe ^ noe))
如果这对你来说看起来像胡言乱语,那么你需要学习很多东西。以下是建议的路径:
- 如果你还没有,请从头开始学习你一个Haskell。
- 然后了解你如何发明Monads
- 然后了解更多关于如何在Haskell中使用随机性的信息
- 最后看看你是否能完成20个中级Haskell练习