Подтвердить что ты не робот

Функторы, когда я должен использовать их, что их предполагаемое использование

Я просто не могу обернуть вокруг себя голову.

Как я понимаю, это динамическое добавление логики в класс. Готовы ли для этого классы в рамках?

Почему я должен просто расширить класс и добавить к нему функциональные возможности в расширении. Я был бы глобально доступным, и афайк был бы намного легче поддерживать.

Я читал, что существует 4 типа функторов:

Comparer
Закрытие
Предикат
Трансформатор

Мы должны, вероятно, обращаться с каждым из них.

p.s. есть что-то подобное в vb?

Поэтому я могу заявить, что я считаю, что лямбда-выражения являются функторами. Это немного облегчает мне вещи:) (хе-хе)

  • Лямбда-выражения являются функторами?
  • Анонимные функции - это функторы?

Но я задал этот вопрос, потому что я столкнулся с другим типом fucntors, а именно:

delegate void FunctorDelegate(int value);
class Addition {
    FunctorDelegate _delegate;

    public Addition AddDelegate(FunctorDelegate deleg) {
        _delegate += deleg;
        return this;
    }
    public int AddAllElements(IList< int> list) {
        int runningTotal = 0;
        foreach( int value in list) {
            runningTotal += value;
            _delegate(value);
        }
        return runningTotal;
    }
}

И затем называя это следующим:

 int runningTotal = new Addition()
     .AddDelegate(new FunctorDelegate(
                     delegate(int value) {
                         if ((value % 2) == 1) {
                             runningOddTotal += value;
                         }
                     }))
    .AddDelegate(new FunctorDelegate(
                     delegate(int value) {
                         if ((value % 2) == 0) {
                             runningEvenTotal += value;
                         }
                     }))
    .AddAllElements(list);

Так что никаких фантазийных вещей в стиле лямбда.

Теперь у меня есть этот пример, но не совсем понятно, почему это "хорошее" решение.

Являются ли делегаты (функторы) используемыми в качестве лямбда-выражений или анонимными методами "в большинстве случаев" только как ярлык для программиста? Есть, насколько я вижу, только несколько случаев, когда они на самом деле являются предпочтительным выбором для проблемы.

4b9b3361

Ответ 1

Я думаю, вы смешиваете термины с разных языков. Кажется, вы используете "Functor" в смысле С++ или Java, например. см. страницу wikipedia. В С++ это объект класса, который перегружает оператор функции-вызова, поэтому его можно использовать как функцию, но с состоянием.

Это логически то же, что и делегат, связанный с методом экземпляра на С# (или на любом языке .NET).

Существует три способа написать такую ​​вещь. Во-первых, вы можете написать обычный метод, а затем назначить имя метода переменной-делегату.

void MyMethod() { Console.WriteLine("Hi!"); }

void Foo()
{
    Action a = MyMethod;
    a();
}

Во-вторых, вы можете использовать анонимный синтаксис метода, введенный в С# 2.0:

void Foo()
{
    Action a = delegate { Console.WriteLine("Hi!"); }
    a();
}

В-третьих, вы можете использовать синтаксис лямбда, введенный в С# 3.0:

void Foo()
{
    Action a = () => Console.WriteLine("Hi!");
    a();
}

Преимущество последних двух состоит в том, что тело метода может читать и записывать локальные переменные в содержащем методе.

Преимущество лямбда-синтаксиса над anon-методами заключается в том, что оно более кратким и оно выводит вывод о параметрах.

Обновление:. Преимущество anon-методов (delegate keyword) над lambdas заключается в том, что вы можете вообще опустить параметры, если они вам не нужны:

// correct way using lambda
button.Click += (sender, eventArgs) => MessageBox.Show("Clicked!");

// compile error - wrong number of arguments
button.Click += () => MessageBox.Show("Clicked!");

// anon method, omitting arguments, works fine
button.Click += delegate { MessageBox.Show("Clicked!"); };

