Haskell 如果没有System.IO.Unsafe,如何获取Monad的值?
我刚开始学习Haskell,今天开始了我的第一个项目。这是一个小程序,它使用Haskell 如果没有System.IO.Unsafe,如何获取Monad的值?,haskell,web-crawler,monads,Haskell,Web Crawler,Monads,我刚开始学习Haskell,今天开始了我的第一个项目。这是一个小程序,它使用Network.HTTP.conduct和Graphics.Rendering.Chart()绘制一个特定问题的谷歌搜索结果量,其中包含一个不断变化的数字 我的问题是,来自导管包的simple http返回一个monad(我希望我正确理解monad的概念…),但我只想在其中使用ByteString,它包含网站的html代码。因此,直到现在,我还是使用download=unsafePerformIO$simplehttpu
Network.HTTP.conduct
和Graphics.Rendering.Chart
()绘制一个特定问题的谷歌搜索结果量,其中包含一个不断变化的数字
我的问题是,来自导管包的simple http
返回一个monad(我希望我正确理解monad的概念…),但我只想在其中使用ByteString,它包含网站的html代码。因此,直到现在,我还是使用download=unsafePerformIO$simplehttpurl
来在以后使用它,而不关心monad-我想这不是最好的方法
那么:有没有更好的解决方案,使我不必随身携带单子进行整个评估?还是让它保持返回结果的方式(使用monad)更好
这是完整的程序-提到的行位于getResultCounter
中。如果事情编码不太好,可以做得更好,请注意:
import System.IO.Unsafe
import Network.HTTP.Conduit (simpleHttp)
import qualified Data.ByteString.Lazy.Char8 as L
import Graphics.Rendering.Chart.Easy
import Graphics.Rendering.Chart.Backend.Cairo
numchars :: [Char]
numchars = "1234567890"
isNum :: Char -> Bool
isNum = (\x -> x `elem` numchars)
main = do
putStrLn "Please input your Search (The first 'X' is going to be replaced): "
search <- getLine
putStrLn "X ranges from: "
from <- getLine
putStrLn "To: "
to <- getLine
putStrLn "In steps of (Only whole numbers are accepted):"
step <- getLine
putStrLn "Please have some patience..."
let range = [read from,(read from + read step)..read to] :: [Int]
let searches = map (replaceX search) range
let res = map getResultCounter searches
plotList search ([(zip range res)] :: [[(Int,Integer)]])
putStrLn "Done."
-- Creates a plot from the given data
plotList name dat = toFile def (name++".png") $ do
layout_title .= name
plot (line "Results" dat)
-- Calls the Google-site and returns the number of results
getResultCounter :: String -> Integer
getResultCounter search = read $ filter isNum $ L.unpack parse :: Integer
where url = "http://www.google.de/search?q=" ++ search
download = unsafePerformIO $ simpleHttp url -- Not good
parse = takeByteStringUntil "<"
$ dropByteStringUntil "id=\"resultStats\">" download
-- Drops a ByteString until the desired String is found
dropByteStringUntil :: String -> L.ByteString -> L.ByteString
dropByteStringUntil str cont = helper str cont 0
where helper s bs n | (bs == L.empty) = L.empty
| (n >= length s) = bs
| ((s !! n) == L.head bs) = helper s (L.tail bs) (n+1)
| ((s !! n) /= L.head bs) = helper s (L.tail bs) 0
-- Takes a ByteString until the desired String is found
takeByteStringUntil :: String -> L.ByteString -> L.ByteString
takeByteStringUntil str cont = helper str cont 0
where helper s bs n | bs == L.empty = bs
| n >= length s = L.empty
| s !! n == L.head bs = L.head bs `L.cons`
helper s (L.tail bs) (n + 1)
| s !! n /= L.head bs = L.head bs `L.cons`
helper s (L.tail bs) 0
-- Replaces the first 'X' in a string with the show value of the given value
replaceX :: (Show a) => String -> a -> String
replaceX str x | str == "" = ""
| head str == 'X' = show x ++ tail str
| otherwise = head str : replaceX (tail str) x
import System.IO.Unsafe
导入Network.HTTP.conductor(simpleHttp)
将限定数据.ByteString.Lazy.Char8作为L导入
导入Graphics.Rendering.Chart.Easy
导入Graphics.Rendering.Chart.Backend.Cairo
numchars::[Char]
numchars=“1234567890”
isNum::Char->Bool
isNum=(\x->x`elem`numchars)
main=do
putStrLn“请输入您的搜索(第一个“X”将被替换):
通过testring搜索L
dropByteStringUntil str cont=helper str cont 0
其中,helper s bs n |(bs==L.empty)=L.empty
|(n>=长度s)=bs
|((s!!n)=L.头bs)=助手s(L.尾bs)(n+1)
|((s!!n)/=L.头bs)=助手s(L.尾bs)0
--获取ByteString,直到找到所需的字符串
takeByteStringUntil::String->L.ByteString->L.ByteString
takeByteStringUntil str cont=helper str cont 0
其中,helper s bs n | bs==L.empty=bs
|n>=长度s=L.空
|s!!n==L.head bs=L.head bs`L.cons`
助手s(左尾bs)(n+1)
|s!!n/=L.head bs=L.head bs`L.cons`
助手s(左尾bs)0
--将字符串中的第一个“X”替换为给定值的显示值
replaceX::(显示a)=>String->a->String
replaceX str x | str==“”“
|头str=='X'=显示X++尾str
|否则=头部str:replaceX(尾部str)x
这是一个谎言:
上面的类型签名很有希望得到的整数只取决于输入字符串,但事实并非如此:Google可以添加/删除一个调用到另一个调用的结果,从而影响输出
使类型更诚实,我们得到
getResultCounter :: String -> IO Integer
这确实承认它将与外部世界互动。然后,代码可以轻松地适应:
getResultCounter search = do
let url = "http://www.google.de/search?q=" ++ search
download <- simpleHttp url -- perform IO here
let parse = takeByteStringUntil "<"
$ dropByteStringUntil "id=\"resultStats\">" download
return (read $ filter isNum $ L.unpack parse :: Integer)
但我们能做到
res <- mapM getResultCounter searches
res顺便说一句,Data.Char
提供了isDigit
,这比用这种方式定义自己的函数更方便有效。啊,谢谢,我不知道这个函数存在。@GuntherRocket:即使它不存在,你也可以将它内联:getResultCounter search=read$filter(`elem`“0123456789”)$L.unpack parse
。不需要在顶层定义它!谢谢现在可以了。我已经尝试过类似的东西,但是我错过了mapM,我想我必须更改所有使用ByteString的函数。不管怎样,现在我对单子有了更多的了解。
let res = map getResultCounter searches
res <- mapM getResultCounter searches