gpt4 book ai didi

Haskell 的 Scrap Your Boilerplate (SYB) - 仅应用一次转换,而不是到处应用转换

转载 作者:行者123 更新时间:2023-12-02 17:22:20 25 4
gpt4 key购买 nike

使用 SYB 对树仅应用一次转换(而不是各处)的最佳方法是什么?例如,在下面的简化表达式中,有多个 Var "x" 实例,我只想用 Var "y" 替换第一个实例。


数据 Exp = 变量字符串
|瓦尔国际
|加Exp Exp
|...



myExp = Val 5 `Plus` Var "x"`Plus` Val 5 `Plus` Var "x"...

这不能使用 everywhere 组合器来完成,因为它会尝试将 Var "x" 的所有实例转换为 Var "y".

编辑(发布后):看起来某处就是我正在寻找的。

最佳答案

作为一名 SYB 初学者,我的答案更像是一个猜测,但似乎可行。

Neil Brown 推荐的

Combinator somewhere 可能并不完全符合您的要求。这是defined作为

-- | Apply a monadic transformation at least somewhere
somewhere :: MonadPlus m => GenericM m -> GenericM m

-- We try "f" in top-down manner, but descent into "x" when we fail
-- at the root of the term. The transformation fails if "f" fails
-- everywhere, say succeeds nowhere.
--
somewhere f x = f x `mplus` gmapMp (somewhere f) x

哪里

-- | Transformation of at least one immediate subterm does not fail 
gmapMp :: forall m. MonadPlus m => (forall d. Data d => d -> m d) -> a -> m a

但是我们最多需要转换一次。对于这一点,gmapMo 似乎会更好:

-- | Transformation of one immediate subterm with success 
gmapMo :: forall m. MonadPlus m => (forall d. Data d => d -> m d) -> a -> m a

所以我制作了自己的组合器:

{-# LANGUAGE DeriveDataTypeable, RankNTypes #-}
import Control.Monad
import Data.Maybe (fromMaybe)
import Data.Data
import Data.Typeable (Typeable)
import Data.Generics.Schemes
import Data.Generics.Aliases

-- | Apply a monadic transformation once.
once :: MonadPlus m => GenericM m -> GenericM m
once f x = f x `mplus` gmapMo (once f) x

如果替换失败,则返回mzero,否则返回替换结果。如果您不关心替换是否失败(没有匹配项),您可以使用类似

once' :: (forall a. Data a => a -> Maybe a) -> (forall a. Data a => a -> a)
once' f x = fromMaybe x (once f x)

有了这些,我们可以做一些替换:

data Exp = Var String | Val Int | Plus Exp Exp
deriving (Show, Typeable, Data)

myExp = Val 5 `Plus` Var "x" `Plus` Val 5 `Plus` Var "x"

replM :: (MonadPlus m) => Exp -> m Exp
replM (Var "x") = return $ Var "y"
replM t = mzero

main = do
-- `somewhere` doesn't do what we want:
print $ (somewhere (mkMp replM) myExp :: Maybe Exp)

-- returns `Just ..` if the substitution succeeds once,
-- Nothing otherwise.
print $ (once (mkMp replM) myExp :: Maybe Exp)
-- performs the substitution once, if possible.
print $ (once' (mkMp replM) myExp :: Exp)

-- Just for kicks, this returns all possible substitutions
-- where one `Var "x"` is replaced by `Var "y"`.
print $ (once (mkMp replM) myExp :: [Exp])

关于Haskell 的 Scrap Your Boilerplate (SYB) - 仅应用一次转换,而不是到处应用转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16910517/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com