f# - ¿Por qué funciona el operador de tubería?
(4)
Si el operador de tubería se crea así:
let (|>) f g = g f
Y usado así:
let result = [2;4;6] |> List.map (fun x -> x * x * x)
Entonces lo que parece hacer es tomar List.Map y ponerlo detrás (fun x -> x * x * x) Y no cambia nada sobre la posición de [2; 4; 6]
Entonces ahora se ve así:
let result2 = [2;4;6] (fun x -> x * x * x) List.map
Sin embargo, esto no funciona.
Solo estoy aprendiendo f # por primera vez ahora. Y esto me molestó mientras leía un libro sobre f #. Entonces podría aprender lo que me estoy perdiendo más tarde, pero decidí preguntar de todos modos.
Sin embargo, es obvio que me estoy perdiendo algo importante. Como puedo recrear fácilmente el operador de tubería. Pero no entiendo por qué funciona. Debo avergonzarme muy pronto a medida que aprendo más. Oh bien.
Como otros han dicho anteriormente, básicamente estás malinterpretando a qué se vería resuelto el resultado2. En realidad, se resolvería
List.map (fun x -> x * x * x) [2;4;6]
List.map toma dos argumentos: una función para aplicar a todos los elementos en una lista y una lista. (fun x -> x * x * x)
es el primer argumento y [2;4;6]
es el segundo.
Básicamente solo coloca lo que está a la izquierda de |>
después del final de lo que está a la derecha.
Los corchetes que rodean |>
significan que es un operador infijo para que pueda escribir su ejemplo
let result = (|>) [2;4;6] (List.map (fun x -> x * x * x))
Dado que |>
aplica su primer argumento al segundo, esto es equivalente a
let result = (List.map (fun x -> x * x)) [2;4;6]
Si ingresa su definición de |>
en fsi y observa la firma del operador derivada por inferencia de tipo, notará que val ( |> ) : ''a -> (''a -> ''b) -> ''b
, es decir, argumento ''a
dado para funcionar (''a -> ''b)
produce ''b
.
Ahora proyecta esta firma en tu expresión [2;4;6] |> List.map (fun x -> x * x * x)
y obtendrás List.map (fun x -> x * x * x) [2;4;6]
, donde el argumento es list [2;4;6]
y la función se aplica parcialmente en función de un argumento List.map (fun x -> x * x * x)
.
El operador de tuberías es simplemente azúcar sintáctica para llamadas a métodos encadenados. Es muy similar a cómo se expresan las expresiones linq en C #.
Explicación desde aquí :
Operador de tuberías delantero Me encanta este tipo. El operador de tubería Forward se define simplemente como:
let (|>) x f = f x
Y tiene una firma de tipo:
''a -> (''a -> ''b) -> ''b
Lo que se traduce en: dado un tipo genérico ''a, y una función que toma un'' a y devuelve un ''b, luego devuelve la aplicación de la función en la entrada.
En lugar de explicar esto, permítanme darles un ejemplo de dónde se puede usar:
// Take a number, square it, then convert it to a string, then reverse that string
let square x = x * x
let toStr (x : int) = x.ToString()
let rev (x : string) = new String(Array.rev (x.ToCharArray()))
// 512 -> 1024 -> "1024" -> "4201"
let result = rev (toStr (square 512))
El código es muy directo, pero fíjate qué tan rebelde se ve la sintaxis. Todo lo que queremos hacer es tomar el resultado de un cálculo y pasarlo al siguiente cálculo. Podríamos reescribirlo introduciendo una serie de nuevas variables:
let step1 = square 512
let step2 = toStr step1
let step3 = rev step2
let result = step3
Pero ahora necesita mantener todas esas variables temporales correctas. Lo que hace el operador (|>) es tomar un valor, y ''reenviarlo'' a una función, esencialmente permitiéndole especificar el parámetro de una función antes de la llamada a la función. Esto simplifica drásticamente el código F # al permitirle canalizar funciones juntas, donde el resultado de uno pasa al siguiente. Entonces, para usar el mismo ejemplo, el código se puede escribir claramente como:
let result = 512 |> square |> toStr |> rev
Editar :
En F #, lo que realmente está haciendo con una llamada a método es tomar una función y luego aplicarla al parámetro que sigue, por lo que en su ejemplo sería List.map (fun x -> x * x * x)
se aplica a [2;4;6]
. Todo lo que hace el operador de tubería es tomar los parámetros en orden inverso y luego hacer la aplicación invirtiéndolos.
función: List.map (fun x -> x * x * x)
: [2;4;6]
Sintaxis de llamada estándar F #: fg
Sintaxis de llamada F # invertida: gf
Estándar:
let var = List.map (fun x -> x * x * x) [2;4;6]
Invertido:
let var = [2;4;6] |> List.map (fun x -> x * x * x)