Какова связь между методами расширения C # и оператором прямой пересылки F #? - PullRequest
4 голосов
/ 13 июля 2010

После использования F # для нескольких небольших проблем, я обнаружил, что для меня полезно думать о методах расширения C # как об «способе обращения».в оператор прямого канала '.

Например, для заданной последовательности Int32 с именем ints код C #:

ints.Where(i => i > 0)
    .Select(i => i * i)

аналогичен коду F #

let where = Seq.filter
let select = Seq.map

ints |> where (fun i -> i > 0)
     |> select (fun i -> i * i)

На самом деле, я часто думаю о методах расширения в IEnumerable как о просто библиотеке функций, которые предоставляют функциональность, аналогичную модулю Seq в F #.

Очевидно, что параметр piped является последним параметром в функции F #, нопервый параметр в методе расширения C # - но кроме этого, есть ли проблемы с использованием этого объяснения при описании методов расширения или конвейерной передачи другим разработчикам?

Буду ли я их вводить в заблуждение, или этополезная аналогия?

Ответы [ 3 ]

6 голосов
/ 13 июля 2010

Я бы не стал проводить аналогию, потому что метод расширения может быть вызван так, как если бы он был методом экземпляра, тогда как конвейерная обработка явно синтаксически совершенно иная.Кроме того, в F # также есть методы расширения (а также свойства и события расширения!), Поэтому я думаю, что существует реальная возможность вызвать путаницу.

Однако я думаю, что верно как стиль, так и расширение трубопроводаметоды позволяют бегло описать вычисления как набор шагов, что, вероятно, является сходством, к которому вы стремитесь.Концептуально было бы хорошо, если бы код отражал идею «взять этот набор целых, сохранить только подмножество, удовлетворяющее i> 0, и затем возвести в квадрат каждый из них», - так можно описать задачу на английском языке.И синтаксис конвейера, и методы расширения C # допускают подобные вещи.

3 голосов
/ 13 июля 2010

Я тоже думаю, что это очень полезная аналогия. Фактически, я использовал именно эту аналогию при описании оператора конвейеризации в моей книге Функциональное программирование в реальном мире (которая пытается объяснить функциональные идеи людям с фоном C #). Ниже приводится цитата из главы 6. ​​

Что касается различий между ними - существуют некоторые концептуальные различия (например, методы расширения «добавление элементов к объектам»), но я не думаю, что это влияет на то, как мы их используем на практике.

  • Одним заметным практическим отличием между методами расширения C # и функциями F # является поддержка редактора - при вводе "." в C # вы можете увидеть методы расширения для конкретного типа. Я считаю, что F # IntelliSense может показывать отфильтрованный список, когда вы набираете |> (в принципе), но это, вероятно, гораздо больше работы, и он пока не поддерживается.

  • Обе конструкции используются для включения стиля композиционного программирования на основе выражений. Под этим я подразумеваю, что вы можете написать гораздо большие части кода в виде одного выражения, описывающего, что должно быть сделано (без методов расширения / оператора конвейерной обработки вы, вероятно, разбили бы код на несколько этапов). Я думаю, что этот стиль программирования обычно приводит к более декларативному коду.


Оператор конвейеризации (|>) позволяет нам написать первый аргумент для функции в левой части; перед именем функции. Это полезно, если мы хотим вызвать несколько функций обработки для некоторого значения в последовательности, и мы хотим записать значение, которое обрабатывается первым. Давайте рассмотрим пример, показывающий, как инвертировать список в F #, а затем взять его первый элемент:

List.hd(List.rev [1 .. 5])

Это не очень элегантно, поскольку операции записываются в обратном порядке, в котором они выполняются, а обрабатываемое значение находится с правой стороны, в нескольких скобках. Используя методы расширения в C #, мы написали бы:

list.Reverse().Head();

В F # мы можем получить тот же результат, используя оператор конвейеризации:

[1 .. 5] |> List.rev |> List.hd

Хотя это может показаться сложным, на самом деле оператор очень прост. У него есть два аргумента - второй (справа) является функцией, а первый (слева) является значением. Оператор дает значение в качестве аргумента функции и возвращает результат.

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

0 голосов
/ 14 июля 2010

Вы также можете обратиться к оператору обратной компоновки функций, если хотите повторно использовать эти конвейеры.

let where = Seq.filter
let select = Seq.map
let whereGreaterThanOneComputeSquare = 
   where (fun i -> i > 0) << select (fun i -> i * i)
ints |> whereGreaterThanOneComputeSquare
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...