在The Essence of Compiling with Continuations Flanagan等人。描述了将术语转换为A-normal form的线性时间算法。简而言之,A-normal表单只允许所有应用程序使用琐碎的参数(例如变量),并且所有非平凡的术语都被绑定。
这是A-normal形式的一个(愚蠢的)例子:
let x0 = x0 in
let x2 = \ x1 -> x1 in
let x3 = 1 in
x2 x3
从这个非A-normal形式的术语创建:
let y = y in
(\ x0 -> x0) 1
我已将本文末尾给出的Scheme代码(第11页)翻译成下面的Haskell模块。但是,我介绍了monads给我一个新名称的来源,并保留重命名变量的映射。我的问题是,是否使用monads破坏了论文中算法的O(n)运行时间?我特别担心>>=
的嵌套,这对于一些monad会导致O(n ^ 2)行为。
-- Based on http://slang.soe.ucsc.edu/cormac/papers/pldi93.pdf
import Control.Applicative
import Control.Monad.State.Strict
import Control.Monad.Reader
import qualified Data.Map.Strict as M
type Name = String
data Term = Var Name
| Lam Name Term
| App Term Term
| Let Name Term Term
| Lit Int
deriving Show
type NameSupply a = ReaderT (M.Map Name Name) (State [Name]) a
fresh :: NameSupply String
fresh = do
(name:rest) <- get
put rest
return name
normalizeTerm :: Term -> NameSupply Term
normalizeTerm m = normalize m return
normalize :: Term -> (Term -> NameSupply Term) -> NameSupply Term
normalize m k = case m of
Var x -> do
mx' <- asks (M.lookup x)
case mx' of
Just x' -> k (Var x')
Nothing -> error $ "var not found: " ++ x
Lam x body -> do
x' <- fresh
k =<< (Lam x' <$> local (M.insert x x') (normalizeTerm body))
Let x m1 m2 -> do
x' <- fresh
local (M.insert x x') $ normalize m1 (\ n1 -> Let x' n1 <$> (normalize m2 k))
App m1 m2 -> normalizeName m1 (\ n1 -> normalizeName m2 (k . App n1))
(Lit _) -> k m
normalizeName :: Term -> (Term -> NameSupply Term) -> NameSupply Term
normalizeName m k = normalize m $ \ n -> do
x <- fresh
Let x n <$> k (Var x)
run :: Term -> Term
run = flip evalState names . flip runReaderT M.empty . normalizeTerm
where names = (map (("x" ++) . show) [0..])
example :: Term
example = Let "y" (Var "y") (App (Lam "x0" (Var "x0")) (Lit 1))
答案 0 :(得分:2)
你的monad只有ReaderT ... (State ...)
,ReaderT
和State
都会传递单个值,因此没有理由期望线性时间(>>=)
或O( n)链取O(n ^ 2)时间。
当monad使用列表或集合或类似时,你可以得到左嵌套的二次爆破。