Подтвердить что ты не робот

Как изменить порядок аргументов?

Что делать, если я хочу изменить порядок аргументов в функции?

Существует flip:

flip :: (a -> b -> c) -> b -> a -> c

но я не вижу, как заставить его работать для большего количества аргументов. Есть ли общий метод для перестановки аргументов?

4b9b3361

Ответ 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 без явного аннотирования его.

Однако это был фантастически интересный вопрос, спасибо.

Ответ 2

Лучший способ в целом - просто сделать это вручную. Предположим, что у вас есть функция

f :: Arg1 -> Arg2 -> Arg3 -> Arg4 -> Res

и вы хотели бы

g :: Arg4 -> Arg1 -> Arg3 -> Arg2 -> Res

тогда вы пишете

g x4 x1 x3 x2 = f x1 x2 x3 x4

Если вам нужна определенная перестановка несколько раз, вы можете, конечно, отвлечься от нее, например, flip для случая с двумя аргументами:

myflip :: (a4 -> a1 -> a3 -> a2 -> r) -> a1 -> a2 -> a3 -> a4 -> r
myflip f x4 x1 x3 x2 = f x1 x2 x3 x4