Я знаю только одну ситуацию, когда это стоит знать, а это значит, что при инициализации события вам не нужно проверять null перед тем, как его запустить:

event EventHandler Birthday = delegate { };

Избегает много глупостей в других местах.

Наконец, вы упомянули, что существует четыре вида функтора. На самом деле существует бесконечность возможных типов делегатов, хотя у некоторых авторов могут быть свои фавориты, и, очевидно, будут некоторые общие шаблоны. Action или Command не принимает параметров и возвращает void, а предикат принимает экземпляр некоторого типа и возвращает true или false.

В С# 3.0 вы можете разбить делегат с четырьмя параметрами любых типов:

Func<string, int, double> f;  // takes a string and an in, returns a double

Re: Обновленный вопрос

Вы спрашиваете (я думаю), если есть много случаев использования для лямбда. Есть более чем возможно перечисление!

Вы чаще всего видите их в середине больших выражений, которые работают с последовательностями (списки вычисляются "на лету" ). Предположим, у меня есть список людей, и мне нужен список людей ровно сорок лет:

var exactlyForty = people.Where(person => person.Age == 40);

Метод Where является методом расширения в интерфейсе IEnumerable<T>, где T в этом случае является своего рода классом Person.

Это известно в .NET как "Linq to Objects", но известное в других местах как чистое функциональное программирование на последовательностях или потоках или "ленивых" списках (все разные имена для одного и того же).

Ответ 2

В .NET-терминах, я думаю, что вы описываете Delegate - и он существует во всех .NET, а не только в С#.

Я не уверен, что "закрытие" было бы "типом" в том же виде, что и "сопоставитель/предикат/трансформатор", поскольку в терминах С# закрытие представляет собой просто деталь реализации, но может быть любой из этих трех.

В .NET делегаты используются двумя основными способами:

  • как механизм событий
  • для программирования функционального стиля

Первое важно, но похоже, что вы больше заинтересованы во втором. В действительности они работают так же, как интерфейсы с одним методом... рассмотрим:

List<int> vals = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<int> evenVals = vals.FindAll(i => i % 2 == 0); // predicate
List<string> valsAsStrings = vals.ConvertAll(i => i.ToString()); // transformer
// sort descending
vals.Sort((x, y) => y.CompareTo(x)); // comparer

Закрытие больше, когда мы добавляем дополнительную область от вне делегата в делегат:

int max = int.Parse(Console.ReadLine()); // perhaps 6
List<int> limited = vals.FindAll(i => i <= max);

здесь max записывается в делегат как закрытие.

Re "Являются ли классы в рамках структуры подготовленными для этого?" - многие из них, и LINQ имеет большой путь к тому, чтобы это еще шире. LINQ предоставляет методы расширения (например) для всех IEnumerable<T> - это означает, что коллекции без доступа на основе делегатов приобретают их бесплатно:

int[] data = { 1,2,3,4,5,6,7,8,9 };
var oddData = data.Where( i => i % 2 == 1 );
var descending = data.OrderBy(i => -i);
var asStrings = data.Select(i => i.ToString());

Здесь методы Where и OrderBy - это методы расширения LINQ, которые принимают делегаты.

Ответ 3

Интересно с терминологией; моя спонтанная интерпретация термина "Functor" заключалась в том, что он ссылался на анонимные методы. Так что это будет мое взятие на себя.

Вот некоторые из моих типичных применений:

Сравнение (обычно для сортировки списка):

List<int> ints = new List<int>();
ints.AddRange(new int[] { 9, 5, 7, 4, 3, 5, 3 });
ints.Sort(new Comparison<int>(delegate(int x, int y)
    {
        return x.CompareTo(y);
    }));
// yes I am aware the ints.Sort() would yield the same result, but hey, it just
// a conceptual code sample ;o)

// and the shorter .NET 3.5 version:
ints.Sort((x, y) =>
{
    return x.CompareTo(y);
});

