Побочные эффекты в итераторе считаются вредными?

Сегодня я написал свой первый итератор на C#. Woohoo.

Интересно, что у него есть побочные эффекты. Мой итератор отфильтровывает недопустимые файлы из каталога и возвращает последовательность действительных файлов для обработки. Когда он встречает инкрустированный файл, он перемещает его в другой каталог.

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

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

Но потом вспомнил итераторы. И теперь у меня есть итератор, который возвращает действительные файлы и обрабатывает (перемещает) недопустимые.

Итак, мой вопрос таков: плохая идея, чтобы итератор имел такие побочные эффекты, как этот? Не скрываю ли я слишком много функциональности в итераторе?

@sharptooth - вы собираетесь получить значок таксономии или что?

annakata 08.05.2009 13:38
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
1
674
9

Ответы 9

Я бы сказал, что в целом - плохая идея иметь побочные эффекты в итераторе, но это не полное «нет-нет». Если у вас есть побочные эффекты, вызывающим абонентам будет сложно / невозможно работать чисто функционально. Это может быть или не быть проблемой в зависимости от вашего варианта использования.

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

Итераторы, которые логически являются перечислениями коллекций, не должны иметь побочных эффектов, нет. В частности, они не будут идемпотентными при перезапуске с помощью метода IEnumerator.Reset ().

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

Обратите внимание: IEnumerator.Reset () очень редко реализуется, а никогда реализуется в блоке итератора.

Jon Skeet 26.11.2008 14:48

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

Barry Kelly 26.11.2008 14:54

Тот факт, что это часть API, был в основном ошибкой :) Но да, я думаю, разумно ожидать, что по крайней мере итераторы самый будут идемпотентными и повторяемыми. Конечно, так будет не всегда - например, получение результатов запроса LINQ из веб-службы может каждый раз давать разные результаты.

Jon Skeet 26.11.2008 14:59

(Продолжение) Но можно надеяться, что причина для получения разных результатов будет «естественной», а не «потому что вы звонили мне в прошлый раз».

Jon Skeet 26.11.2008 15:00

Итераторы с побочными эффектами - ПЛОХО, мкай? :)

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

Итак, я не говорю на C#, но что-то вроде этого псевдокода:

good_handler = new FileHandler() {
  handle(File f) { print "Yay!"; }
}

bad_handler = new FileHandler() {
  handle(File f) { print "Nay!"; }
}

files = YourFileSequence();
visitor = new Visitor(good_handler, bad_handler);
visitor.visit(files);

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

Спасибо, ребята - и черт возьми! быстрые ответы!

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

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

Кроме того, я думаю, что я объединил идею посетителя с итератором, что тоже не очень хорошая идея.

С тех пор я изменил свою реализацию, чтобы создать две последовательности из моей исходной последовательности всех файлов - одну хорошую, одну плохую. Теперь я могу обрабатывать их более очевидным и интуитивным образом. Ура.

Итак, я до сих пор не использовал итератор в реальном мире. Ну что ж.

Спасибо! Мэтт

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

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

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

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

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

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

Еще одна проблема заключается в том, что метод может быть «неправильно использован» в том смысле, что вызывающий может попытаться использовать его для перемещения файлов, не будучи действительно заинтересованным в возвращаемых результатах.

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

Вопрос обсуждается в этом посте: http://codequota.com/archive/2012/02/13/iterator-blocks-and-side-effects.aspx

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