Дополнить функцию высшего порядка

Я пытаюсь написать функцию дополнения, так что при наличии функции f она возвращает функцию, которая при предоставлении того же ввода, что и f, возвращает логическую противоположность.

Поместив аналогичный код в VS2017, я не получаю ошибок, однако пока не могу запустить код, чтобы проверить, будет ли он работать должным образом. Я намеревался сначала попробовать это в repl, чтобы посмотреть, будет ли он работать так, как ожидалось. Код, который я использовал, был таким:

   public static Func<T, bool> Complement<T>(Func<T, bool> f)
   {
       return (T x) => !f(x);
   }

   public static bool GreaterThanTwo (int x) {
     return x > 2;
   }

   static public void Main(string[] args)
   {
     Func<int, bool> NotGreaterThanTwo = Complement(GreaterThanTwo);
     Console.WriteLine(NotGreaterThanTwo(1));
   }

Вот ссылка на то же.

В реплике я получаю сообщение об ошибке:

main.cs(17,42): error CS0411: The type arguments for method `MainClass.Complement(System.Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly Compilation failed: 1 error(s), 0 warnings compiler exit status 1

Я просмотрел несколько вопросов о переполнении стека, которые охватывают одно и то же сообщение об ошибке, например это и это, но я не могу понять, как они относятся к этой проблеме, с которой я столкнулся.

Система типов не может определить, что такое <T>, когда вы вызываете его в своем основном методе, вы можете «помочь», указав тип ввода для дополнения, например. изменить Complement(GreaterThanTwo); на Complement<int>(GreaterThanTwo);

MikNiller 20.06.2019 10:02

@OliverRadini ссылки точно объясняют, что не так, и их следует считать дубликатами. Компилятор не может определить, какой тип использовать для Complement.

Panagiotis Kanavos 20.06.2019 10:02

@PanagiotisKanavos Ссылки, возможно, точно объясняют, что не так, но, боюсь, они не для меня. Учитывая это, вы можете по-прежнему помечать это как дубликат, это, конечно, ваше дело, хотя я считаю, что в переполнении стека есть место для вопросов, которые, если их сформулировать по-другому, предлагают понимание, которое может отсутствовать в существующих вопросах, несмотря на тот факт, что некоторые могут считать, что они имеют одинаковое содержание.

OliverRadini 20.06.2019 10:05

@PanagiotisKanavos, кроме того, к вашему сведению, правильный ответ на дубликат - пометить вопрос как таковой, я не вижу смысла добавлять комментарий, просто говоря, что вы считаете, что вопрос является дубликатом, когда возможность иметь его правильно считается таковым доступным для вас

OliverRadini 20.06.2019 10:06

@OliverRadini, потому что я пытаюсь найти видео Эрика Липперта с объяснением из этого ответа 2006 года. Он отсутствует после изменения движка блога MSDN. В любом случае, кажется, что фактический ответ заключается в том, что Complement(GreaterThanTwo) пытается использовать группу методов, а не Func<int,bool>, что смущает компилятор.

Panagiotis Kanavos 20.06.2019 10:08

Если мы укажем явный Func<T, bool>, например. Func<int, bool> gt = GreaterThanTwo; var NotGreaterThanTwo = Complement(gt); код компилируется

Dmitry Bychenko 20.06.2019 10:13

@DmitryBychenko - как уже было отмечено в 1-м комментарии здесь.

Henk Holterman 20.06.2019 10:14

Приведение типов также работает: Complement((Func<int, bool>)GreaterThanTwo)

Henk Holterman 20.06.2019 10:14

Меня интересует «аналогичный код в VS2017, я не получаю ошибок». Можете ли вы опубликовать версии компилятора и фреймворка для этого? (без репродукции).

Henk Holterman 20.06.2019 10:17

Я только что вернулся к C# 4 и получил ту же ошибку.

Henk Holterman 20.06.2019 10:22

@HenkHolterman Правила конвертации изменились в VS 2010

Panagiotis Kanavos 20.06.2019 10:46
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
11
11
383
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Complement(GreaterThanTwo) пытается использовать группа методов, а не Func<int,bool> делегата. Это не удается, потому что Complement<T> ожидает общий делегат.

Вызов будет компилироваться с Func<int,bool>, например:

Func<int,bool> cmp= x=>x > 2;
var NotGreaterThanTwo = Complement(cmp);

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

Func<int,bool> cmp= GreaterThanTwo;
var NotGreaterThanTwo = Complement(cmp);

Возникает вопрос, почему исходный код не работал? Приведение явный также работает:

var NotGreaterThanTwo = Complement((Func<int,bool>)GreaterThanTwo);

Группа методов представляет собой группа перегруженных методов, а не только один метод. Это означает, что компилятор должен иметь возможность найти который доступных групп для использования в любой ситуации.

Остальное предположение, поскольку я не нашел определенной ссылки или примечания к дизайну об этом конкретном случае.

Первые два правила преобразования групп методов, вероятно, объясняют, что не так:

  • A single method M is selected corresponding to a method invocation (Method invocations) of the form E(A), with the following modifications:

    • The argument list A is a list of expressions, each classified as a variable and with the type and modifier (ref or out) of the corresponding parameter in the formal_parameter_list of D.
    • The candidate methods considered are only those methods that are applicable in their normal form (Applicable function member), not those applicable only in their expanded form.
  • If the algorithm of Method invocations produces an error, then a compile-time error occurs. Otherwise the algorithm produces a single best method M having the same number of parameters as D and the conversion is considered to exist.

В Complement<T>(Func<T, bool> f) нет вызова, поэтому компилятор не знает метод который в группе для выбора и преобразования. Он даже не знает, что такое T, поэтому он не может знать, совпадают ли какие-либо методы в этой группе.

С другой стороны это работает:

var xx=new []{1,2,3}.Where(GreaterThanTwo);

Однако в этом случае подпись Where:

public static System.Collections.Generic.IEnumerable<TSource> Where<TSource> (
    this System.Collections.Generic.IEnumerable<TSource> source, 
    Func<TSource,bool> predicate);

и аргумент типа уже доступен из IEnumerable<TSource>.

Мы начали комментировать одновременно, просматривали одни и те же документы и примерно в одно и то же время опубликовали один и тот же ответ :)

Camilo Terevinto 20.06.2019 10:43

Большое спасибо за исчерпывающий ответ, из которого становится понятно, в чем тут проблема

OliverRadini 20.06.2019 10:45

@OliverRadini, честно говоря, определенным ответом будет ссылка на конкретное правило или объяснение от Джона Скита или Эрика Липперта. Я не языковой юрист, поэтому я не уверен в более глубоких причинах. Я даже не думаю сознательно о группах методов, пока не сталкиваюсь с ошибками компиляции, подобными этой.

Panagiotis Kanavos 20.06.2019 10:49

@CamiloTerevinto думаю, мы сталкивались с этой ошибкой достаточно часто, чтобы понять, что это не так просто ...

Panagiotis Kanavos 20.06.2019 10:50

От Что такое группа методов в C#?

A method group is the name for a set of methods (that might be just one) - i.e. in theory the ToString method may have multiple overloads (plus any extension methods): ToString(), ToString(string format), etc - hence ToString by itself is a "method group".

Когда вы используете:

Func<int, bool> NotGreaterThanTwo = Complement(GreaterThanTwo);

GreaterThanTwo — группа методов. Итак, это может быть правдой:

public static bool GreaterThanTwo (int x) {
  return x > 2;
}

// to make it clear this is something completely different
public static bool GreaterThanTwo (Action<bool, string, object> x) {
  return false;
}

Таким образом, компилятор не может сделать вывод о том, какой именно метод вы имеете в виду, и вам нужно помочь ему, разрешив эту двусмысленность.

Как вы решите решить это, решать вам, но здесь есть как минимум 3 варианта:

  1. Укажите общий аргумент:

    Complement<int>(GreaterThanTwo);
    
  2. Неявно преобразуйте группу методов в нужный делегат:

    Func<int, bool> greaterThanTwo = GreaterThanTwo; 
    var notGreaterThanTwo = Complement(greaterThanTwo);
    
  3. Явно преобразуйте группу методов в нужный делегат:

    Complement((Func<int, bool>)GreaterThanTwo);
    

Другие вопросы по теме