Я буду использовать этот подход для сопоставлений, вместо того, чтобы иметь его в своем собственном методе, используя делегат для этого метода, в тех случаях, когда этот конкретный вид происходит только в одном месте. Если вполне вероятно, что я захочу использовать одно и то же сравнение где-то в другом месте, он будет жить в своем собственном многоразовом методе.

Еще одно из моих довольно распространенных применений - в модульном тестировании, когда тест полагается на какое-то событие, которое поднимается. Я обнаружил, что это необходимо, когда рабочие процессы модульного тестирования в Workflow Foundation:

WorkflowRuntime runtime = WorkflowHost.Runtime;  
WorkflowInstance instance = runtime.CreateWorkflow(typeof(CreateFile)); 
EventHandler<WorkflowEventArgs> WorkflowIdledHandler = delegate(object sender, WorkflowEventArgs e)
{
    // get the ICreateFileService instance from the runtime  
    ISomeWorkflowService service = WorkflowHost.Runtime.GetService<ISomeWorkflowService>();

    // set the desired file content  
    service.DoSomeWork(instance.InstanceId, inputData);
};  
// attach event handler
runtime.WorkflowIdled += WorkflowIdledHandler;  

instance.Start();  
// perform the test, and then detach the event handler
runtime.WorkflowIdled -= WorkflowIdledHandler; 

В этом случае проще обработать обработчик событий как анонимный метод, так как он использует переменную instance, которая определена в области методов unit test. Если бы я нанял, чтобы реализовать обработчик событий в качестве своего отдельного метода, мне также нужно было бы найти способ, чтобы он забирал instance, возможно, введя члена класса, который не казался бы идеальным дизайном в unit test класс.

Есть больше случаев, когда я нахожу это в своем коде, но обычно они имеют одну или две вещи:

  • Мне неинтересно ссылаться на этот фрагмент кода из любого места, кроме того, что в этом месте.
  • Метод нуждается в доступе к данным, которые будут недоступны для обычного метода.

Ответ 4

Я уверен, что вы имеете в виду Lambda Expressions. Это Малые функции, которые вы можете написать очень быстро, и у них есть характеристика "= > " Оператор. Это новая функция С# 3.0.

Этот пример будет классическим Transformer; для использования одного нам нужен делегат для определения подписи для лямбда-функции.

delegate int Transformer(int i);

Теперь объявите лямбда с этим делегатом:

Transformer sqr = x => x * x;

Мы можем использовать его как обычную функцию:

Console.WriteLine(sqr(3)); //9

Они часто используются в запросах LINQ, например, для сортировки (Comparer), для поиска через (Predicate).

В книге "Карманный справочник С#" (кроме того, что, по моему мнению, лучше всего подходит, я очень хорошо отношусь к Lambdas. (ISBN 978-0-596-51922-3)

Ответ 5

Реальный ответ заключается в том, что функтор является типом математического объекта и по-разному "овеществляется" разными языками. Например, предположим, что у вас есть "контейнерный" объект, который хранит кучу других объектов того же типа. (Например, набор или массив). Затем, если у вашего языка был метод, который позволяет вам "сопоставлять" по контейнеру, чтобы вы могли вызвать метод для каждого объекта в контейнере, контейнер был бы функтор.

Другими словами, функтор представляет собой контейнер с методом, который позволяет передать метод содержащимся в нем.

У каждого языка есть свой способ сделать это, и они иногда объединяют использование. Например, С++ использует указатели функций для представления "прохождения" метода и вызывает указатель функции "функтор". Делегаты - это всего лишь дескриптор методов, которые вы можете передать. Вы используете терминологию "неправильно", как это делает С++.

Хаскелл понимает это правильно. Вы объявляете, что тип реализует интерфейс functor, а затем вы получаете метод сопоставления.

Функции (например, lambdas) также являются функторами, но может быть трудно описать функцию как "контейнер". Короче говоря, функция представляет собой "контейнер" вокруг возвращаемого значения, построенный таким образом, что возвращаемое значение (возможно) зависит от аргументов функции.