haskell - textos - ¿Cómo cambiar el orden de los argumentos?
texto argumentativo partes (2)
¿Qué pasa si quiero cambiar el orden de los argumentos en una función?
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?
La mejor manera en general es simplemente hacerlo manualmente. Asume que tienes una función
f :: Arg1 -> Arg2 -> Arg3 -> Arg4 -> Res
y te gustaria
g :: Arg4 -> Arg1 -> Arg3 -> Arg2 -> Res
entonces escribes
g x4 x1 x3 x2 = f x1 x2 x3 x4
Si necesita una permutación particular varias veces, entonces, por supuesto, puede abstraerse de ella, como lo hace flip
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
Si tienes ganas de editar las funciones después de que estén escritas, realmente deberías leer los excelentes combinadores de editor semántico de Conal Elliott.
http://conal.net/blog/posts/semantic-editor-combinators
De hecho, todos deberían leerlo de todos modos. Es un método realmente útil (que estoy abusando aquí). Conal usa más construcciones que solo result
y flip
a un efecto muy flexible.
result :: (b -> b'') -> ((a -> b) -> (a -> b''))
result = (.)
Supongamos que tengo una función que usa 3 argumentos
use3 :: Char -> Double -> Int -> String
use3 c d i = c: show (d^i)
y me gustaría intercambiar los dos primeros, solo uso flip use3
como usted dice, pero si quisiera intercambiar el segundo y el tercero, lo que quiero es aplicar flip
al resultado de aplicar use3
a su primer argumento .
use3'' :: Char -> Int -> Double -> String
use3'' = (result) flip use3
Avancemos e intercambiemos los argumentos cuarto y quinto de una función use5
que usa 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 más tarde y definirlo?
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 debes parar si te gusta la simplicidad y la elegancia. Tenga en cuenta que el other
tipo podría ser b -> c -> d
por lo que debido a la fabulosa Curry y la 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, deberías escribir una función permutada a mano. Lo que sigue es solo por el bien de la curiosidad intelectual.
Ahora, ¿qué pasa con el intercambio de los argumentos segundo y cuarto? [Aparte: hay un teorema que recuerdo de mis lecciones de álgebra que cualquier permutación se puede hacer como la composición de intercambiar elementos adyacentes.]
Podríamos hacerlo así: paso 1: mueve 2 junto a 4 ( swap_2_3
)
a1 -> a2 -> a3 -> a4 -> otherstuff
a1 -> a3 -> a2 -> a4 -> otherstuff
intercambialas usando swap_3_4
a1 -> a3 -> a2 -> a4 -> otherstuff
a1 -> a3 -> a4 -> a2 -> otherstuff
luego intercambie 4 de nuevo a la posición 2 usando swap_2_3
nuevo:
a1 -> a3 -> a4 -> a2 -> otherstuff
a1 -> a4 -> a3 -> a2 -> otherstuff
asi que
swap_2_4 = swap_2_3.swap_3_4.swap_2_3
Tal vez haya una manera más concisa de llegar directamente con muchos resultados y giros, ¡pero el juego aleatorio no lo encontré para mí!
De manera similar, para intercambiar 1 y 5 podemos mover 1 sobre 4, intercambiar con 5, mover 5 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 prefieres, puedes reutilizar swap_2_4
volteando en los extremos (intercambiando 1 con 2 y 5 con 4), swap_2_4 y luego volteando de nuevo 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 de definir.
swap_1_5'''' f a b c d e = f e b c d a
que tiene la ventaja de ser claro, coherente, eficiente y tiene una firma de tipo útil en ghci sin anotarlo explícitamente.
Sin embargo, esta fue una pregunta fantásticamente entretenida, gracias.