В настоящее время я изучаю F #, и мне действительно нравится оператор yield!
(yield-bang). Конечно, не только для его имени, но и для того, что он делает.
Оператор yield!
в основном позволяет вам выводить все элементы последовательности из выражения последовательности. Это полезно для составления перечислителей. Поскольку я регулярно сталкиваюсь с большими и сложными перечислителями, меня интересуют стратегии, которые мы можем использовать, чтобы разбить их и составить из более простых перечислителей.
К сожалению, оператор yield yield!
недоступен в C #. Насколько я понимаю, то, что он делает, похоже на foreach (var x в источнике) и дает x;
, но книгу, которую я читаю ( Petricek ' Реальный мир F # - Мэннинг ) предполагает, что он имеет лучшую производительность ...
Для достижения аналогичной конструкции в 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 ()
реализован так, как я обрисовал выше.
Есть ли другие варианты?
Что касается того, как компилятор переводит yield! Операция
, статья , процитированная Томасом Левеском в его ответе, иллюстрирует один метод реализации в разделе 4.3 (в частности, их пример, охватывающий рисунки 7-9, иллюстрирует общую стратегию). Я не думаю, что есть хороший способ сделать это из блока итератора в C # - насколько я понимаю предложенные вами решения, они оба могут привести к квадратичному поведению при рекурсивном использовании. Вы всегда можете вручную создать подкласс NestedEnumerable
для повышения производительности, но это будет довольно уродливо по сравнению с использованием обычного блока итератора.
В текущей версии 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 # ...
Нет прямого аналога yield!
на C #. В настоящее время вы застряли с комбинацией foreach
и yield return
.
Однако IIRC, LINQ предлагает нечто подобное, а именно оператор запроса SelectMany
, который переводится в C # как несколько предложений from .. in ..
.
(Надеюсь, я не путаю две разные концепции, но IIRC, оба yield!
и SelectMany
по сути являются «сглаживающими» проекциями, т. Е. Иерархией объекты «сплющиваются» в список.)