2015-10-24 2 views
4

Рассмотрите следующую ситуацию. Я определяю функцию для обработки списка элементов типичным способом выполнения операции над головой и вызова функции над остальной частью списка. Но при определенном условии элемента (будучи отрицательным, являясь особым символом, ...), прежде чем продолжить, я меняю знак на остальную часть списка. Как это:Можно ли определить шаблоны композиции в функциях или функторах?

f [] = [] 
f (x : xs) 
    | x >= 0  = g x : f xs 
    | otherwise = h x : f (opposite xs) 

opposite [] = [] 
opposite (y : ys) = negate y : opposite ys 

opposite (opposite xs) = xs Как я стал к ситуации избыточных противоположных операций, накапливая opposite . opposite . opposite ....

Это происходит с другими операциями, а не с opposite, любое такое, что композиция с самим собой идентична, например reverse.

Возможно ли преодолеть эту ситуацию, используя функторы/монады/аппликативы/стрелки? (Я не очень хорошо понимаю эти концепции). То, что я хотел бы, чтобы иметь возможность определения собственности, или образец композиции, как это:

opposite . opposite = id -- or, opposite (opposite y) = y 

для того, чтобы компилятор или интерпретатор избегает вычислить обратное обратное (можно и просто (родной) в некоторых конкатенативных языках).

ответ

5

Вы можете решить эту проблему без каких-либо монады, поскольку логика довольно проста:

f g h = go False where 
    go _ [] = [] 
    go b (x':xs) 
    | x >= 0 = g x : go b xs 
    | otherwise = h x : go (not b) xs 
     where x = (if b then negate else id) x' 

Тело функции go практически идентична исходной f функции. Единственное отличие состоит в том, что go решает, должен ли элемент быть отброшен или не основан на логическом значении, переданном ему из предыдущих вызовов.

+2

Звучит как 'картаAccumL'. – user3237465

+2

@ user3237465 ... и 'mapAccumL' является' mapM' для монадии 'State'. И, pow! Мы вернулись в монады. Вопрос только в том, на каком уровне вы абстрагируетесь. «Без монад» может быть хорошо, а может и нет - многое зависит от ваших целей. –

+0

@ Даниэль Вагнер, я думал, что версия с 'mapAccumL' будет проще, но я был [неправильно] (http://ideone.com/TmGEx5). Монадическая версия более описательна. – user3237465

5

Несомненно, просто сохраняйте состояние, указывающее, следует ли применять negate к текущему элементу или нет. Таким образом:

f = mapM $ \x_ -> do 
    x <- gets (\b -> if b then x_ else negate x_) 
    if x >= 0 
     then return (g x) 
     else modify not >> return (h x)