2013-06-04 1 views
5

SYB를 사용하여 everywhere 대신 한 번만 트리에 변환을 적용하는 가장 좋은 방법은 무엇입니까? 예를 들어, 다음과 같은 간단한 표현식에서 Var "x"의 인스턴스가 여러 개 있으며 첫 번째 인스턴스를 Var "y"으로 바꾸고 싶습니다. Haskell의 SYB (Borap Plate Your Scrap Your Boilerplate) - 어디서나 대신 한 번만 변환을 적용합니다.

data Exp = Var String | Val Int | Plus Exp Exp |...

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

Var "y"Var "x"의 모든 인스턴스를 변환하려고합니다 이후 everywhere 콤비를 사용하여 수행 할 수 없습니다.

EDIT (게시 후) : somewhere은 내가 찾고있는 것 같습니다.

답변

3

SYB 초보자이기 때문에 내 대답은 추측과 비슷하지만 작동하는 것처럼 보입니다.

결합 자 somewhere 닐 브라운 (Neil Brown)이 권장하는 것은 아마도 사용자가 원하는대로 할 수 없습니다. 우리가 에서 가장 번을 변환 할 필요가 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]) 
+0

우수한 솔루션! 정확히 내가 무엇을 찾고 있었는지. 대단히 감사합니다! – user1546806

+0

내 코드에서이 작업을 수행하려면'once f x = f x \'mplus \'gmapMo (once f) x'로 다시 작성해야했습니다. – user1546806

+0

@ user1546806 예, 죄송합니다. 그건 바보 같은 실수였습니다. 나는 답을 바로 잡을 것이다. –

2

예, MonadPlus 모나드를 사용하고 찾고있는 것을 찾으면 성공 하리라 생각합니다. somewhere (mkMp mySpecificFunction)이 성공해야한다고 생각합니다. 그런 식으로, 작업이 완료 -

유연하지만 해키 대안은 Boolean (또는 Maybe MyFunc를 저장하거나 무엇이든) 저장할 수있는 상태 모나드와 everywhereM를 사용하고 True 또는 Just myFunc되는 상태에 따라 변환을 적용하는 것입니다 (예 : 변환을 한 번 적용한 후) 상태를 False/Nothing으로 변경하면됩니다.

+0

감사합니다, @NeilBrown을. 첫 번째 접근법에 대해 좀 더 자세히 설명해 주시겠습니까? 나는이 [library] (http://web.engr.oregonstate.edu/~erwig/reclib/)를 발견했으며, MonadPlus를 사용하여 일회성 변환을 지정하지만'somewhere'는 사용하지 않습니다. 두 번째 방법은 효과가 있지만 그 경로를 사용하고 싶지는 않습니다. – user1546806

관련 문제