Ответ 1
Если вы чувствуете, что редактируете функции после их написания, вы действительно должны читать Коналл Эллиотт превосходные комментеры для текстовых редакторов блога.
http://conal.net/blog/posts/semantic-editor-combinators
На самом деле, все должны прочитать его в любом случае. Это действительно полезно
метод (который я злоупотребляю здесь). Conal использует больше конструкций, чем просто result
и flip
, для очень гибкого эффекта.
result :: (b -> b') -> ((a -> b) -> (a -> b'))
result = (.)
Предположим, что у меня есть функция, которая использует 3 аргумента
use3 :: Char -> Double -> Int -> String
use3 c d i = c: show (d^i)
и я бы хотел обменять первые два, я просто использовал бы flip use3
, как вы говорите,
но если бы я захотел поменять второй и третий, то я хочу применить flip
к результату применения use3
к его первому аргументу.
use3' :: Char -> Int -> Double -> String
use3' = (result) flip use3
Пусть перемещаются и заменяют четвертый и пятый аргументы функции use5
, которая использует 5.
use5 :: Char -> Double -> Int -> (Int,Char) -> String -> String
use5' :: Char -> Double -> Int -> String -> (Int,Char) -> String
use5 c d i (n,c') s = c : show (d ^ i) ++ replicate n c' ++ s
Нам нужно применить flip
к результату применения use5
к нему первые три аргумента,
так что результат результата результата:
use5' = (result.result.result) flip use5
Почему бы не сохранить мышление позже и определить
swap_1_2 :: (a1 -> a2 -> other) -> (a2 -> a1 -> other)
swap_2_3 :: (a1 -> a2 -> a3 -> other) -> (a1 -> a3 -> a2 -> other)
--skip a few type signatures and daydream about scrap-your-boilerplate and Template Haskell
swap_1_2 = flip
swap_2_3 = result flip
swap_3_4 = (result.result) flip
swap_4_5 = (result.result.result) flip
swap_5_6 = (result.result.result.result) flip
... и то, где вы должны остановиться, если вам нравится простота и элегантность.
Обратите внимание, что тип other
может быть b -> c -> d
, поэтому из-за сказочной карри и правой ассоциативности ->
,
swap_2_3 работает для функции, которая принимает любое количество аргументов выше двух.
Для чего-то более сложного, вы должны действительно написать перестановочную функцию вручную.
Далее следует только ради интеллектуального любопытства.
Теперь, как насчет замены второго и четвертого аргументов? [Кроме того, есть теорема, которую я помню из своих лекций по алгебре что любая перестановка может быть сделана в качестве композиции для переключения соседних элементов.]
Мы могли бы сделать это вот так:
Шаг 1: переместите 2 рядом с 4 (swap_2_3
)
a1 -> a2 -> a3 -> a4 -> otherstuff
a1 -> a3 -> a2 -> a4 -> otherstuff
замените их там, используя swap_3_4
a1 -> a3 -> a2 -> a4 -> otherstuff
a1 -> a3 -> a4 -> a2 -> otherstuff
затем поменяйте 4 назад на позицию 2 с помощью swap_2_3
снова:
a1 -> a3 -> a4 -> a2 -> otherstuff
a1 -> a4 -> a3 -> a2 -> otherstuff
так
swap_2_4 = swap_2_3.swap_3_4.swap_2_3
Может быть, есть более тонкий способ попасть туда с большим количеством результатов и переворачивает, но случайный беспорядок не нашел его для меня!
Аналогично, чтобы поменять 1 и 5, мы можем переместить 1 на 4, поменять местами с 5, переместить 5 назад с 4 на 1.
swap_1_5 = swap_1_2.swap_2_3.swap_3_4 . swap_4_5 . swap_3_4.swap_2_3.swap_1_2
Или, если вы предпочитаете, вы можете повторно использовать swap_2_4
, щелкнув по концам
(обмен 1 с 2 и 5 с 4), swap_2_4, затем снова перевернутый на концах.
swap_1_5' = swap_1_2.swap_4_5. swap_2_4 .swap_4_5.swap_1_2
Конечно, гораздо проще определить
swap_1_5'' f a b c d e = f e b c d a
который имеет преимущество быть ясным, consise, эффективным и имеет полезную подпись типа в ghci без явного аннотирования его.
Однако это был фантастически интересный вопрос, спасибо.