How to change the order of arguments?

前端 未结 2 1068
[愿得一人]
[愿得一人] 2020-12-24 02:28

What if I want to change the order of arguments in a function?

There is flip:

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


        
相关标签:
2条回答
  • 2020-12-24 03:11

    If you feel like editing functions after they're written, you really really should read Conal Elliott's excellent blog post semantic editor combinators

    http://conal.net/blog/posts/semantic-editor-combinators

    In fact, everyone should read it anyway. It's a genuinely useful method (which I'm abusing here). Conal uses more constructs than just result and flip to very flexible effect.

    result :: (b -> b') -> ((a -> b) -> (a -> b'))
    result =  (.)
    

    Suppose I have a function that uses 3 arguments

    use3 :: Char -> Double -> Int -> String
    use3 c d i = c: show (d^i)
    

    and I'd like to swap the first two, I'd just use flip use3 as you say, but if I wanted to swap the seconnd and third, what I want is to apply flip to the result of applying use3 to its first argument.

    use3' :: Char -> Int -> Double -> String
    use3' = (result) flip use3
    

    Let's move along and swap the fourth and fifth arguments of a function use5 that uses 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
    

    We need to apply flip to the result of applying use5 to it's first three arguments, so that's the result of the result of the result:

    use5' = (result.result.result) flip use5
    

    Why not save thinking later and define

    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
    

    ...and that's where you should stop if you like simplicity and elegance. Note that the type other could be b -> c -> d so because of fabulous Curry and right associativity of ->, swap_2_3 works for a function which takes any number of arguments above two. For anything more complicated, you should really write a permuted function by hand. What follows is just for the sake of intellectual curiosity.

    Now, what about swapping the second and fourth arguments? [Aside: there's a theorem I remember from my algebra lectures that any permutation can be made as the composition of swapping adjacent items.]

    We could do it like this: step 1: move 2 next to 4 (swap_2_3)

    a1 -> a2 -> a3 -> a4 -> otherstuff
    a1 -> a3 -> a2 -> a4 -> otherstuff
    

    swap them there using swap_3_4

    a1 -> a3 -> a2 -> a4 -> otherstuff
    a1 -> a3 -> a4 -> a2 -> otherstuff
    

    then swap 4 back to position 2 using swap_2_3 again:

    a1 -> a3 -> a4 -> a2 -> otherstuff
    a1 -> a4 -> a3 -> a2 -> otherstuff
    

    so

    swap_2_4 = swap_2_3.swap_3_4.swap_2_3
    

    Maybe there's a more terse way of getting there directly with lots of results and flips but random messing didn't find it for me!

    Similarly, to swap 1 and 5 we can move 1 over to 4, swap with 5, move 5 back from 4 to 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
    

    Or if you prefer you could reuse swap_2_4 by flipping at the ends (swapping 1 with 2 and 5 with 4), swap_2_4 then flipping at the ends again.

    swap_1_5' = swap_1_2.swap_4_5. swap_2_4 .swap_4_5.swap_1_2
    

    Of course it's much easier to define

    swap_1_5'' f  a b c d e = f  e b c d a
    

    which has the benefit of being clear, consise, efficient and has a helpful type signature in ghci without explicitly annotating it.

    However, this was a fantastically entertaining question, thanks.

    0 讨论(0)
  • 2020-12-24 03:23

    The best way in general is to just do it manually. Assume you have a function

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

    and you would like

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

    then you write

    g x4 x1 x3 x2 = f x1 x2 x3 x4
    

    If you need a particular permutation several times, then you can of course abstract from it, like flip does for the two-argument case:

    myflip :: (a4 -> a1 -> a3 -> a2 -> r) -> a1 -> a2 -> a3 -> a4 -> r
    myflip f x4 x1 x3 x2 = f x1 x2 x3 x4
    
    0 讨论(0)
提交回复
热议问题