哈斯克尔懒惰 - 我如何迫使IO更快地发生? [英] Haskell laziness - how do I force the IO to happen sooner?

查看:105
本文介绍了哈斯克尔懒惰 - 我如何迫使IO更快地发生?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚开始学习Haskell。下面是一些用命令式编写的代码,它实现了一个简单的服务器 - 它打印出HTTP请求头。除了我需要在Haskell中重新思考这个事实,为了处理懒惰列表和更高阶的函数,我想清楚地看到它为什么没有按照我的意图去做。它总是落在后面 - 我用一个请求触发它,什么都没有发生,再次触及它,打印第一个请求,第三次打它,打印第二个请求等。为什么?这个代码的最小变化是什么,当请求进来时会导致它打印正确?

  import Network 
import System.IO
import Network.HTTP.Headers

acceptLoop :: Socket - > IO()
acceptLoop s = do
(handle,hostname,_)< - 接受s
putStrLn(来自++主机名的接受连接)
text< - hGetContents处理
让lns =行文本
hds =尾部lns
打印$ parseHeaders hds
h关闭句柄
acceptLoop s


main :: IO()
main = do
s< - listenOn(PortNumber 8080)
acceptLoop s

感谢,
Rob

跟进



全部答案是有帮助的。下面的代码工作,但没有使用bytestrings,建议,但。一个后续问题:可以使用标准库中的一些函数替代 ioTakeWhile ,可能在Control.Monad中?

  ioTakeWhile ::(a  - > Bool) - > [IO a]  - > IO [a] 
ioTakeWhile pred actions = do
x< - 头部动作
if pred x
then(ioTakeWhile pred(tail actions))>> = \xs - > return(x:xs)
else return []

acceptLoop :: Socket - > IO()
acceptLoop s = do
(handle,hostname,_)< - 接受s
putStrLn(来自++主机名的接受连接)
let lineActions = repeat (hGetLine句柄)
行< - ioTakeWhile(/ =\r)lineActions
打印行
h关闭句柄


解决方案

你的问题是使用 hGetContents 套接字关闭。您可以尝试解析输入的最后一行,直到连接终止才会知道该输入。

解决方案:获取与您一样多的数据需要(或可用),然后终止连接。



这很晚了,我很累,但这是一个我不知道的解决方案(阅读:丑陋的罪过) :你可以移动到字节串(无论如何应该这样做)并使用 hGetNonBlocking hGetSome 而不是 hGetContents 。或者,您可以持续< hGetLine (阻止),直到解析成功达到您的满意度:

  import Network 
import System.IO
import Network.HTTP.Headers
Import Control.Monad
将合格的Data.ByteString.Char8导入为B
导入Data.ByteString(hGetSome)

acceptLoop :: Socket - > IO()
acceptLoop s = do
(handle,hostname,_)< - 接受s
putStrLn(来自++主机名的接受连接)
printHeaders句柄B.空
h关闭句柄
其中
printHeaders hs = do
t < - hGetSome h 4096
let str = B.append st - 低效!
loop = printHeaders h str
case(parseHeaders。tail。lines)(B.unpack str)of
Left _ - >循环
Right x
|长度x < 3 - >循环
|否则 - > print x

main :: IO()
main = do
hSetBuffering stdin NoBuffering
s< - listenOn(PortNumber 8080)
forever $ acceptLoop s


I just started learning Haskell. Below is some code written in an imperative style that implements a simple server -- it prints out the HTTP request headers. Besides the fact that I need to rethink it in Haskell, to work with lazy lists and higher order functions, I'd like to see clearly why it does not do what I intended. It is always one behind -- I hit it with a request, nothing happens, hit it again, it prints the first request, hit it the 3rd time, it prints the 2nd request, etc. Why is that? And what is a minimal change to this code that would cause it to print right when the request came in?

import Network
import System.IO
import Network.HTTP.Headers

acceptLoop :: Socket -> IO ()
acceptLoop s = do
  (handle, hostname, _) <- accept s
  putStrLn ("Accepted connection from " ++ hostname)
  text <- hGetContents handle
  let lns = lines text
      hds = tail lns
  print $ parseHeaders hds
  hClose handle
  acceptLoop s


main :: IO ()
main = do
  s <- listenOn (PortNumber 8080)
  acceptLoop s

thanks, Rob

Followup

All the answers were helpful. The code below works, but does not use bytestrings, as suggested, yet. A followup question: can ioTakeWhile be replaced by using some functions from the standard libraries, maybe in Control.Monad?

ioTakeWhile :: (a -> Bool) -> [IO a] -> IO [a]
ioTakeWhile pred actions = do
  x <- head actions
  if pred x
    then (ioTakeWhile pred (tail actions)) >>= \xs -> return (x:xs)
    else return []

acceptLoop :: Socket -> IO ()
acceptLoop s = do
  (handle, hostname, _) <- accept s
  putStrLn ("Accepted connection from " ++ hostname)
  let lineActions = repeat (hGetLine handle)
  lines <- ioTakeWhile (/= "\r") lineActions
  print lines
  hClose handle

解决方案

Your problem is using hGetContents will get all contents on the handle till the socket closes. You follow this call by trying to parse the last line of the input, which won't be known till the connection terminates.

The solution: get as much data as you need (or is available) then terminate the connection.

It's late and I'm tired but here's a solution I know is non-optimal (read: ugly as sin): You can move to bytestrings (should do this anyway) and use hGetNonBlocking or hGetSome instead of hGetContents. Alternatively, you can hGetLine (blocking) continually till the parse succeeds to your satisfaction:

import Network
import System.IO
import Network.HTTP.Headers
import Control.Monad
import qualified Data.ByteString.Char8 as B
import Data.ByteString (hGetSome)

acceptLoop :: Socket -> IO ()
acceptLoop s = do
    (handle, hostname, _) <- accept s
    putStrLn ("Accepted connection from " ++ hostname)
    printHeaders handle B.empty
    hClose handle
  where
  printHeaders h s = do
  t <- hGetSome h 4096
  let str  = B.append s t -- inefficient!
      loop = printHeaders h str
  case (parseHeaders . tail . lines) (B.unpack str) of
      Left _   -> loop
      Right x
       | length x < 3 -> loop
       | otherwise    -> print x

main :: IO ()
main = do
  hSetBuffering stdin NoBuffering
  s <- listenOn (PortNumber 8080)
  forever $ acceptLoop s

这篇关于哈斯克尔懒惰 - 我如何迫使IO更快地发生?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