2010-02-01 3 views
7

Я читал эту статью Томаса Petricek, и он упомянул конвейерную |>, как в приведенном примере:В F #, что означает конвейеризация?

 
> 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];; 

Что конвейерная значит? Первоначально я думал, что это похоже на инструкцию процессора, конвейеризованную внутри ядер. Можете ли вы объяснить, что это такое и как это работает в контексте F #?

Thanks, С уважением, Tom.

+2

думать о нем, как трубопровод в PowerShell или традиционных оболочек (хотя, учитывая, что объекты передаются это действительно больше похоже на PowerShell). Он не имеет ничего общего с конвейером CPU :-) – Joey

+0

@Johannes: вы имеете в виду что-то вроде этого ... cat myfile | больше, вход от одного передается на вывод, который будет обрабатываться больше? – t0mm13b

+0

Точно. Хотя, как сказано, аналогия с UNIX-оболочкой довольно ошибочна для этого. – Joey

ответ

11

Конвейеризация означает передачу результатов одной функции другой функции. В примере, который вы даете «nums», передается List.Filter, отфильтрованные результаты затем передаются в List.Map.

Больше информации здесь: http://msdn.microsoft.com/en-us/magazine/cc164244.aspx#S6

+0

@Steve: Спасибо за ссылку, рассмотрим это, быстрый вопрос - как конвейерная информация знает, когда прекратить передачу информации от одного к другому? :) Благодаря. – t0mm13b

+0

@Steve: Это хорошая ссылка! У меня есть закладки, как только я закончу статью Томаша Петричека! Это моет мой мозг в настоящий момент с F # интерактивной оболочкой здесь, на моем конце .... – t0mm13b

+0

Извините, я просто вхожу в F # тоже, поэтому я не уверен, что могу ответить на ваш следующий вопрос. Я не думаю, что вы можете «остановить» конвейерную обработку, я думаю, что все результаты первой функции переходят к следующей. –

13

В некотором смысле нет ничего особенного конвейерного; вместо того, чтобы писать f (g (h x)), вы можете написать x |> h |> g |> f, что не кажется очевидным улучшением. Тем не менее, есть два момента, стоит иметь в виде:

  1. Иногда порядок чтения лучше для конвейерной версии: «Take х и отправить его в час, отправить результат в г, отправить результат е» может легче понять, чем «Применить f к результату применения g к результату применения h к x».
  2. Тип вывода часто работает намного лучше для конвейерной версии. Вероятно, это самая большая причина, по которой конвейерная обработка используется так много в F #. Поскольку вывод типа продолжается слева направо, x |> Array.map (fun s -> s.Length) будет работать, когда x является string[], но Array.map (fun s -> s.Length) x не будет; вам нужно сделать Array.map (fun (s:string) -> s.Length) x.
2

Оформить заявку Pipelining in F# для получения пояснения.

(Если вы знакомы с командной строки и труб UNIX как

cat file1 | sort | head 

Похожая идея;. Результат предыдущего выражения становится аргументом следующей функции)

3

Как другие упомянутые, конвейерная обработка больше похожа на конвейер UNIX. Это позволит вам написать некоторый ввод, за которым следуют операции, которые должны быть применены к нему, вместо обычных вызовов вложенных функций. В этом примере стандартный F # код будет выглядеть следующим образом:

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

Обратите внимание, что вход nums глубоко вложенной в выражении, и это не так легко увидеть, что сначала фильтруется, а затем проецируется. Используя конвейерную обработку, вы можете написать код по-другому, но это будет означать одно и то же.

Хитрость заключается в том, что оператор конвейерной обработки принимает два параметра с использованием инфиксной нотации (например, x |> f). Параметр x будет передан как последний аргумент функции справа (f). Вы можете использовать конвейерный с любыми функциями 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 :-) 

Важны о F операторе # конвейерных является то, что это не каким-либо специальным встроенными функциями языка.Это простой пользовательский оператор, который можно определить по своему усмотрению:

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

Меня отталкивает, что определение такого мощного и полезного оператора настолько тривиально. – Benjol

Смежные вопросы