F # выход! оператор - Реализация и возможные эквиваленты C #

В настоящее время я изучаю F #, и мне действительно нравится оператор yield! (yield-bang). Конечно, не только для его имени, но и для того, что он делает.

Оператор yield! в основном позволяет вам выводить все элементы последовательности из выражения последовательности. Это полезно для составления перечислителей. Поскольку я регулярно сталкиваюсь с большими и сложными перечислителями, меня интересуют стратегии, которые мы можем использовать, чтобы разбить их и составить из более простых перечислителей.

К сожалению, оператор yield yield! недоступен в C #. Насколько я понимаю, то, что он делает, похоже на foreach (var x в источнике) и дает x; , но книгу, которую я читаю ( Petricek ' Реальный мир F # - Мэннинг ) предполагает, что он имеет лучшую производительность ...

  • Так что же здесь делает компилятор F #? (Да, я тоже могу посмотреть на это с помощью Reflector, но мне бы хотелось иметь более подробное описание механизма).

Для достижения аналогичной конструкции в C # я исследовал несколько способов, но ни один из них не является так же кратко, как и оператор yield! , и я также не уверен в их сложности. Может ли кто-нибудь предоставить входные данные, если мои номера BigO верны?

  • Разложить перечислитель на несколько частных перечислителей, а затем вывести каждый элемент из общедоступного перечислителя:

     foreach (var x in part1 ()) вывести x Я хотел бы получить более подробное описание механизма). 

Чтобы создать аналогичную конструкцию в C #, я исследовал несколько способов, но ни один из них не является столь же кратким, как оператор yield! и Я также не уверен в их сложности. Может ли кто-нибудь предоставить входные данные, если мои номера BigO верны?

  • Разложить перечислитель на несколько частных перечислителей, а затем вывести каждый элемент из общедоступного перечислителя:

     foreach (var x in part1 ()) вывести x Я хотел бы получить более подробное описание механизма). 

Чтобы создать аналогичную конструкцию в C #, я исследовал несколько способов, но ни один из них не является столь же кратким, как оператор yield! и Я также не уверен в их сложности. Может ли кто-нибудь предоставить входные данные, если мои номера BigO верны?

  • Разложить перечислитель на несколько частных перечислителей, а затем вывести каждый элемент из общедоступного перечислителя:

     foreach (var x in part1 ()) вывести x
    foreach (var x в part2 ()) дает x
    

    Это эффективно приведет к «двойной доходности» по каждому элементу. Это O (2n) тогда? (или, возможно, хуже?) В любом случае, использование этого подхода мешает мне использовать yield break; из любой из моих частей.

  • Разложить перечислитель на несколько частных перечислителей, а затем объединить все частные перечислители из общедоступного перечислителя:

     вернуть part1 (). Concat (part2 ())
    

    Я считаю, что это ничем не отличается от вышеупомянутого решения, потому что Concat () реализован так, как я обрисовал выше.

Есть ли другие варианты?

15
задан Ruben Bartelink 17 August 2010 в 12:18
поделиться

3 ответа

Что касается того, как компилятор переводит yield! Операция , статья , процитированная Томасом Левеском в его ответе, иллюстрирует один метод реализации в разделе 4.3 (в частности, их пример, охватывающий рисунки 7-9, иллюстрирует общую стратегию). Я не думаю, что есть хороший способ сделать это из блока итератора в C # - насколько я понимаю предложенные вами решения, они оба могут привести к квадратичному поведению при рекурсивном использовании. Вы всегда можете вручную создать подкласс NestedEnumerable для повышения производительности, но это будет довольно уродливо по сравнению с использованием обычного блока итератора.

6
ответ дан 1 December 2019 в 04:17
поделиться

В текущей версии C # я не думаю, что у вас есть другие варианты, кроме foreach ... yield return и Concat . Согласен, было бы неплохо получить доходность ! в C #, это сделало бы некоторые конструкции более элегантными, но я сомневаюсь, что эта функция когда-либо попадет в список обязательных, поскольку мы легко можем обойтись без нее.

Возможно, вас заинтересует эта исследовательская статья MS , в которой представлена ​​новая конструкция yield foreach :

IEnumerable<XmlNode> Traverse(XmlNode n)
{
    yield return n;
    foreach (XmlNode c in n.ChildNodes)
        yield foreach Traverse(c);
}

Относительно вашего вопроса о сложности: в обоих случаях это O (n) . O (2n) не используется, потому что он обозначает ту же сложность, что и O (n) (линейный). Я не думаю, что вы можете добиться большего с текущими функциями C # ...

7
ответ дан 1 December 2019 в 04:17
поделиться

Нет прямого аналога yield! на C #. В настоящее время вы застряли с комбинацией foreach и yield return .

Однако IIRC, LINQ предлагает нечто подобное, а именно оператор запроса SelectMany , который переводится в C # как несколько предложений from .. in .. .

(Надеюсь, я не путаю две разные концепции, но IIRC, оба yield! и SelectMany по сути являются «сглаживающими» проекциями, т. Е. Иерархией объекты «сплющиваются» в список.)

3
ответ дан 1 December 2019 в 04:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: