Là người mới bắt đầu với SYB, câu trả lời của tôi giống như đoán, nhưng có vẻ như hiệu quả.
Bộ kết hợp somewhere
được đề xuất bởi Neil Brown có thể không thực hiện chính xác những gì bạn muốn. Đó là defined như
-- | 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
nơi
-- | 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
Nhưng chúng ta cần phải chuyển đổi tối đa một lần. Đối với điều này có vẻ như gmapMo
sẽ tốt hơn:
-- | Transformation of one immediate subterm with success
gmapMo :: forall m. MonadPlus m => (forall d. Data d => d -> m d) -> a -> m a
Vì vậy, tôi đã combinator của riêng tôi:
{-# 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
Nếu sự thay thế không, nó sẽ trả mzero
, nếu không nó sẽ trả về kết quả thay thế. Nếu bạn không quan tâm nếu thay không (không có trận đấu), bạn có thể sử dụng một cái gì đó giống như
once' :: (forall a. Data a => a -> Maybe a) -> (forall a. Data a => a -> a)
once' f x = fromMaybe x (once f x)
Với những, chúng ta có thể làm một số thay thế:
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])
Giải pháp tuyệt vời! Chính xác những gì tôi đang tìm kiếm. Cảm ơn rất nhiều! – user1546806
Để làm cho công việc này trên mã của tôi, tôi đã phải viết lại một lần là 'một lần f x = f x \ 'mplus \' gmapMo (một lần f) x'. – user1546806
@ user1546806 Vâng, xin lỗi, đó là một sai lầm ngớ ngẩn. Tôi sẽ sửa câu trả lời. –