我有一个函数tabulate
,它接受一个对象列表,以及一个将这些对象的字段转换为Builders
的函数列表。它返回一个表示格式良好的表的Builder
。 E.g:
tabulate :: [a -> Builder] -> [a] -> Builder
tabulate = ...
data Assc = Assc { key :: String, value :: String }
> let funcs = [string7 . key, const $ char7 '=', string7 . value ]
> let objs = ["short" `Assc` "a", "longer" `Assc` "b", "waylongername" `Assc` "c"]
> hPutBuilder stdout $ tabulate funcs objs
short = a
longer = b
waylongername = c
为此,我需要确定每列的最大长度。目前我在每个元素上使用toLazyByteString
(这太慢了)。
是否有可能在没有先将Builder
变为ByteString
的情况下获得tabulate
的长度?
或者,是否存在(有效)实施Builder
的另一种方式(使用或不使用{{1}})?
答案 0 :(得分:5)
查看Builder
的来源,将其定义为
newtype Builder = Builder (forall r. BuildStep r -> BuildStep r)
因此,排序Builder
只是组成函数,如果不评估函数堆栈,就无法获得输出的长度。但是你可以创建一个自己的帮助器模块,其数据类型可以将Builder
与长度计算结合起来:
newtype BuilderL = BuilderL { blLenght :: !Int, blBuilder :: Builder }
instance Monoid BuilderL where
mempty = BuilderL 0 mempty
mappend (BuilderL l1 t1) (BuilderL l2 t2) = BuilderL (l1 + l2) (t1 <> t2)
然后创建辅助函数来构造BuilderL
s,比如
byteString :: ByteString -> BuilderL
byteString t = BuilderL (length t) (byteString t)
等。然后使用此模块和BuilderL
作为您的表格,您将始终掌握所需的长度。