2010-02-01 21 views
7

que estaba leyendo este artículo por Tomas Petricek, y el mencionado canalización |> como en el ejemplo dado:En F #, ¿qué significa "pipelining"?

 
> let nums = [1; 2; 3; 4; 5];; 
val nums : list<int> 

> let odds_plus_ten = 
     nums 
     |> List.filter (fun n-> n%2 <> 0) 
     |> List.map (add 10) 
val odds_plus_ten : list<int> = [11; 13; 15];; 

¿Qué significa la canalización? Inicialmente, pensé que era similar a una instrucción de CPU canalizada dentro de los núcleos. ¿Puedes explicar de qué se trata y cómo funciona en el contexto de F #?

Gracias, Saludos cordiales, Tom.

+2

Piense en lo más parecido a la tubería en PowerShell o conchas tradicionales (aunque, teniendo en cuenta que los objetos se pasan en realidad es más parecido a PowerShell). No tiene nada que ver directamente con el pipeline de la CPU :-) – Joey

+0

@Johannes: quieres decir algo como esto ... cat myfile | más, la entrada de uno se pasa a la salida para ser procesada por más? – t0mm13b

+0

Exactamente. Aunque, como se dijo, la analogía con el shell de UNIX es bastante deficiente para esto. – Joey

Respuesta

11

Pipelining significa pasar los resultados de una función a otra función. En el ejemplo que le da a "nums" se pasa el List.Filter, los resultados filtrados se pasan luego a List.Map.

Más información aquí: http://msdn.microsoft.com/en-us/magazine/cc164244.aspx#S6

+0

@Steve: gracias por el enlace, analizaremos eso, una pregunta rápida: ¿cómo sabe la canalización cuándo dejar de pasar la información de una a otra? :) Gracias. – t0mm13b

+0

@Steve: ¡Es un buen vínculo! ¡Haga esto marcado una vez que termine el artículo de Tomas Petricek! Me está friendo el cerebro en este momento con el caparazón interactivo F # aquí en mi extremo ... – t0mm13b

+0

Disculpa, solo estoy llegando a F # también, así que no estoy seguro de poder responder a tu pregunta de seguimiento. No creo que pueda "detener" la canalización, creo que todos los resultados de la primera función pasarán a la siguiente. –

13

En cierto modo, no hay nada especial acerca de la canalización; en lugar de escribir f (g (h x)), puede escribir x |> h |> g |> f, lo que no parece una mejora obvia. Sin embargo, hay dos puntos que vale la pena teniendo en cuenta:

  1. veces el orden de lectura es mejor para la versión pipeline: "Tome x y enviarlo a h, enviar el resultado al g, enviar el resultado al f" puede ser más fácil de entender que "Aplicar f al resultado de aplicar g al resultado de aplicar h a x".
  2. La inferencia de tipo a menudo funciona mucho mejor para la versión interconectada. Esta es probablemente la razón más importante por la que la canalización se usa tanto en F #. Como la inferencia de tipo procede de izquierda a derecha, x |> Array.map (fun s -> s.Length) funcionará cuando x sea string[], pero Array.map (fun s -> s.Length) x no; necesita hacer Array.map (fun (s:string) -> s.Length) x en su lugar.
2

Consulte Pipelining in F# para obtener una explicación.

(Si está familiarizado con la línea de comandos UNIX y tuberías como

cat file1 | sort | head 

es una idea similar;. El resultado de la expresión anterior se convierte en el argumento de la función siguiente)

3

Como otros mencionaron, la canalización es más como una tubería de shell de UNIX. Le permite escribir una entrada seguida de las operaciones que se le deben aplicar, en lugar de las llamadas a funciones anidadas habituales. En este ejemplo, el F # código estándar se vería así:

let r = List.map (add 10) (List.filter (fun n-> n%2 <> 0) nums) 

Tenga en cuenta que la entrada nums está profundamente anidado en la expresión y no es fácil ver que se filtra primero y luego proyecta. Al utilizar la canalización, puede escribir el código de manera diferente, pero significará exactamente lo mismo.

El truco es que el operador de canalización toma dos parámetros utilizando la notación infija (por ejemplo, x |> f). El parámetro x se pasará como último argumento a una función a la derecha (f). Puede utilizar la canalización con cualquier función F #:

let sinOne = 1.0 |> sin 

let add a b = a + b 
let r = 10 |> add 5 // it doesn't always make code more readable :-) 

Un punto importante sobre F # operador de la canalización es que no es ningún daño especial característica integrada de la lengua.Es un operador personalizado simple que se puede definir por su cuenta:

let (|>) x f = f x 

// Thanks to operator associativity rules, the following: 
let r = 1.0 |> sin |> sqrt 
// ...means this: 
let r = (1.0 |> sin) |> sqrt 
+2

Me sorprende que la definición de un operador tan poderoso y útil sea tan trivial. – Benjol