2017/02/22

Imprimir formas en consola

En un aviso de Workana encontré un pedido como este:

Escribir un programa que pregunte al usuario elegir una forma para dibujar en la pantalla.
La forma puede ser: Circle, X, Box y un Box con una X.
También el usuario indica cuántas filas se usarán para dibujar la forma.
La forma debe ser dibujada con asteriscos (*).

Me parece que se trata de un programa para correr en consola.

En mi caso, me pareció más práctico intentarlo con php.

shapes.php

<?php
do {
 print "1. Circle\n";
 print "2. X\n";
 print "3. Box\n";
 print "4. XBox\n";
 fscanf(STDIN, "%d\n", $option);
} while (!$option);

do {
 print "Number of rows: ";
 fscanf(STDIN, "%d\n", $rows);
} while (!$rows);

for ($i=0; $i<$rows; $i++) {
 for ($j=0; $j<$rows; $j++) {
  $print_dot = false;
  switch ($option) {
   case 1:
    $print_dot = is_circle($i, $j, $rows);
    break;
   case 2:
    $print_dot = is_x($i, $j, $rows);
    
    break;
   case 3:
    $print_dot = is_box($i, $j, $rows);
    break;
   case 4:
    $print_dot = is_xbox($i, $j, $rows);
    break;
  }
  if ($print_dot) {
   print "*";
  } else {
   print " ";
  }
 }
 print "\n";
}

function is_circle($i, $j, $rows) {
 $r = $rows - 1;
 $d = pow(($i - $r/2), 2) + pow(($j - $r/2), 2) - pow($r/2, 2);
 return abs($d) <= $rows/3;
}

function is_x($i, $j, $rows) {
 return ($i == $j) || ($j == ($rows-1) - $i);
}

function is_box($i, $j, $rows) {
 return ($i == 0) || ($j == 0) || ($i == $rows-1) || ($j == $rows-1);
}

function is_xbox($i, $j, $rows) {
 return is_x($i, $j, $rows) || is_box($i, $j, $rows);
}


El ejercicio me pareció interesante para ver una manera simple de tomar una entrada desde consola. Y cuando llegué a la parte de impresión de formas me pareció más interesante. En particular la del círculo, que dejé para el final.

Algunos conceptos de coordenadas y geometría analítica me ayudaron a completar el programa.

El problema del círculo es, en el fondo, cómo imprimir una imagen en un medio de baja resolución.

Esta es la forma en que se me ocurrió resolverlo, con una especie de detección de borde seteado a $rows/3, al tanteo (para que se vea bien el círculo en 4, 8 o 16 filas).

Seguramente hay muchas maneras de resolverlo. ¿Cómo lo harías tú?


2017/02/17

Invertir una cadena con javascript

¿Cómo harías una función que invirtiera una cadena dada?
Es decir que f("abcdef") sea "fedcba".


Yo pienso que no hay una "forma correcta" de resolverlo. Simplemente hay formas, y cualquiera que se te ocurra y haga el trabajo está bien.

De todas esas formas habrá algunas que logran hacerlo con menos variables o menos código, o usando técnicas esotéricas. Formas optimizadas.

Hay programadores que se familiarizan con esas técnicas optimizadas y las aplican luego directamente en los siguiente problemas que enfrentan. Puede ser impresionante, como cuando ves a un mago sacando del sombrero cosas que no creías posible. Igual que con los magos, se requiere de práctica y ensayo para lograr esas cosas. Pero, igual que con los magos, no significa que esa es la forma en que todo el mundo debería pretender hacer las cosas.

La optimización prematura es la raíz del mal, es la advertencia de Knuth, comprobada una y otra vez en la experiencia de los programadores. Está bien optimizar, por supuesto, pero tiene su momento, y definitivamente no es al inicio.

En la vida real, la solución de los problemas se produce por tanteo. Como en un cuarto oscuro, estiras los brazos y tientas. Tocas algo una y otra vez, formándote una idea de lo que hay. Así, descubres la puerta hacia la solución. Una vez que la descubriste, podrías volver al punto de partida y hacerlo más rápido, en menos tiempo o de manera más elegante. Recién ahí es que se realiza la optimización. Si intentaras salir a toda velocidad al inicio, sería muy probable que te lastimaras con algún obstáculo o fueras rápido en la dirección equivocada. También sería un poco raro hacer movimientos elegantes si ni siquiera sabes a dónde vas a ir.

Así que para este problema, quizás se te podría ocurrir algo como:

// recorrer la cadena en reversa
function f(s) {
 var result = "";
 for (var i=s.length; i>0; i--) {
  result = result + s[i-1];
 }
 return result;
}

Ok, es lo primero que se me ocurrió. Veamos si se puede hacer mejor. Quizás se pueda recorrer la cadena de otro modo:

// recorrer la cadena de frente pero acumular en reversa
function g(s) {
 var result = "";
 for (var i=0; i<s.length; i++) {
  result = s[i] + result;
 }
 return result;
}

¿Y si vemos como luce con reduce?:

// usando reduce y acumulando en reversa
function h(s) {
 return s.split("").reduce(function(result, c) {
  return c + result;
 });
}

¿Y usando la notación fat arrow?:

// usando reduce y fat arrow
function j(s) {
 return s.split("").reduce((result, c) => c + result);
}

Quizás con la práctica sea esta forma la que aparezca primero en tu cabeza en el futuro. Podría ser impresionante. Pero hay que recordar que las cosas empiezan tanteando. Y que está bien.