¿Cuál es la forma más rápida de convertir los elementos de una lista de números en caracteres? Este desafío es una extensión de la pregunta que se encuentra aquí en la Comunidad. Decidí probar ocho enfoques: tres métodos iterativos ( For , For Each y Transform Each ), cuatro métodos no iterativos (operaciones de tabla, expresiones, Words + expresiones y Regex ) y un híbrido ( Words + Transform Each ). En la publicación vinculada se consideraron cinco de estos métodos, aquellos que no involucran expresiones regulares o expresiones. Los métodos que utilizan Words y Regex son un poco engañosos porque explotan un efecto secundario de las operaciones de cadenas: específicamente, cualquier estructura de datos puede convertirse en una cadena de caracteres y analizarse en consecuencia. Dejaré a criterio del lector si considera que este enfoque es legítimo. ¡Conozco a algunas personas que lo desaprobarían!
Utilizando un diseño que llena el espacio, analicé cuatro factores: número de listas, número de elementos de la lista, si las listas tenían un número igual o desigual de elementos y tipo de número: entero o flotante. El diseño está en Lista de listas – Ejecuciones experimentales.JMP . Se han redondeado los valores fraccionarios de la generación del diseño original. Se realizaron diez repeticiones para cada ejecución. El código que utilicé para ejecutar el experimento está en Ejecutar lista de listas experimento.JSL y los resultados en Lista de listas resultados.JMP. Las ejecuciones preliminares con Regex fueron extremadamente lentas y se eliminó de consideración.
Para comparar métodos, calculo el orden de clasificación de cada método por ejecución y replicación y la diferencia entre el tiempo de ejecución y el tiempo de ejecución medio para todas las replicaciones dentro de una ejecución experimental. Cuanto más pequeño, mejor. Los gráficos a continuación sugieren que Transform Each , las operaciones de tabla y los métodos de Words fueron superiores. Si bien la diferencia de tiempo real es pequeña (los valores están en microsegundos), estos métodos siempre tienen una clasificación más alta y tienen menos variabilidad.


Para ver si había diferencias entre los cuatro métodos, volví a ejecutar el experimento con listas más grandes ( Lista de listas grandes - Ejecuciones experimentales.JMP y Lista de listas grandes resultados.JMP ). Al observar las diferencias de tiempo con respecto al tiempo medio dentro de la ejecución, parece que a medida que aumenta el número total de elementos, las operaciones de Transformar cada uno y de tabla funcionan mejor. Este es un resultado afortunado, ya que Transform Each se acaba de introducir en JMP 16 , y las operaciones de tabla proporcionan una buena solución para aquellas personas que todavía están en JMP 15.

Para el segundo desafío, te pedí que transpusieras una lista de listas. Una lista de listas transpuesta recopila todos los primeros elementos de las listas de entrada en la primera lista de salida, los segundos elementos de entrada en la segunda lista de salida, etc. Si la entrada tuviera n listas, entonces cada lista de salida tendría n elementos. El número de listas de salida sería igual al máximo del número de elementos en cada lista. Si bien el número de listas de entrada es fácil de determinar, el número de listas de salida requiere contar los elementos de cada lista de entrada. ¿Es esto necesario? ¿Conduce a un proceso más rápido?
Para responder a esta pregunta y probar métodos específicos, ejecuté otro diseño de relleno de espacio. En él, analicé tres métodos iterativos ( For , For Each y Transform Each ), cada uno de los cuales utiliza dos enfoques: uno calcula el número de listas de salida antes de generar la salida, lo que requiere realizar un bucle sobre la entrada dos veces, y el otro crea la salida utilizando un solo bucle sobre la entrada. Además, analicé el uso de operaciones de tabla. Se consideraron seis tipos diferentes de elementos de lista (entero, flotante, 1 carácter, 8 caracteres, 15 caracteres y carácter de ancho variable (longitud máxima 15), así como longitudes de lista fijas y variables (internas).
Durante mi codificación inicial, descubrí que es más rápido crear la lista de salida comenzando con una lista vacía y usando Insertar en en lugar de completar previamente la salida con valores faltantes usando Repetir o expresiones. Puede utilizar el código en Prebuild o Insert.JSL para comprobarlo usted mismo. Un segundo descubrimiento tuvo que ver con Transform Each y For Each . Si no está familiarizado con estas funciones, se comportan de manera un poco diferente a su bucle For o While estándar en el sentido de que le permiten iterar sobre listas, matrices o matrices asociativas, sin tener que especificar su tamaño. Esto puede hacer que el código sea un poco más compacto y limpio. La estructura de una llamada básica a cualquiera de estas funciones es
fnName ({ indValue , ind }, contenedor , cuerpo )
donde fnName es For Each o Transform Each , indValue es el valor del contenedor en el índice ind , el contenedor es una matriz, lista o matriz asociativa y body es el cuerpo del código. Se debe tener cuidado al trabajar con contenedores grandes. El uso de grandes estructuras de datos como contenedor o indValue ralentizará considerablemente la ejecución. Se proporciona un ejemplo de esto en Uso de For Each y Transformar Each.JSL . En él, contamos la cantidad de elementos en cada lista de una lista de listas. Los primeros dos bloques de código corresponden a cómo se podría esperar codificar el problema: el primero utiliza cada sublista como valor de entrada y el segundo indexa las sublistas. Compare esto con el tercer bloque donde los índices de una Lista se crean y utilizan explícitamente en el bucle. Dependiendo del tamaño de aList , las mejoras en la velocidad de procesamiento pueden ser 100 veces o mejores. Aún más rápido es Transform Each , lo que le permite devolver los valores resultantes. Los bloques 4 y 5 muestran que la estructura de datos de salida será la misma que la estructura de datos utilizada para el contenedor de entrada. Hay un argumento Output() opcional para controlar el tipo de estructura de datos de salida.
El código para probar los diferentes métodos de transposición se encuentra en Transpose listas.JSL . Como se indicó anteriormente, se utilizó un diseño de relleno de espacio para comparar la producción en diferentes condiciones. A diferencia del código anterior, se crea una nueva estructura de datos para cada repetición. Los primeros tres métodos leen la entrada y construyen la salida en un solo bloque. Los tres siguientes predeterminan primero el tamaño de las listas de salida, antes de procesar la entrada. El penúltimo método utilizó operaciones de tabla y el último Transformar cada uno, estructurado para usar sus valores de salida.

Predeterminar el tamaño de las listas de salida resultó ser más rápido que intentar hacerlo en un solo bloque. Las diferentes implementaciones de Transform Each muestran las mejoras de velocidad obtenidas al trabajar con sus valores de salida. Los resultados están en Transpose results.JMP . Se puede encontrar un experimento con listas más grandes que comparan For Each , Transform Each ( segunda implementación) y operaciones de tabla en Transpose results big.JMP . Los resultados sugieren que las operaciones con tablas, en general, superan a los otros dos enfoques.

Para resumir lo que hemos descubierto:
- En el tiempo: transformar cada < para cada < para
- Las operaciones con tablas pueden ser súper rápidas si no intervienen fórmulas de columnas. En publicaciones anteriores vimos que, dependiendo de su complejidad, las fórmulas de columna pueden ralentizar las operaciones hasta el punto en que la iteración o las expresiones sean favorables.
No recibí los comentarios típicos para este desafío que he tenido en el pasado, así que estoy seguro de que hay un mejor enfoque. Me encantaría escuchar tus comentarios. Busque un nuevo desafío el próximo mes.