data driven - programming - Reflexiones sobre minimizar el código y maximizar la filosofía de los datos.
data driven testing (6)
Cuando pienso en esta filosofía con la que estoy bastante de acuerdo, lo primero que me viene a la mente es la eficiencia del código.
Cuando estoy creando un código, estoy seguro de que no siempre es algo que esté cerca de ser perfecto o que esté completamente informado. Saber lo suficiente para acercarme a la máxima eficiencia de una máquina cuando se necesita y una buena eficiencia en el resto del tiempo (quizás cambiando por un mejor flujo de trabajo) me ha permitido producir productos terminados de alta calidad.
Al codificar de una manera determinada por los datos, terminas usando el código para el cual es el código. Para ir y ''externalizar'' cada variable a los archivos sería estúpidamente extremo, la funcionalidad de un programa debe estar en el programa y el contenido, la configuración y otros factores pueden ser gestionados por el programa.
Esto también permite aplicaciones mucho más dinámicas y nuevas características.
Si tiene incluso una forma simple de base de datos, puede aplicar la misma funcionalidad a muchos estados. También puede hacer todo tipo de cosas creativas, como cambiar el contexto de lo que hace su programa basándose en los datos del encabezado del archivo o tal vez en el directorio, el nombre del archivo o la extensión, aunque no todos los datos se almacenan necesariamente en un sistema de archivos.
Finalmente, mantener su código en un estado en el que simplemente maneja los datos lo pone en un estado mental en el que está más cerca de imaginar lo que realmente está sucediendo. Esto también mantiene el volumen fuera de su código, reduciendo en gran medida el bloatware.
Creo que hace que el código sea más fácil de mantener, más flexible y más eficiente y me gusta.
¡Gracias a los demás por su aporte en esto también! Lo encontré muy alentador.
He oído hablar del concepto de minimizar el código y maximizar los datos, y me preguntaba qué consejos pueden darme otras personas sobre cómo / por qué debo hacer esto al crear mis propios sistemas.
En el software moderno, la línea entre el código y los datos puede volverse muy delgada y borrosa, y no siempre es fácil diferenciarlos. Después de todo, en lo que concierne a la computadora, todo es información , a menos que el código existente, normalmente el sistema operativo, determine lo contrario. Incluso los programas deben cargarse en la memoria como datos, antes de que la CPU pueda ejecutarlos.
Por ejemplo, imagine un algoritmo que calcula el costo de un pedido, donde los pedidos más grandes obtienen precios más bajos por artículo. Es parte de un sistema de software más grande en una tienda, escrito en C.
Este algoritmo está escrito en C y lee un archivo que contiene una tabla de entrada proporcionada por la administración con los distintos precios por artículo y los umbrales de tamaño de orden correspondientes. La mayoría de la gente diría que un archivo con una tabla de entrada simple es, por supuesto, datos.
Ahora, imagine que la tienda cambia su política a algún tipo de función asintótica, en lugar de umbrales preseleccionados, para que pueda acomodar órdenes increíblemente grandes. Es posible que también quieran tener en cuenta los tipos de cambio y la inflación, o cualquier otra cosa que proponga la gente de administración.
La tienda contrata a un programador competente y ella incorpora un buen analizador de expresiones matemáticas en el código C original. El archivo de entrada ahora contiene una expresión con variables globales, funciones como log()
y tan()
, así como algunas cosas simples como la constante de Planck y la tasa de degradación del carbon-14 .
cost = (base * ordered * exchange * ... + ... / ...)^13
La mayoría de la gente todavía argumentaría que la expresión, aunque no sea tan simple como una tabla, es de hecho datos. Después de todo, es probable que sea proporcionado tal cual por la gerencia.
La tienda recibe una gran cantidad de quejas de clientes que se convirtieron en personas con muerte cerebral al tratar de estimar sus gastos y del personal de contabilidad sobre la gran cantidad de cambios sueltos. La tienda decide volver a la mesa para pedidos pequeños y usar una secuencia de Fibonacci para pedidos más grandes.
El programador se cansa de modificar y volver a compilar el código C, por lo que en su lugar incorpora un intérprete de Python. El archivo de entrada ahora contiene una función Python que sondea una sala llena de monos Fib(n)
para el costo de pedidos grandes.
Pregunta: ¿Son estos datos de entrada de archivo?
Desde un punto de vista técnico estricto, no hay nada diferente. Tanto la tabla como la expresión debían analizarse antes del uso. El analizador de expresiones matemáticas probablemente admitía funciones y bifurcaciones, podría no haber sido Turing-complete, pero aún utilizaba un lenguaje propio (por ejemplo, MathML).
Sin embargo, ahora mucha gente diría que el archivo de entrada simplemente se convirtió en código .
Entonces, ¿cuál es la característica distintiva que convierte el formato de entrada de los datos en código ?
Modificabilidad: tener que volver a compilar todo el sistema para efectuar un cambio es una buena indicación de un sistema centrado en el código. Sin embargo, puedo imaginar fácilmente (bueno, más como lo que realmente he visto ) software que ha sido diseñado lo suficientemente incompetente para tener, por ejemplo, una tabla de entrada incorporada en el momento de la compilación. Y no olvidemos que muchas aplicaciones todavía tienen íconos, que la mayoría de la gente consideraría datos , incorporados en sus ejecutables.
Formato de entrada: este es, en mi opinión, ingenuamente, el factor más común que la gente considera: "Si está en un lenguaje de programación, entonces es un código" . Bien, C es el código, tienes que compilarlo después de todo. También estoy de acuerdo en que Python también es un código, es un lenguaje completo. Entonces, ¿por qué no es el código XML / XSL? XSL es un lenguaje bastante complejo por derecho propio, de ahí la
L
en su nombre.
En mi opinión, ninguno de estos dos criterios es la característica distintiva real. Creo que la gente debería considerar algo más:
- Mantenimiento: en resumen, si el usuario del sistema tiene que contratar a un tercero para hacer que la experiencia necesaria para modificar el comportamiento del sistema esté disponible, entonces el sistema debe considerarse centrado en el código hasta cierto punto.
Esto, por supuesto, significa que si un sistema está basado en datos o no debe considerarse al menos en relación con el público objetivo , si no en relación con el cliente, caso por caso.
También significa que la distinción puede verse afectada por el conjunto de herramientas disponible. La especificación de UML es una pesadilla por la que pasar, pero en estos días tenemos todos esos editores gráficos de UML para ayudarnos. Si existiera algún tipo de herramienta de inteligencia artificial de alto nivel de terceros que analice el lenguaje natural y produzca XML / Python / lo que sea, entonces el sistema se convierte en información basada en datos incluso para una entrada mucho más compleja.
Es probable que una tienda pequeña no tenga la experiencia o los recursos para contratar a un tercero. Por lo tanto, algo que permita a los trabajadores modificar su comportamiento con el conocimiento que se obtendría en un curso de administración promedio (matemáticas, cuadros, etc.) podría considerarse lo suficientemente basado en datos para esta audiencia.
Por otro lado, una corporación internacional multimillonaria generalmente tiene en su nómina un grupo de especialistas en TI y diseñadores web. Por lo tanto, XML / XSL, Javascript, o incluso Python y PHP son probablemente lo suficientemente fáciles de manejar. También tiene requisitos suficientemente complejos para que algo más simple simplemente no lo corte.
Creo que al diseñar un sistema de software, uno debe esforzarse por lograr ese equilibrio fino en los formatos de entrada utilizados donde el público objetivo puede hacer lo que necesita , sin tener que recurrir con frecuencia a terceros.
Cabe señalar que la externalización desdibuja las líneas aún más. Hay bastantes problemas, para los cuales la tecnología actual simplemente no permite que el laico pueda acceder a la solución. En ese caso, el público objetivo de la solución probablemente debería considerarse como el tercero al que se subcontrataría la operación. Se puede esperar que ese tercero emplee a un buen número de expertos.
En los idiomas en los que el código puede tratarse como datos no es un problema. Usted usa lo que es claro, breve y fácil de mantener, inclinado hacia los datos, el código, el funcional, el OO o el procedimiento, según lo requiera la solución.
En el procedimiento, la distinción está marcada y tendemos a pensar en los datos como algo almacenado de una manera específica , pero incluso en el procedimiento es mejor ocultar los datos detrás de una API o detrás de un objeto en OO.
Una lookup(avalue)
puede volver a implementar de muchas maneras diferentes durante su vida útil, siempre que se inicie como una función.
... Todo el tiempo diseñé programas para máquinas no existentes y agregué: ''si ahora tenemos una máquina que comprende las primitivas aquí asumidas, entonces el trabajo está hecho''. ... En la práctica real, por supuesto, esta máquina ideal resultará en no existir, por lo que nuestra siguiente tarea, estructuralmente similar a la original, es programar la simulación de la máquina "superior" ... Pero esto El conjunto de programas está escrito para una máquina que probablemente no existirá, por lo que nuestro siguiente trabajo será simularlo en términos de programas para una próxima máquina de nivel inferior, etc., hasta que finalmente tengamos un programa que pueda ejecutarse. nuestro hardware ...
EW Dijkstra en Notes on Structured Programming , 1969, citado por John Allen , en Anatomy of Lisp , 1978.
Normalmente, el código basado en datos es más fácil de leer y mantener. Sé que he visto casos en los que los datos se han llevado al extremo y terminan siendo inutilizables (estoy pensando en algunas implementaciones de SAP que he usado), pero codificando sus propios "lenguajes específicos de dominio" para ayudarlo a construir su software es típicamente un gran ahorro de tiempo.
Los programadores pragmáticos siguen siendo en mi mente los defensores más vívidos de escribir pequeños lenguajes que he leído. Las pequeñas máquinas de estado que ejecutan pocos lenguajes de entrada se pueden lograr mucho con muy poco espacio y facilitan las modificaciones.
Un ejemplo específico: considere un sistema de impuesto a la renta progresivo, con niveles de impuestos de $ 1,000, $ 10,000 y $ 100,000 USD. Los ingresos por debajo de $ 1,000 no están sujetos a impuestos. Los ingresos entre $ 1,000 y $ 9,999 se gravan al 10%. Los ingresos entre $ 10,000 y $ 99,999 se gravan al 20%. Y los ingresos superiores a $ 100,000 se gravan al 30%. Si escribieras todo esto en código, lo verías como sospechas:
total_tax_burden(income) {
if (income < 1000)
return 0
if (income < 10000)
return .1 * (income - 1000)
if (income < 100000)
return 999.9 + .2 * (income - 10000)
return 18999.7 + .3 * (income - 100000)
}
Agregar nuevos corchetes de impuestos, cambiar los corchetes existentes o cambiar la carga de impuestos entre corchetes, todos requerirían modificar el código y volver a compilar.
Pero si fuera manejado por datos, podría almacenar esta tabla en un archivo de configuración:
1000:0
10000:10
100000:20
inf:30
Escriba una pequeña herramienta para analizar esta tabla y haga las búsquedas (no es muy difícil, ¿verdad?) Y ahora cualquiera puede mantener fácilmente las tablas de tasas de impuestos. Si el Congreso decide que 1000 corchetes serían mejores, cualquiera podría hacer que las tablas se alineen con las tablas del IRS, y terminar con eso, no es necesario volver a compilar el código. El mismo código genérico podría usarse para un corchete o cientos de corchetes.
Y ahora, por algo que es un poco menos obvio: la prueba. El proyecto AppArmor tiene cientos de pruebas para lo que deben hacer las llamadas del sistema cuando se cargan varios perfiles. Una prueba de muestra se ve así:
#! /bin/bash
# $Id$
# Copyright (C) 2002-2007 Novell/SUSE
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, version 2 of the
# License.
#=NAME open
#=DESCRIPTION
# Verify that the open syscall is correctly managed for confined profiles.
#=END
pwd=`dirname $0`
pwd=`cd $pwd ; /bin/pwd`
bin=$pwd
. $bin/prologue.inc
file=$tmpdir/file
okperm=rw
badperm1=r
badperm2=w
# PASS UNCONFINED
runchecktest "OPEN unconfined RW (create) " pass $file
# PASS TEST (the file shouldn''t exist, so open should create it
rm -f ${file}
genprofile $file:$okperm
runchecktest "OPEN RW (create) " pass $file
# PASS TEST
genprofile $file:$okperm
runchecktest "OPEN RW" pass $file
# FAILURE TEST (1)
genprofile $file:$badperm1
runchecktest "OPEN R" fail $file
# FAILURE TEST (2)
genprofile $file:$badperm2
runchecktest "OPEN W" fail $file
# FAILURE TEST (3)
genprofile $file:$badperm1 cap:dac_override
runchecktest "OPEN R+dac_override" fail $file
# FAILURE TEST (4)
# This is testing for bug: https://bugs.wirex.com/show_bug.cgi?id=2885
# When we open O_CREAT|O_RDWR, we are (were?) allowing only write access
# to be required.
rm -f ${file}
genprofile $file:$badperm2
runchecktest "OPEN W (create)" fail $file
Se basa en algunas funciones de ayuda para generar y cargar perfiles, probar los resultados de las funciones e informar a los usuarios. Es mucho más fácil extender estos pequeños scripts de prueba que escribir este tipo de funcionalidad sin un poco de lenguaje. Sí, estos son scripts de shell, pero están tan alejados de los scripts de shell reales;) que prácticamente son datos.
Espero que esto ayude a motivar la programación basada en datos; Me temo que no soy tan elocuente como otros que han escrito sobre eso, y ciertamente no lo he hecho bien , pero lo intento.
Otras respuestas ya han investigado cómo a menudo se puede codificar el comportamiento complejo con un código simple que solo reacciona al patrón de su entrada particular. Puede pensar en los datos como un idioma específico del dominio y en su código como un intérprete (tal vez uno trivial).
Dada una gran cantidad de datos, puede ir más lejos: las estadísticas pueden impulsar decisiones. Peter Norvig escribió un gran capítulo que ilustra este tema en Beautiful Data , con texto, código y datos disponibles en línea. (Revelación: estoy agradecido en los agradecimientos). En las páginas 238-239:
¿Cómo se compara el enfoque basado en datos con un proceso de desarrollo de software más tradicional en el que el programador codifica reglas explícitas? ... Claramente, las reglas escritas a mano son difíciles de desarrollar y mantener. La gran ventaja del método basado en datos es que se codifica mucho conocimiento en los datos y se pueden agregar nuevos conocimientos simplemente recolectando más datos. Pero otra ventaja es que, si bien los datos pueden ser masivos, el código es conciso: aproximadamente 50 líneas para
correct
, en comparación con más de 1,500 para el código de ortografía de ht: // Dig. ...Otro tema es la portabilidad. Si quisiéramos un corrector ortográfico letón, las reglas del metáfono en inglés serían de poca utilidad. Para trasladar
correct
algoritmocorrect
basado en datos a otro idioma, todo lo que necesitamos es un gran corpus de letón; El código se mantiene sin cambios.
Lo muestra de manera concreta con el código en Python usando un conjunto de datos recopilados en Google. Además de la corrección ortográfica, hay código para segmentar palabras y para descifrar criptogramas: en solo un par de páginas, de nuevo, donde el libro de Grady Booch pasó docenas sin siquiera terminarlo.
"La efectividad irrazonable de los datos" desarrolla el mismo tema de manera más amplia, sin todas las tuercas y tornillos.
Tomé este enfoque en mi trabajo para otra compañía de búsqueda y creo que aún está subexplotado en comparación con la programación basada en tablas / DSL, porque la mayoría de nosotros no nadamos mucho en los datos hasta la última década o dos.
Una de las cinco máximas bajo la filosofía de Unix , presentada por Rob Pike , es esta:
Los datos dominan. Si ha elegido las estructuras de datos correctas y las cosas están bien organizadas, los algoritmos casi siempre serán evidentes. Las estructuras de datos, no los algoritmos, son fundamentales para la programación.
A menudo se reduce a "escribir código estúpido que usa datos inteligentes".