2012-08-26 2 views
18

Et si je veux changer l'ordre des arguments dans une fonction?Comment changer l'ordre des arguments?

Il est flip:

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

mais je ne vois pas comment le faire fonctionner pour un plus grand nombre d'arguments. Existe-t-il une méthode générale pour permuter les arguments?

+0

@phimuemue il peut ne pas être une fonction, mais des macros TH. Oui, une telle macro peut être écrite, mais le lambda simple est presque aussi court. – permeakra

+5

Si vous avez tendance à avoir beaucoup d'arguments, vous risquez de manquer des opportunités de créer des types de données appropriés. Ou vous êtes juste une personne querelleuse. – Landei

Répondre

12

La meilleure façon en général est de le faire manuellement. Supposons que vous avez une fonction

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

et vous souhaitez

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

vous écrire

g x4 x1 x3 x2 = f x1 x2 x3 x4 

Si vous avez besoin d'une permutation particulière à plusieurs reprises, vous pouvez bien sûr abstraite de celui-ci , comme flip fait pour le cas de deux arguments:

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

Je pense que c'est la meilleure façon de le faire, si vous avez besoin d'un réarrangement spécialisé d'arguments, il est plus facile de définir votre propre fonction, mais je suppose que tout peut être généré avec des compositions de flip et lambda x -> flip $ fx) qui applique partiellement la fonction pour retourner les arguments autres que le premier et le second. –

32

Si vous vous sentez comme des fonctions d'édition après qu'ils sont écrits, vous devriez vraiment vraiment lire Conal Elliott excellent éditeur sémantique blog combinators

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

En fait, tout le monde devrait lire de toute façon. C'est une méthode vraiment utile (que j'abuse ici). Conal utilise plus de constructions que juste result et flip pour un effet très flexible.

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

Supposons que j'ai une fonction qui utilise 3 arguments

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

et je voudrais échanger les deux premiers, je venais d'utiliser flip use3 comme vous le dites, mais si je voulais échanger le deuxième et le troisième, ce que je veux est d'appliquer flip au résultat de l'application use3 à son premier argument.

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

déménagement Let le long et échanger les quatrième et cinquième arguments d'une fonction use5 qui utilise 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 

Nous devons appliquer flip au résultat de l'application use5 à ses trois premiers arguments, donc c'est le résultat du résultat du résultat:

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

Pourquoi ne pas penser plus tard et définir

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 

... et c'est là que vous devriez vous arrêter si vous aimez la simplicité et l'élégance. Notez que le type other pourrait être b -> c -> d donc en raison de fabuleux Curry et associativité droite de ->, swap_2_3 fonctionne pour une fonction qui prend n'importe quel nombre d'arguments au-dessus de deux. Pour quelque chose de plus compliqué, vous devriez vraiment écrire une fonction permutée à la main. Ce qui suit est juste pour la curiosité intellectuelle.

Maintenant, qu'en est-il de l'échange des deuxième et quatrième arguments? [En plus: il y a un théorème Je me souviens de mes cours d'algèbre que toute permutation peut être faite que la composition de la permutation des éléments adjacents.]

On pourrait le faire comme ceci: étape 1: déplacer 2 à côté de 4 (swap_2_3)

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

les échanger en utilisant là swap_3_4

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

puis échanger 4 en position 2 en utilisant à nouveau swap_2_3:

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

si

swap_2_4 = swap_2_3.swap_3_4.swap_2_3 

Peut-être qu'il ya une façon plus laconique d'y arriver directement avec beaucoup de résultats et flips mais Messing aléatoire n'a pas trouvé pour moi!

De même, pour échanger 1 et 5, nous pouvons déplacer 1 sur 4, swap avec 5, 5 déplacer en arrière de 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 

Ou si vous préférez vous pourriez réutiliser swap_2_4 en retournant à la termine (permutation 1 avec 2 et 5 avec 4), swap_2_4 puis retour aux extrémités.

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

Bien sûr, il est beaucoup plus facile de définir

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

qui a l'avantage d'être clair, concise, efficace et a une signature de type utile dans ghci sans explicitement annotant.

Cependant, c'était une question très amusante, merci.

Questions connexes