我正在尝试写入文件中的随机整数列表.这里好像有问题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)
Run Code Online (Sandbox Code Playgroud)
我认为你对IO是什么有误解.如果你还没有这样做,我强烈建议你阅读Learn You a Haskell的输入和输出部分.
IO不一定与任何事情有关print.在Haskell中,由您自己的代码创建的内存中的每个条目都被认为是"纯粹的",而任何接触计算机其余部分的条目都存在于IO中(除了一些例外,您将随着时间的推移了解).
我们使用称为Monad的东西来建模IO.您将了解有关Haskell工作时间越长的更多信息.为了理解这一点,让我们看一下使用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)
Run Code Online (Sandbox Code Playgroud)
第一个功能是"纯粹".第二和第三功能具有IO"效果",其以打印到屏幕的形式出现.usesIO并且usesIO2只是两种不同的方式做同样的事情(它是相同的代码,但语法不同).我将使用第二种格式,do从这里称为符号.
以下是其他一些可能产生IO效果的方法:
add5WithFile :: Int -> IO Int
add5WithFile x = do
writeFile "someFile.txt" (show x)
return (x + 5)
Run Code Online (Sandbox Code Playgroud)
请注意,在该函数中我们没有打印任何东西,我们写了一个文件.但是编写文件会产生副作用,并与系统的其他部分进行交互.因此,我们返回的任何值都必须包含在IO中.
addRandom :: Int -> IO Int
addRandom x = do
y <- randomRIO (1,10)
return (x + y)
Run Code Online (Sandbox Code Playgroud)
在addRandom我们打电话randomRIO (1,10).但问题是randomRIO没有返回Int.它返回一个IO Int.为什么?因为为了获得真正的随机性,我们需要以某种方式与系统交互.为了解决这个问题,我们必须暂时剥离IO.这就是这条线的用武之地:
y <- randomRIO (1,10)
Run Code Online (Sandbox Code Playgroud)
那个<-箭头告诉我们,我们想要一个yIO之外的值.只要我们保持do语法内部y值将是"纯粹的".现在我们可以像任何其他值一样使用它.
所以例如我们不能这样做:
let w = x + (randomRIO (1,10))
Run Code Online (Sandbox Code Playgroud)
因为那将是尝试添加Int到IO Int.不幸的是,我们的+功能不知道如何做到这一点.所以首先我们必须将结果"绑定" randomRIO到y我们可以添加之前x.
现在让我们来看看你的代码:
let l=(replicate (fromInteger(noe ^ noe)) ( mind `randNo` maxd))
writeFile "RFile.txt" (show l)
Run Code Online (Sandbox Code Playgroud)
l实际上是这种类型IO a0.这是a0因为你没有告诉编译器你想要什么样的号码.所以它不知道你是否需要分数,双数,大整数或其他什么.
所以第一个问题是让编译器更多地了解你想要什么样的随机数.我们通过添加类型注释来完成此操作:
randNo :: Int -> Int -> IO Int
randNo mind maxd = randomRIO (mind,maxd)
Run Code Online (Sandbox Code Playgroud)
现在你和编译器都知道什么样的值randNo.
现在我们需要在do符号内"绑定"该值以暂时转义IO.您可能认为这很简单,如下所示:
randomFile mind maxd noe = do
l <- replicate (fromInteger(noe ^ noe)) ( mind `randNo` maxd)
writeFile "RFile.txt" (show l)
Run Code Online (Sandbox Code Playgroud)
肯定会"绑定" 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]
Run Code Online (Sandbox Code Playgroud)
正如我之前提到的,我们将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)
Run Code Online (Sandbox Code Playgroud)
但sequence随后replicate是人们必须一直做的事情.所以有人去做了一个叫做的函数replicateM:
replicateM :: Monad m => Int -> m a -> m [a]
Run Code Online (Sandbox Code Playgroud)
现在我们可以像这样编写你的函数:
randomFile mind maxd noe = do
l <- replicateM (fromInteger(noe ^ noe)) ( mind `randNo` maxd)
writeFile "RFile.txt" (show l)
Run Code Online (Sandbox Code Playgroud)
对于一些真正的Haskell魔法,你可以在一行中编写所有3行代码,如下所示:
randomFile mind maxd noe = randomRIO >>= writeFile "RFile.txt" . replicateM (fromInteger(noe ^ noe))
Run Code Online (Sandbox Code Playgroud)
如果这看起来像是胡言乱语,那么你需要学习很多东西.这是建议的路径: