2012-08-26 33 views
18

¿Qué ocurre si quiero cambiar el orden de los argumentos en una función?¿Cómo cambiar el orden de los argumentos?

Hay flip:

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

pero no veo cómo hacer que funcione para un mayor número de argumentos. ¿Hay un método general para permutar los argumentos?

+0

@phimuemue puede que no sea una función, sino macros TH. Sí, se puede escribir dicha macro, pero la lambda simple es casi tan corta. – permeakra

+5

Si tiende a tener muchos argumentos, puede perder oportunidades de crear tipos de datos apropiados. O solo eres una persona quejumbrosa. – Landei

Respuesta

12

La mejor manera en general es simplemente hacerlo manualmente. Suponga que tiene una función

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

y que le gustaría

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

entonces escribir

g x4 x1 x3 x2 = f x1 x2 x3 x4 

Si necesita una permutación en particular varias veces, entonces por supuesto puede abstraer de ella , como flip hace para el caso de dos argumentos:

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

Creo que esta es la mejor manera de hacerlo, si necesita una reorganización especializada de argumentos, es más fácil definir su propia función para hacerlo, pero sospecho que cualquiera de estos se puede generar con composiciones de flip, y lambdas like (\ x -> flip $ fx) que aplica parcialmente la función para voltear argumentos que no sean el primero y el segundo. –

32

Si usted se siente como funciones de edición después de ser escritos, que realmente realmente debe leer el excelente blog de Conal Elliott combinadores editor semánticas

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

De hecho, todo el mundo debería leer todos modos. Es un método genuinamente útil (que estoy abusando aquí). Conal utiliza más constructos que solo result y flip con un efecto muy flexible.

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

Supongamos que tengo una función que utiliza 3 argumentos

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

y me gustaría intercambiar los dos primeros, que acababa de uso flip use3 como usted dice, pero si quería intercambie el segundo y tercero, lo que quiero es aplicar flip al resultado de aplicar use3 a su primer argumento. movimiento

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

Vamos a lo largo y cambiar los argumentos cuarto y quinto de una función use5 que utiliza 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 

Necesitamos aplicar flip al resultado de aplicar use5 a sus primeros tres argumentos, así que ese es el resultado del resultado del resultado:

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

¿Por qué no guardar el pensamiento después? y 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 

... y ahí es donde deberías parar si te gusta la simplicidad y la elegancia. Tenga en cuenta que el tipo other podría ser b -> c -> d por lo que debido al fabuloso Curry y asociatividad correcta de ->, swap_2_3 funciona para una función que toma cualquier número de argumentos por encima de dos. Para algo más complicado, realmente debe escribir una función permutada a mano. Lo que sigue es solo por el bien de la curiosidad intelectual.

Ahora, ¿qué hay de intercambiar los argumentos segundo y cuarto? [Aparte: hay un teorema que recuerdo de mis conferencias de la álgebra que cualquier permutación se puede hacer como la composición de intercambio de elementos adyacentes.]

podríamos hacerlo así: paso 1: mover 2 junto a 4 (swap_2_3)

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

ellos intercambie allí utilizando swap_3_4

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

entonces intercambiar 4 de vuelta a la posición 2 usando swap_2_3 nuevo:

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

por lo

swap_2_4 = swap_2_3.swap_3_4.swap_2_3 

Tal vez hay una manera más concisa de llegar allí directamente con un montón de resultados y voltea Y jugando al azar, no lo encontró para mí!

Del mismo modo, para intercambiar 1 y 5 se puede mover 1 a 4, intercambio con 5, 5 mover hacia atrás de 4 a 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 

O si lo prefiere, puede volver a utilizar swap_2_4 moviendo de un tirón en el termina (intercambiando 1 con 2 y 5 con 4), swap_2_4 y volteando nuevamente en los extremos.

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

Por supuesto que es mucho más fácil definir

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

que tiene la ventaja de ser claro, conciso, eficiente y tiene una firma de tipo útiles en ghci sin anotar explícitamente él.

Sin embargo, esta fue una pregunta fantásticamente entretenida, gracias.

Cuestiones relacionadas