gpt4 book ai didi

haskell - 如何在没有 System.IO.Unsafe 的情况下获取 Monad 的值?

转载 作者:行者123 更新时间:2023-12-02 14:11:54 25 4
gpt4 key购买 nike

我刚刚开始学习 Haskell,今天我的第一个项目就开始工作了。它是一个小程序,使用 Network.HTTP.ConduitGraphics.Rendering.Chart ( How to get normal value from IO action in Haskell ) 来绘制特定问题的 Google 搜索结果数量,其中的数字不断变化。

我的问题是管道包中的 simple-http 返回一个 monad (我希望我正确理解了 monad 的概念...),但我只想使用其中的 ByteString,其中包含 html 代码网站。所以直到现在我都使用 download = unsafePerformIO $ simpleHttp url 稍后使用它而不关心 monad - 我想这不是最好的方法。

那么:有没有更好的解决方案,这样我就不必在整个评估过程中随身携带 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

最佳答案

这是一个谎言:

getResultCounter :: String -> Integer

上面的类型签名 promise 生成的整数仅取决于输入字符串,但情况并非如此: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)

上面,我尝试保留代码的原始结构。

现在,在 main 中我们不能再这样做

let res = map getResultCounter searches

但我们可以做到

res <- mapM getResultCounter searches

导入Control.Monad后。

关于haskell - 如何在没有 System.IO.Unsafe 的情况下获取 Monad 的值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30584526/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com