所以我正在编写一个数据包嗅探应用程序.基本上我想让它嗅探tcp会话,然后解析它们是否是http,如果它们是,如果它们有正确的内容类型等,请将它们保存为我的硬盘上的文件.
所以,为此,我希望它有效率.由于当前的http库是基于字符串的,我将处理大文件,而我只需要解析http响应,我决定在attoparsec中自己编写.
当我完成我的程序时,我发现当我使用其中的wav文件解析9 meg的http响应时,当我对其进行分析时,它在尝试解析http响应的主体时分配了一大堆内存.当我查看HTTP.prof时,我看到一些行:
httpBody Main 362 1 0.0 0.0 93.8 99.3
take Data.Attoparsec.Internal 366 1201 0.0 0.0 93.8 99.3
takeWith Data.Attoparsec.Internal 367 3603 0.0 0.0 93.8 99.3
demandInput Data.Attoparsec.Internal 375 293 0.0 0.0 93.8 99.2
prompt Data.Attoparsec.Internal 378 293 0.0 0.0 93.8 99.2
+++ Data.Attoparsec.Internal 380 586 93.8 99.2 93.8 99.2
正如你所看到的,httpbody中的某个地方,被称为1201次,导致500+(+++)串行的字节串联,这导致了大量的内存分配.
这是代码.N只是http响应的内容长度(如果有的话).如果没有,它只会尝试采取一切.
我希望它返回一个1000左右的字符字节串的惰性字节串,但即使我把它改为只需要n并返回一个严格的字节串,它仍然有它的那些分配(它使用14 gig的内存).
httpBody n = do
x <- if n > 0
then AC.take n
else AC.takeWhile (\_ -> True)
if B.length x == 0
then return …Run Code Online (Sandbox Code Playgroud) 我很惊讶我无法在任何地方找到答案.
我正在编写一个roguelike,我正在使用来自hackage的ncurses库,这是ncurses库的一个非常好的包装器.现在ncurses有这个怪癖,如果你试图写下右下角,它会这样做,然后它会尝试将光标移动到下一个角色,然后失败,因为没有地方可以将它移动到.它返回一个您只能忽略的错误值.
我的问题是haskell ncurses库编写者尽职地检查所有调用的任何错误,当有一个时,他调用:error"drawText:etc etc.".
在其他语言中,比如c或python,为了解决这个问题,你不得不忽略错误或捕获并忽略异常,但对于我的生活,我无法弄清楚如何在haskell中做到这一点.错误功能是不可恢复的吗?
我将在本地修改库,以便在必要时不检查该函数的错误,但我讨厌这样做.我也对任何可以让我在不移动光标的情况下绘制最后一个字符的解决方法持开放态度,但我不认为这是可能的.
haskell exception-handling exception referential-transparency
我浏览了yesod书和来源,并了解了一切如何运作.但在我写自己的东西之前,脚手架网站中有一件事我不明白.
所以我在一个网站"copywww"上加工,在文件CopyWWWState.hs中有代码:
instance YesodPersist CopyWWWState where
type YesodDB CopyWWWState = SqlPersist
runDB db = liftIOHandler
$ fmap connPool getYesod >>= Settings.runConnectionPool db
instance YesodAuth CopyWWWState where
type AuthId CopyWWWState = UserId
-- Where to send a user after successful login
loginDest _ = RootR
-- Where to send a user after logout
logoutDest _ = RootR
getAuthId creds = runDB $ do
x <- getBy $ UniqueUser $ credsIdent creds
case x of
Just (uid, _) -> return $ Just …Run Code Online (Sandbox Code Playgroud) 我用镜头包编码.一切都很顺利,直到我试图访问代数类型的某个字段:
import Control.Lens
data Type = A { _a :: Char } | B
makeLenses ''Type
test1 = _a (A 'a')
test2 = (A 'a') ^. a
No instance for (Data.Monoid.Monoid Char)
arising from a use of `a'
Possible fix:
add an instance declaration for (Data.Monoid.Monoid Char)
In the second argument of `(^.)', namely `a'
In the expression: (A 'a') ^. a
In an equation for `test2': test2 = (A 'a') ^. a
Run Code Online (Sandbox Code Playgroud)
我可以选择_a,但我的真实程序中的数据类型更深,我打算使用镜头来降低我必须做的工作量.我一直在查看镜头库,但那里有很多,我不确定他是否处理过这种情况,或者它只是镜头库不支持的东西.
作为旁注,如果我实际上使用类似String的monoid作为数据类型而不是Char,那么它会编译并给出正确的答案,我不知道为什么.
编辑:在阅读了hammar的评论后,我尝试了这个,这有效:
test2 = (A 'a') …Run Code Online (Sandbox Code Playgroud) 我非常理解其他语言的3/4,但每次我在我的代码中以有意义的方式使用类时,我都会得到根深蒂固.
为什么这个非常简单的代码不起作用?
data Room n = Room n n deriving Show
class HasArea a where
width :: (Num n) => a -> n
instance (Num n) => HasArea (Room n) where
width (Room w h) = w
Run Code Online (Sandbox Code Playgroud)
因此,房间宽度由整数或浮点数表示,我不想在此时限制它.类和实例都将n类型限制为Nums,但它仍然不喜欢它,我收到此错误:
Couldn't match expected type `n1' against inferred type `n'
`n1' is a rigid type variable bound by
the type signature for `width' at Dungeon.hs:11:16
`n' is a rigid type variable bound by
the instance declaration at Dungeon.hs:13:14
In the expression: w
In …Run Code Online (Sandbox Code Playgroud) 我很惊讶我找不到任何关于此的信息.我必须是唯一一个遇到任何麻烦的人.
所以,假设我有一个破折计数器.我希望它计算字符串中的破折号数,并返回字符串.假装我给出了一个使用parsec状态处理无效的示例.所以这应该工作:
dashCounter = do
str <- many1 dash
count <- get
return (count,str)
dash = do
char '-'
modify (+1)
Run Code Online (Sandbox Code Playgroud)
事实上,这是编译.好的,我尝试使用它:
:t parse dashCounter "" "----"
parse dashCounter "" "----"
:: (Control.Monad.State.Class.MonadState
t Data.Functor.Identity.Identity,
Num t) =>
Either ParseError (t, [Char])
Run Code Online (Sandbox Code Playgroud)
好的,这是有道理的.它应该返回状态和字符串.凉.
>parse dashCounter "" "----"
<interactive>:1:7:
No instance for (Control.Monad.State.Class.MonadState
t0 Data.Functor.Identity.Identity)
arising from a use of `dashCounter'
Possible fix:
add an instance declaration for
(Control.Monad.State.Class.MonadState
t0 Data.Functor.Identity.Identity)
In the first argument of `parse', namely `dashCounter'
In the …Run Code Online (Sandbox Code Playgroud) 在我的工作中,我遇到了很多粗糙的SQL,我有一个明智的想法,即编写一个程序来解析sql并将其打印出来.我很快就完成了大部分工作,但我遇到了一个我不知道如何解决的问题.
所以让我们假装sql是"从1中选择foo".我的想法是总是有一个关键字后跟数据,所以我要做的就是解析一个关键字,然后在下一个关键字之前捕获所有乱码并存储以供以后清理,如果值得的话.这是代码:
import Text.Parsec
import Text.Parsec.Combinator
import Text.Parsec.Char
import Data.Text (strip)
newtype Statement = Statement [Atom]
data Atom = Branch String [Atom] | Leaf String deriving Show
trim str = reverse $ trim' (reverse $ trim' str)
where
trim' (' ':xs) = trim' xs
trim' str = str
printStatement atoms = mapM_ printAtom atoms
printAtom atom = loop 0 atom
where
loop depth (Leaf str) = putStrLn $ (replicate depth ' ') ++ str
loop depth (Branch str atoms) = …Run Code Online (Sandbox Code Playgroud) 我正在试验单身人士图书馆,我发现了一个我不理解的案例.
{-# LANGUAGE GADTs, StandaloneDeriving, RankNTypes, ScopedTypeVariables,
FlexibleInstances, KindSignatures, DataKinds, StandaloneDeriving #-}
import Data.Singletons.Prelude
import Data.Singletons.TypeLits
data Foo (a :: Nat) where
Foo :: Foo a
deriving Show
data Thing where
Thing :: KnownNat a => Foo a -> Thing
deriving instance Show Thing
afoo1 :: Foo 1
afoo1 = Foo
afoo2 :: Foo 2
afoo2 = Foo
athing :: Thing
athing = Thing afoo1
foolen :: forall n. KnownNat n => Foo n -> Integer
foolen foo =
case …Run Code Online (Sandbox Code Playgroud) 默认情况下,管道是基于拉的.这是由于实施的操作员>->,通过+>>该bind操作员是他的拉动类别的有意义的操作员.我的理解是,这意味着如果你有代码producer >-> consumer,首先会调用消费者的身体,然后一旦等待数据,就会调用生产者.
我见过的在pipes文档在这里,你可以使用代码(reflect .)从Pipes.Core把一个基于拉管道进入基于推送管.这意味着(纠正我,如果我错了)在上面的代码中producer >-> consumer,生产者先运行,产生一个值,然后消费者试图消费.这似乎非常有用,我想知道如何做到这一点.
我在这里的讨论中也看到没有基于推送的对应物,>->因为它很容易转换任何管道(我假设有反射?),但我无法真正想出如何做或找到任何例子.
这是我尝试过的一些代码:
stdin :: Producer String IO r
stdin = forever $ do
lift $ putStrLn "stdin"
str <- lift getLine
yield str
countLetters :: Consumer String IO r
countLetters = forever $ do
lift $ putStrLn "countLetters"
str <- await
lift . putStrLn . show . length $ str
-- …Run Code Online (Sandbox Code Playgroud) 我已经解析了大量的json,操纵了一些值,我想把它写回来.Aeson将数字解码为科学,但是当它编码时,默认情况下,科学在很多情况下用科学记数法显示数字,而且aeson没有提供任何我可以看到的改变它的方法.
> decode "[\"asdf\", 1, 1.0, 1000000000.1, 0.01]" :: Maybe Value
Just (Array [String "asdf",Number 1.0,Number 1.0,Number 1.0000000001e9,Number 1.0e-2])
encode (Array [String "asdf",Number 1.0,Number 1.0,Number 1.0000000001e9,Number 1.0e-2])
"[\"asdf\",1,1,1.0000000001e9,1.0e-2]"
> encode (Array [String "asdf", Number 1, Number 1.0, Number 1000000000.1, Number 0.01])
"[\"asdf\",1,1,1.0000000001e9,1.0e-2]"
Run Code Online (Sandbox Code Playgroud)
如何以更广泛接受的格式用其他语言可以使用的数字写出我的值?让我假装我不关心精度损失或整数溢出.科学软件包具有以这种方式格式化数字的方法,aeson恰好没有使用它.
>formatScientific Fixed Nothing (0.01)
"0.01"
>formatScientific Fixed Nothing (1000000000.1)
"1000000000.1"
Run Code Online (Sandbox Code Playgroud)