我正在做一个代码转换项目,需要我生成变量名。我生成的名称都不应该是彼此重复的。
我真的很沮丧,因为使用Python生成器功能会非常简单和优雅。
我之前的做法是通过递归调用我的翻译代码传递一个计数器变量,然后将(可能递增的)计数器传递回基本上每个函数的返回值。
这真的很乱:它增加了一个额外的参数来跟踪这些功能;更糟糕的是,它还强迫我使用凌乱的元组返回值,否则我会有一个简单的一元返回值。
我在Haskell的短暂时间内从未真正熟练使用monad,但我有一个想法,我可以使用State
monad上的包装来模拟全局计数器变量。经过3天的努力试图修改monad并制作我自己的monad之后,然后试图改变别人的monad以产生我需要的价值,我终于辞职了,直接使用其他人& #39;高级单子(也许有一些改动。)
我已将MonadSupply和MonadUnique模块识别为一对可能提供我需要的简单接口的模块。不幸的是,我无法弄清楚如何使用它们。
特别是MonadSupply
模块文档提供了这个很好的示例用例:
runSupplyVars x = runSupply x vars
where vars = [replicate k ['a'..'z'] | k <- [1..]] >>= sequence
看起来像我想要的!一旦我得到了编译模块,我在解释器中检查了这个函数的类型:
> :t runSupplyVars
runSupplyVars :: Supply [Char] a -> Identity (a, [[Char]])
我已经尝试过很多(价值数小时)不同的东西给这个功能,没有成功。我也尝试将函数传递给其他各种函数,以查看它们是否会隐式提供我需要的参数。到目前为止没有运气。
有人可以提供此runSupplyVars
函数的示例用例吗?
我可以做我想的吗?我希望有一个我可以在程序中的任何地方调用的函数,它将在每次调用时为我提供不同的变量名或整数。
答案 0 :(得分:6)
要实际使用Supply
monad,您应该使用do
notation构建代码,并在实际需要名称时调用supply
函数。
例如,这将生成一个以var_
为前缀的新变量名称,只是为了说明如何从供应中获取并使用它:
newVar :: Supply [Char] [Char]
newVar = do
name <- supply
return ("var"_++name)
您需要围绕Supply
monad构建整个程序,然后只在顶层调用runSupplyVars
一次,否则程序的不同部分将具有独立的供应,因此可能会重复使用相同的变量名称。
最后,您需要Control.Monad.Identity
中的runIdentity
将runSupplyVars
的结果解压缩到(a, [[Char]])
类型的基础元组中,然后丢弃第二个值只是未使用名称的(无限)列表。您可能最好重新定义runSupplyVars
为您执行此操作:
import Control.Monad.Identity
[...]
runSupplyVars :: Supply [Char] a -> a
runSupplyVars x = fst (runIdentity (runSupply x vars))
where vars = [replicate k ['a'..'z'] | k <- [1..]] >>= sequence
这是一个更完整的例子,将它们放在一起。请注意使用do
符号的不同monad - IO
函数main
和Supply [Char]
realProgram
以及其他大部分代码更大的版本:
import MonadSupply
import Control.Monad.Identity
main :: IO ()
main = do
let result = runSupplyVars realProgram
print result
realProgram :: Supply [Char] Int
realProgram = do
x <- newVar
return 0
newVar :: Supply [Char] [Char]
newVar = do
name <- supply
return ("var_"++name)
runSupplyVars :: Supply [Char] a -> a
runSupplyVars x = fst (runIdentity (runSupply x vars))
where vars = [replicate k ['a'..'z'] | k <- [1..]] >>= sequence