Últimamente me ha dado por “entrenarme” o “entretenerme” con la plataforma www.codewars.com, una herramienta especialmente útil para yo diría que cualquier programador, se basa en resolver, contemplando buenas prácticas a ser posible, pequeños (o grandes) problemas a partir de unas instrucciones, parámetros de entrada y datos esperados para la salida. Puedes elegir el lenguaje y el nivel de dificultad y la herramienta te buscará cientos de estos pequeños problemas por los que podrás ir ganando “puntos” y mejorando tu perfil poco a poco.
¿Qué mejor forma de dejar patente que eres capaz de resolver ciertos problemas de una forma programática correctamente que resolviendo Katas en codewars? Pocas…
Puedes ver la forma en que lo han resuelto otras personas (y esto es lo mejor) sólo cuando lo has logrado conseguir, así puedes comparar tu solución con la de medio mundo. Estas soluciones además son votadas y la mejor o más votada la verás siempre la primera.
Metiéndonos en el meollo de la cuestión he decidido resolver un Kata de nivel 5 que planteaba ser un conversor de decimal a hexadecimal de valores RGB. Se trataba de implementar una función (en C# en este caso) que tomando 3 parámetros enteros como valores RGB, devolver una cadena formateada en hexadecimal. Las reglas de partida serían que los valores que se saliesen de los extremos de 0-255 debían contemplarse como el valor correcto más cercano y que la cadena de salida debía tener el forma fijo de salida de 6 caracteres (Ej: #00FFFF, #FFFFAA, etc..)
El Kata
Bien, que opciones tenemos para convertir enteros a hexadecimal y ahorrarnos tener que pensar cómo funciona la conversión entre estos dos sistemas… pues tienes si, si te has leído la documentación de C#, una opción realmente fácil se trata de usar el método de extensión de string llamado Format para definir la cadena que nos permite convertir a hexadecimal además indicando el número de caracteres que quieres que conformen el resultado, en este caso X2, de esta forma tendríamos por ejemplo
var n = 148;
string hex = n.ToString(“X2”); //resultado = 94
usando este método por cada valor de entrada lo tenemos resuelto.
Bien, lo hemos conseguido de una forma rápida y fácil, para eso está estudiar un poco ya que nos puede ahorrar muuuuchas horas de trabajo.
Alguna de las soluciones que han propuesto otras personas en una sóla línea de código es esta
La función Clamp valida fácilmente que se cumpla el rango válido y después se aplica directamente la conversión de X2
O esta otra que es la más votada
Usando Max y Min igualmente para contemplar el rango , finalmente se formatea todo de nuevo usando X2
A alguno se le ha ido un poco más la pinza y se ha “currado” un array de 255 valores posibles…claro así solo tengo que aplicar el numero recibido al array y me dice cual corresponde….pero criatura, cuando tengas que convertir a hexadecimal cualquier numero decimal vas a hacer un array de ?….
Me gustan mucho las soluciones que implementan Linq pues son relativamente fáciles de entender, manejar Linq nivel master te permite realizar consultas y operaciones de una forma muy versátil que de otra forma suponen decenas de líneas de código. En este caso tenemos una solución al Kata con Linq
Eh…alguno también se ha acordado que hay métodos en algún ensamblado que te convierten los tres valores enteros a formato color HTML (hexadecimal) pues listo…engancho el ensamblado y tira millas. 😊
Todas las soluciones dan el resultado por bueno…independientemente de cómo lo resuelvas, pero creo que por coherencia, sentido común, y solidaridad con quien pueda ver el código después, la solución a un problema siempre debería ser la que incluya estas reglas.
- Eficiencia
- Reutilización
- Calidad
- Claridad
Saber usar estas funciones rápidas es muy bueno por eficiencia, pero como sabemos, la operación real de conversión entre diferentes sistemas numéricos se realiza usando la base del propio sistema numérico, en este caso 16 así que vamos a darle una vuelta más elaborada, como vemos resumida en la siguiente imagen.
Esto esta mas entretenido…
Vemos que tomando el resto de las divisiones por 16 realizadas al valor a convertir y cogiendo el último cociente podemos componer la conversión final.
Y esta ha sido mi solución
public static string Rgb(int r, int g, int b)
{
//creamos un diccionario con correspondencias por letras en //hexadecimal, cuando un numero decimal sea mayor que 9 acudiremos //aquí para saber que letra corresponde
Dictionary<int,string> HEX = new Dictionary<int,string>();
HEX.Add(10,"A");
HEX.Add(11,"B");
HEX.Add(12,"C");
HEX.Add(13,"D");
HEX.Add(14,"E");
HEX.Add(15,"F");
//aquí controlamos los rangos válidos, (había formas mejores)
r = r > 255 ? 255 : r;
g = g > 255 ? 255 : g;
b = b > 255 ? 255 : b;
r = r < 0 ? 0 : r;
g = g < 0 ? 0 : g;
b = b < 0 ? 0 : b;
//y aquí empezamos a calcular
List<int> rgb = new List<int>(){r,g,b};
string resultado = string.Empty;
//como necesitamos repetir el mismo proceso para cada valor entero que //recibamos, pues aunque sean solo 3 los metemos en un array lo //recorremos
foreach(var n in rgb)
{
string patron1 = "0";
string patron2= "0";
if(n>0)
{
var resto = n%16;
var cociente = n/16;
patron1 = resto > 9 ? HEX[resto] : resto.ToString();
patron2 = cociente%16 > 9 ? HEX[cociente%16] : cociente.ToString();
}
resultado += $"{patron2}{patron1}";
}
return resultado;
}
Esta ha sido mi solución desde luego no es la mejor, pero es clara y eficaz.
PD: usa codewars para entrenarte o sólo como un reto para comparar tus soluciones con las del resto de Warriors .. aprenderás fijo y veras otros puntos de vista al aplicar tus soluciones.
Hasta el próximo Kata!