Push Ifs Up и Fors Down: алгебра и границы идиомы
Push Ifs Up and Fors Down: The Idiom, Its Algebra, and Its Limits
Идиома push-ifs-up-fors-down из Tiger Style от TigerBeetle и блога matklad переносится на оптимизацию SQL-запросов и теорию категорий. Подъём if к вызывающему сужает тип (Walrus вместо Option<Walrus>), а опускание for в batch-функцию убирает ветвления из горячего цикла. В базах это ранние проекции и selections, отложенные joins и векторизованное исполнение. Закон filter p . map f == map f . filter (p . f) следует из естественности catMaybes, но экономит только при дешёвом p . f.
Алгебра подсказывает, какие переписывания допустимы. В случае filter/map выше именно естественность catMaybes определяет законность и помогает рассуждать об общей структуре кода.
- socializer
Меня постоянно поражает способность LLM взять тривиальную идею и превратить её в длинный и заумный пост в блоге с ненужными аналогиями.
- hatthew
Мы говорим об этом с точки зрения CS (оптимизация алгоритмов) или SE (проектирование кода)?
С точки зрения SE, сделайте функцию flatmap, которая явно обрабатывает Collection<Optional<Walrus>>. Реализация не важна. Если в вашем языке/фреймворке уже есть совместимая функция flatmap, сделайте одну функцию frobnicate(Optional<Walrus>), возвращающую любое значение, необходимое для того, чтобы flatmap(frobnicate) их отбросил.
С точки зрения CS, делать фильтрацию из Collection<Optional<Walrus>> в Collection<Walrus>, вероятно, плохая идея. Если ваша коллекция маленькая, всё неважно. Если ваша коллекция большая, вы, вероятно, не хотите тратить время на создание её новой копии. Если ваш фильтр просто возвращает представление, а не жёсткую копию, то никакой оптимизационной выгоды нет, и вам следует просто делать то, что имеет наибольший смысл с точки зрения SE. Если frobnicate дешёвая, то вы в любом случае платите налог на провал предсказания ветвлений независимо от того, когда вы выполняете frobnicate, а если frobnicate дороже, то вам, вероятно, следует распараллелить и позволить каждому потоку обрабатывать распаковку Optional. В любом случае, вы, вероятно, не хотите тратить время на создание копии.
Все это обобщения, основанные на гипотетических ситуациях, и, конечно, есть много исключений, но в целом я не вижу здесь сильного аргумента. Если оптимизация важна, то оптимизируйте на основе собственного профилирования вашей ситуации, а если оптимизация не важна, то проектируйте свои функции исходя из того, что feat […]
- rtpg
Я всегда придерживался противоположного мнения: загоняйте условные операторы глубже в код, чтобы управляющий поток на верхнем уровне был регулярным.
Но, полагаю, моя более общая философия создания кода, избегающего ошибок, состоит в том, что при работе с данными есть пара вещей, которые делаются:
- распределение
- принятие решений
И вы хотите избегать того, чтобы распределение и принятие решений смешивались в одном месте.
«Распределение» может быть циклами for, но также и разбиением некоторых данных по некоторому ключу на N различных корзин.
«Принятие решений» — это когда вы смотрите на данные более внимательно, чтобы принять какое-то решение (например, «это крупный клиент или мелкий клиент»).
Распределение часто включает принятие решений, но если вы смешаете всё в одном месте, вы можете замаскировать свои точки принятия решений. Разделение просто делает вещи «очевидно» правильными или «очевидно» неправильными. Вопросы производительности — это, конечно, другой разговор, но на практике большинство вещей не в том масштабе, где это имеет значение.
by_category = defaultdict(list)
for d in data:
by_category[category(d)].append(d)
for category, per_category_data in by_category.items():
do_thing(category, per_category_data)
Я действительно ценю паттерны кода, которые делают ошибки очевидными или, по крайней мере, затрудняют заталкивание ошибки куда-либо. Некоторые паттерны, впрочем, труднее описать в этой модели.
(Мне всё же нравится совет иметь согласованный словарь для работы с коллекциями как принцип, просто я нахожу, что использование условных операторов на верхнем уровне быстро приводит к «... почему этот метод н […]
- ivanjermakov
Сэкономьте время и прочитайте оригинальный пост вместо этого: https://matklad.github.io/2023/11/15/push-ifs-up-and-fors-do...
- gorgoiler
Эм, нет? Вы пишете f(w: Walrus) -> Walrus, а затем позволяете вызывающему коду обрабатывать Walrus|None и Iterable[Walrus] как ему угодно!
А если кто-то решит, что кодовой базе нужна абстракция над (и, следовательно, конкретные функции для обработки) Iterable[Walrus|None], то вы проверяете погоду и предлагаете им сделать перерыв и прогуляться. (Вы проверяете погоду, чтобы понять, стоит ли одолжить им свой зонтик.)
Что я упускаю?