打印表达式的值是调试中的常见做法。例如,如果我有一段像这样的代码
my . super . cool . fUnCtIoN . chain $ value
我试图查看fUnCtIoN . chain
的输出,我会添加
my . super . cool . (\ x -> traceShow x x ) . fUnCtIoN . chain $ value
对于像这样的简单任务而言是满口的,更不用说我是否想要打印许多中间结果:
(\ x -> traceShow x x )
. my
. (\ x -> traceShow x x )
. super
. (\ x -> traceShow x x )
. cool
. (\ x -> traceShow x x )
. fUnCtIoN
. (\ x -> traceShow x x )
. chain
$ value
看起来很糟糕。有更好的方法吗?
答案 0 :(得分:7)
只需使用traceShowId
!它完全符合您的要求。
my . super . cool . traceShowId . fUnCtIoN . chain $ value
答案 1 :(得分:4)
是。 join traceShow
。
λ> import Control.Monad
λ> :t join
join :: Monad m => m (m a) -> m a
λ> :t join (+)
join (+) :: Num a => a -> a
如果是函数monad join f x = f x x
,那么join traceShow
相当于\x -> traceShow x x
。
或者制作where
子句,提供(.)
的新定义:
--...your code without the nasty bits...
where
(.) f g a = f ( join traceShow (g a))
这可能只会有所帮助,但会有比以前更多的traceShow
来电。
答案 2 :(得分:2)
如何为函数添加跟踪调用的辅助函数:
dbg :: Show a => String -> a -> a
dbg name x = trace (name ++ ": " ++ show x) x
main = do
let x = dbg "my" . my
. dbg "super" . super
. dbg "cool" . cool
. dbg "func" . fUnCtIoN
. dbg "chain" . chain
$ value
print x
my = (+1)
super = (+2)
cool = (+3)
fUnCtIoN = (+4)
chain = (+5)
value = 3
输出:
chain: 3
func: 8
cool: 12
super: 15
my: 17
18
答案 3 :(得分:1)
你可以写一个高阶函数,它接受两个参数的函数,并对两个参数使用相同的值。
applyBoth :: (a -> a -> b) -> a -> b
applyBoth f x = f x x
(旁白:对于“读者”monad join
,这是(->) a
。)
然后你可以以咖喱形式使用该组合器:
applyBoth traceShow
. my
. applyBoth traceShow
. super
. applyBoth traceShow
. cool
. applyBoth traceShow
. fUnCtIoN
. applyBoth traceShow
. chain
$ value
或者为applyBoth traceShow
定义别名。
traceS = applyBoth traceShow
traceS
. my
. traceS
. super
. traceS
. cool
. traceS
. fUnCtIoN
. traceS
. chain
$ value
对于最大的简洁点,您可以通过折叠将traceS
自动交错到函数列表中:
showSteps :: Show a => [a -> a] -> a -> a
showSteps = foldr (\f g -> f . traceS . g) id
showSteps [my, super, cool, fUnCtIoN, chain] value
编辑呃,到底是什么......它并不完全相关,但是当你想通过多种类型管道数据时,如何让showSteps
工作。这是我们在没有GHC的高级类型系统功能的情况下无法编写的程序示例(在此实例中为GADTs
和RankNTypes
)。
Path
是一个GADT,它解释了如何遍历类型的有向图,从源类型x
开始到目标类型y
结束。它由类别c :: * -> * -> *
参数化。
infixr 6 :->
data Path c x y where
End :: Path c z z
(:->) :: c x y -> Path c y z -> Path c x z
:->
提醒我们,千里之行始于一步:如果您正在使用的类别允许您从x
转到y
,那么您可以从y
到z
采取路径,您可以从x
转到z
。
End
适用于您到达目的地的时间 - 从z
步行至z
非常容易,不会走路。
所以Path
具有与链表相同的递归结构,但是对其中的内容采用更灵活的方法。只要一个箭头的返回类型与下一个箭头的输入类型匹配,它就不会要求所有元素具有相同的类型,而是为您提供一种连接像多米诺骨牌一样的箭头的方法。 (若要使用数学术语:如果您将基础类别c
视为逻辑关系,则End
augments c
with reflexivity和:->
augments c
with transitivity。Path c
因此构造自反传递闭包<{em> c
。另一种看待此问题的方法是Path
是免费类别,就像[]
是免费幺半群;您可以instance Category (Path c)
定义c
而不受Path
约束。)
您可以使用与折叠列表完全相同的代码折叠foldr :: (forall x y. c x y -> r y z -> r x z) -> r z z -> Path c x z -> r x z
foldr f z End = z
foldr f z (x :-> xs) = f x $ foldr f z xs
,但类型更精确:折叠功能无法知道 a priori 关于路径内箭头的类型。
type TAS = Path (->)
此时,我可以定义类型对齐的函数序列(f :-> g :-> h :-> End
),并向您展示h . g . f
如何折叠到->
,但由于我们的目标是打印除了所有中间值之外,我们必须使用比普通旧data Showoff x y where
Showoff :: Show y => (x -> y) -> Showoff x y
更多结构的类别。 (感谢@dfeuer在建议的评论中 - 我调整了他的名字,以更好地反映我的行为引起注意的性质。)
Showoff
y
就像常规函数一样,除了它确保返回值Show
能够showSteps
。我们可以使用这些额外的知识为每个步骤为Showoff
的路径编写type ShowTAS = Path Showoff
showSteps :: ShowTAS a b -> a -> b
showSteps path = foldr combine id path . traceS
where combine (Showoff f) g = g . traceS . f
。
traceS
在所有这些强烈类型的乐趣中,使用不纯的 String
权利让我感到有些羞耻。在现实生活中,我可能会回答String
以及答案。
为了证明它确实有效,这里有一系列不同类型的函数。我们将read
,Int
加入Float
,向其添加一个,将其转换为chain :: ShowTAS String Float
chain = Showoff read :-> plusOne :-> toFloat :-> divideTwo :-> End
where plusOne :: Showoff Int Int
plusOne = Showoff (+1)
toFloat :: Showoff Int Float
toFloat = Showoff fromIntegral
divideTwo :: Showoff Float Float
divideTwo = Showoff (/2)
ghci> showSteps chain "4"
"4"
4
5
5.0
2.5
2.5 -- this last one is not from a traceShow call, it's just ghci printing the result
,然后将其除以2.
$draw = new \ImagickDraw();
$draw->setFont("../fonts/Arial.ttf");
$draw->setFontSize(48);
$draw->setStrokeAntialias(true);
$draw->setTextAntialias(true);
$draw->setFillColor('#ff0000');
$textOnly = new \Imagick();
$textOnly->newImage(600, 300, "rgb(230, 230, 230)");
$textOnly->setImageFormat('png');
$textOnly->annotateImage($draw, 30, 40, 0, 'Your Text Here');
$textOnly->trimImage(0);
$textOnly->setImagePage($textOnly->getimageWidth(), $textOnly->getimageheight(), 0, 0);
$distort = array(180);
$textOnly->setImageVirtualPixelMethod(Imagick::VIRTUALPIXELMETHOD_TRANSPARENT);
$textOnly->setImageMatte(true);
$textOnly->distortImage(Imagick::DISTORTION_ARC, $distort, false);
$textOnly->setformat('png');
header("Content-Type: image/png");
echo $textOnly->getimageblob();
趣!