El principio de subir los ifs y bajar los fors: la clave para un código más claro y rápido
Push Ifs Up and Fors Down: The Idiom, Its Algebra, and Its Limits
El principio «push ifs up, fors down» propone centralizar las bifurcaciones en el llamador y dejar los bucles en funciones de lote sin ramas. El artículo explora su aplicación en optimización de consultas SQL (proyecciones y selecciones tempranas, joins tardíos), en programación funcional y teoría de categorías, y en la ley que relaciona filter y map. También analiza sus límites: solo es válido cuando la condición es invariante al bucle o el predicado es barato.
La ley que realmente los relaciona es: filter p . map f == map f . filter (p . f)
- socializer
Siempre me impresiona la capacidad de los LLM para tomar ideas triviales y convertirlas en publicaciones de blog largas y obtusas con analogías innecesarias.
- hatthew
¿Estamos hablando de esto desde la perspectiva de CS (optimización de algoritmos) o de SE (diseño de código)?
Desde una perspectiva de SE, haz una función flatmap que maneje explícitamente Collection<Optional<Walrus>>. La implementación no importa. Si tu lenguaje/framework ya tiene una función flatmap compatible, haz una única función frobnicate(Optional<Walrus>) que devuelva el valor que sea necesario para que flatmap(frobnicate) los descarte.
Desde una perspectiva de CS, hacer un filter de Collection<Optional<Walrus>> a Collection<Walrus> es probablemente una mala idea. Si tu colección es pequeña, nada importa. Si tu colección es grande, probablemente no quieras perder tiempo haciendo una nueva copia de ella. Si tu filter solo devuelve una vista en lugar de una copia dura, entonces no hay beneficio de optimización y deberías hacer lo que tenga más sentido desde una perspectiva de SE. Si frobnicate es barato, entonces estás pagando el impuesto de fallo de predicción de ramas de todos modos, sin importar cuándo hagas frobnicate, y si frobnicate es más caro, probablemente deberías paralelizar y hacer que cada hilo se encargue de desempaquetar el Optional. De cualquier manera, probablemente no quieras perder tiempo haciendo una copia.
Todas estas son generalizaciones basadas en hipotéticos y ciertamente hay muchas excepciones, pero en términos generales no veo un argumento sólido aquí. Si la optimización importa, entonces optimiza según tu propio perfilado de tu situación, y si la optimización no importa, entonces diseña tus funciones según lo que [feat …]
- rtpg
Siempre he creído lo contrario: lleva los condicionales a lo más profundo de tu código para que el flujo de control de alto nivel sea regular.
Pero supongo que mi filosofía más amplia para hacer código que evite errores es que hay un par de cosas que se hacen al tratar con datos:
- distribución
- decisión
Y quieres evitar que la distribución y la decisión estén mezcladas en el mismo lugar.
La "distribución" puede ser bucles for, pero también dividir algunos datos según alguna clave en N cubos distintos.
La "decisión" es donde miras los datos más de cerca para tomar alguna decisión (como "¿es este un cliente grande o un cliente pequeño?").
La distribución a menudo implica toma de decisiones, pero si las mezclas todas en un solo lugar puedes ofuscar tus puntos de decisión. Dividirlo simplemente hace que las cosas sean "obviamente" correctas o "obviamente" incorrectas. El rendimiento es otra discusión, por supuesto, pero en la práctica la mayoría de las cosas no están a una escala donde importe.
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)
Realmente valoro los patrones de código que hacen obvios los errores, o al menos dificultan meter un error en algún lugar. Sin embargo, algunos patrones son más difíciles de describir en este modelo.
(Aunque me gusta el consejo de tener un vocabulario consistente para trabajar con colecciones como principio, solo encuentro que el uso de condicionales de nivel superior tiende a llevarte rápidamente a "... ¿por qué este método n[…]"
- ivanjermakov
Ahorra algo de tiempo y lee la publicación original en su lugar: https://matklad.github.io/2023/11/15/push-ifs-up-and-fors-do...
- gorgoiler
Erm, ¿no? Escribes f(w: Walrus) -> Walrus y luego dejas que quien llama maneje Walrus|None e Iterable[Walrus] como quiera.
Y si alguien decide que el código base necesita una abstracción sobre (y por lo tanto funciones específicas para manejar) Iterable[Walrus|None], entonces consultas el clima y sugieres que se tomen un descanso y den un paseo. (Consultas el clima para ver si deberías prestarles tu paraguas).
¿Qué me estoy perdiendo?