Haskell:为什么要使用命名助手函数的约定;围棋;?
当我阅读哈斯克尔的材料或资料时,我看到了很多“go”,但我对它从来都不是很满意(我想它在我脑海中有“goto”的负面含义)。我开始与LYAH一起学习Haskell,在那里我开始倾向于在编写折叠时使用Haskell:为什么要使用命名助手函数的约定;围棋;?,haskell,naming-conventions,Haskell,Naming Conventions,当我阅读哈斯克尔的材料或资料时,我看到了很多“go”,但我对它从来都不是很满意(我想它在我脑海中有“goto”的负面含义)。我开始与LYAH一起学习Haskell,在那里我开始倾向于在编写折叠时使用acc和step。书写的惯例从何而来 最重要的是,go这个名字到底意味着什么?嗯!一些考古学 大约从2004年开始,在对递归函数进行worker/wrapper转换时,我使用了go作为尾部递归worker循环的通用名称。我开始在bytestring中广泛使用它,例如 foldr :: (Word8 -
acc
和step
。书写的惯例从何而来
最重要的是,go
这个名字到底意味着什么?嗯!一些考古学
大约从2004年开始,在对递归函数进行worker/wrapper转换时,我使用了go
作为尾部递归worker循环的通用名称。我开始在bytestring
中广泛使用它,例如
foldr :: (Word8 -> a -> a) -> a -> ByteString -> a
foldr k v (PS x s l) = inlinePerformIO $ withForeignPtr x $ \ptr ->
go v (ptr `plusPtr` (s+l-1)) (ptr `plusPtr` (s-1))
where
STRICT3(go)
go z p q | p == q = return z
| otherwise = do c <- peek p
go (c `k` z) (p `plusPtr` (-1)) q -- tail recursive
{-# INLINE foldr #-}
这可能就是我学会的窍门(我原以为这是安迪·吉尔的论文,但在那里找不到任何go
的用法)。在Gofer中没有以这种形式给出,所以我认为它首先出现在GHC代码库中
到2001年,Simon Marlow在一些系统级代码中使用了go
,因此我们可能会将责任归咎于GHC,这条线索引导我们,其中go
广泛应用于工作函数:
myCollectBinders expr
= go [] expr
where
go bs (Lam b e) = go (b:bs) e
go bs e@(Note (SCC _) _) = (reverse bs, e)
go bs (Cast e _) = go bs e
go bs (Note _ e) = go bs e
go bs e = (reverse bs, e)
GHC 3.02和格拉斯哥
翻开GHC的旧版本,我们发现在GHC 0.29中没有出现这个成语,但在GHC 3.02系列(1998年)中,go
成语随处可见。例如,在1996-1997年的Numeric.lhs
中,在showInt
的定义中:
showInt n r
| n < 0 = error "Numeric.showInt: can't show negative numbers"
| otherwise = go n r
where
go n r =
case quotRem n 10 of { (n', d) ->
case chr (ord_0 + fromIntegral d) of { C# c# -> -- stricter than necessary
let
r' = C# c# : r
in
if n' == 0 then r' else go n' r'
}}
showInt n r
|n<0=错误“Numeric.showInt:无法显示负数”
|否则=继续
哪里
继续=
第10号案例{(n',d)->
{C#C#->的情形chr(ord#u 0+来自积分d)——比必要的更严格
让
r'=C#C#:r
在里面
如果n'==0,则r'否则为n'r'
}}
这是一个不同的实现。但是,深入研究的实现,我们发现它与1997年添加到GHC 2.06的版本不同,1998年4月,Sigbjorn Finne发布了一个非常有趣的补丁
这意味着至少到1998年,Sigbjorn将go
循环添加到GHC“std”库中,同时,许多人拥有go
循环。进一步挖掘,这个——代码来自Simon PJ
因此,我将其称为格拉斯哥习语,由格拉斯哥的人发明,他们在90年代中期从事GHC工作,例如,和。我希望这个习语不仅适用于线性结构(因此也适用于“循环”),还适用于分支(树状)结构
我想知道go
模式与累加参数对应的频率有多高,更一般地说,与米奇·旺德在论文(我一直最喜欢的论文之一)中探讨的连续编码策略对应的频率有多高。
在这些情况下,go
函数有一个特殊的含义,可以用来从优雅的规范中导出有效的代码。显然,Don的答案是正确的。让我补充一个小细节(因为你直接提到的似乎是我的作品):go很好,因为它只有两个字母
哦,Yesod的书之所以在enumerator软件包中投入这么多内容,是因为我已经把enumerator的三部分教程写成了一个博客文章系列,所以我决定把它也包括在书中。enumerator包在整个Yesod中的许多地方都使用,因此它是相关的。我通常调用我的函数loop
。在我阅读的任何Haskell材料中从未见过go
。你能举个例子/参考吗?@Ionuț。(为什么这本书在这个主题上花了这么多的材料我不知道,但这是离题的)为了它的价值,我见过许多C/C++程序员在想不出更好的名字时也把他们的助手函数命名为“go”。FWIW,显式尾部递归在很多方面都是goto的函数版本,包括潜在的混淆。不过,静态类型和范围规则有助于将混淆降至最低。至于名字的选择,我喜欢下面@Michael Snoyman关于长度和适用性的回答。另外,当只有一个helper函数时,它的名称似乎基本上是不相关的,所以我通常只选择“go”或“loop”,因为我必须选择一些东西,它们都是有意义的。我倾向于用“go”表示惰性循环,用“loop”表示严格循环。+1表示“尾部递归工作循环的通用名称”,这在我见过的大多数用法中似乎都是正确的。对于函数f
,我个人通常会使用f'
作为这类事物的名称,尽管我可能会尝试使用go
作为一种近似关键字的习惯用法。有趣的是,showInt
使用这个成语来避免多次评估同一个保护。顺便说一句,对于“go的名字到底意味着什么?”我想说它暗示了goto
,以及将控制权交给助手函数。我模糊的记得这是一个Simon PJ ism。我倾向于使用loop
,除非我正在修改已经使用go
约定的代码。我一直认为它的字面意思是“围着循环转”,我一直认为“围着循环转”是对worker函数的一个命令,以开始它肮脏的递归工作。在任何情况下,就我个人而言,我从一张流融合幻灯片中选择了它,因为在函数名中添加勾号总是有一个问题,那就是我会忘记勾号。我相信它起源于Haskell之前。go是scheme(,)中命名let的流行名称+1“go”只有2个字母(仍然有意义),这是一个很容易理解的事实。当我评论Yesod书中使用的“go”(这是一个很好的名字)时
showInt n r
| n < 0 = error "Numeric.showInt: can't show negative numbers"
| otherwise = go n r
where
go n r =
case quotRem n 10 of { (n', d) ->
case chr (ord_0 + fromIntegral d) of { C# c# -> -- stricter than necessary
let
r' = C# c# : r
in
if n' == 0 then r' else go n' r'
}}