2014-09-29 3 views
3

У меня есть кортеж значений, представляющих некоторое состояние, и вы хотите перевести его добавлением (сдвигом). Мои ценности являются более длинная версия (Int, [Int], Int), и я хочу что-то концептуально (но не в буквальном смысле), как это:Haskell - применить кортеж функций к кортежу значений?

shift n = ??? (+n) (id, map, id)  -- simple(?) 

, который был бы эквивалентен:

shift n (a, b, c) = (a+n, map (+n) b, c+n) 

Я рад просто пойти с этим явным использованием функции, но задавался вопросом, что существует более идиоматическая бесконтактная версия, использующая аппликативные или стрелки или ..., или если они просто заканчивают запутывание вещей. Я думал, что в бесконтактной версии более четко показана основная структура операции.

ответ

4

С расширением языка DeriveFunctor вы можете написать

data MyState a = MyState a [a] a 
    deriving (Functor) 

Производный экземпляр выглядит

instance Functor MyState where 
    fmap f (MyState a bs c) = MyState (f a) (map f bs) (f c) 

Теперь вы можете определить

shift :: MyState Int -> MyState Int 
shift n = fmap (+n) 

(Вы говорите, что ваш кортеж даже больше, чем (Int, [Int], Int), так что вы можете определить ваше состояние типа с использованием запись синтаксис.)

+0

Thanks; Я знаю, что могу это сделать, и, возможно, это «лучшее» решение, но если для этого нет текущих операторов, так лучше всего написать эту функцию. Я думал о чем-то вроде обобщения ***. – guthrie

1

Применение список функций в списке значений просто

zipWith ($) 

Поскольку кортежи разных размеров, каждый их собственный тип, вам придется написать функцию явно для 3-кортежей , Общая функция применить 3-кортеж функций к 3-кортеж значений

apply (f, g, h) (a, b, c) = (f a, g b, h c) 

Вы должны явно записать каждую функцию приложения, потому что кортежи не имеют рекурсивное свойство списков.

5

Вы можете просто написать

shift n (a,b,c) = (a+n, map (+n) b, c+n) 

Или определить новые комбинаторы подобные Эрроу (***) и (&&&),

prod3 (f,g,h) (a,b,c) = (f a, g b, h c)  -- cf. (***) 
call3 (f,g,h) x  = (f x, g x, h x)  -- cf. (&&&) 
ap3 f  (a,b,c) = (f a, f b, f c) 
uncurry3 f (a,b,c) = f a b c 

, а затем вызвать

prod3 ((+n), map (+n), (+n)) (a,b,c) 

или даже (с соответствующим установленным оператором приоритеты)

ap3 ($ (+n)) (id, map, id) `prod3` (a,b,c) 

Или, если вы хотите написать ваши тройки как вложенные пары, вы можете использовать

Prelude Control.Arrow> ((+5) *** map (+5) *** (+5)) (1,([2,3],4)) 
(6,([7,8],9)) 

Таким образом, для вложенных пар,

shift' :: (Num b) => b -> (b, ([b], b)) -> (b, ([b], b)) 
shift' n = ((+n) *** map (+n) *** (+n)) 

(смотрите также Multiple folds in one pass using generic tuple function)

+0

Thanks; Вложенные пары - хорошая идея, и я попробовал это, но с более длинным состоянием (четыре элемента) это грязно, и не очень-то лучше для этой ситуации. Мне было интересно узнать об обобщении ***, но это похоже на нет. – guthrie

+0

Когда я пробовал этот подход: ((+5) *** map (+5) *** (+5)) Я задавался вопросом, есть ли простой способ реакторизации обычных операций (+ n) из определения (см. исходный вопрос). – guthrie

+0

well, '(f *** (f *** f)) = (***) f ((***) ff) = (***) f (join (***) f) = ((***) <*> join (***)) f'. Для 4-х наборов '((a, b), (c, d))' -shaped будет '(join (***) <*> join (***)) f'. –

Смежные вопросы