empezando por el principio

Como en programancia101 estamos solo empezando, creo que lo más apropiado es empezar por el principio, pero claro, esto sería si hubiera un principio.


Lo ideal sería poder identificar cuál es la habilidad más importante que debe tener un desarrollador y decir: «¡aha! éste es el truco y éste es el problema que necesitáis para afinar la capacidad X». Pero esto creo que es un problema bastante difícil. ¿Cuál creéis vosotros que debería ser la habilidad número uno del buen programante?


Yo he sido incapaz de decidirme sólo por una, pero sí que he visto una actitud común en muchos programantes avezados: no se conforman con saber que algo funciona, sino que quieren saber por qué funciona, o por qué no funciona, o por qué funciona mejor en tal o cual caso. Saber este tipo de cosas está directamente relacionado con la curiosidad por conocer cómo maneja cada concepto «por dentro» nuestro lenguaje favorito, nuestro entorno, nuestro marco de trabajo…


Una de las típicas preguntas que suelen hacerse cuando se quiere comprobar qué tal maneja alguien los conceptos desde el principio es: ¿sabes darle la vuelta? Sí, exactamente, darle la vuelta a las cosas es un procedimiento poco usual en la «programación estándar», pero nos viene al pelo para empezar a proponer el que se vean las cosas de un modo distinto



Vamos a plantear esta pregunta a distintos niveles:



  • la idea más básica sería: ¿sabrías escribir un trozo de código que reciba una cadena de texto y le dé la vuelta? Es decir, si nuestra entrada es «abc», la salida sería «cba»
  • Bajando un poco más dentro de nuestros tipos de datos: ¿y un trozo de código que le de la vuelta a los bits de un byte? Es decir, si la entrada es 1, la salida sería 128
  • Y para terminar, uno un poco más complejo: en C# (y muchos otros lenguajes) los tipos flotantes se representan mediante el estándar aritmético IEEE 754. ¿Sabrías escribir un trozo de código que le de la vuelta a los bits del exponente y la mantisa de esa representación IEEE 754? Es decir, si la entrada es -118,625, la salida sería -17.180.580.000 (nota: en español la coma «,» es decimal y el punto «.» el separador cada 3 números de la parte entera)

Podéis poner vuestras respuestas, preguntas o ideas en los comentarios o bien poner un post con la solución en vuestro propio blog y dejar aquí un enlace (¡es mejor que mandármelas por mail, porque así todos podemos opinar!)


Preparados, listos… ¡ya! }:)

22 comentarios sobre “empezando por el principio”

  1. Bueno, ya que nadie se lanza, pongo mi parte para las 2 primeras preguntas, la última me la tengo que empollar 😉

    Para la 1ª, podría ser algo así:
    void reverseStringBuffers(char* inputString, char* outputString)
    {
    int len = strlen(inputString);
    for(int i=len-1; i >= 0; i–)
    {
    outputString[len – i -1] = inputString[i];
    }
    }

    Para la segunda, algo de este estilo (programa completo):

    #include «stdafx.h»
    #include

    using namespace std;

    int _tmain(int argc, _TCHAR* argv[])
    {
    unsigned char input = 0;
    short inputNumber = 0;
    short output = 0;

    cout < < "Enter a number:" << endl; cin >> inputNumber;
    input = (unsigned char)inputNumber;

    for(unsigned char i=0; i<8; i++)
    {
    // Mask to the i bit
    unsigned char mask = (1 << i); unsigned char masked = input & mask; if(masked != 0) { // bit i is set output += 1 << (7 - i); } } cout << "Output: " << output << endl; return 0; } Sin verificaciones adicionales que habría que hacer de entradas, claro...

  2. Yo pongo mi granito de arena con Visual Basic para el 1, el resto las estoy procesando…

    Public Function ReverseString(ByVal String As String)
    Dim OutString As String = «»
    For I As Integer = 0 to String.Leght – 1
    OutString = String.Substring(String.Leght – i, 1) & OutString
    End For
    Return OutString
    End Function

    Si hay algún error avisad 🙂

  3. Ala! Pues ya lo aviso yo, esto me pasa por no probarlo antes de enviarlo 🙂

    Public Function ReverseString(ByVal InString As String)
    Dim OutString As String = «»
    For I As Integer = 0 To InString.Length – 1
    OutString &= InString.Substring(InString.Length – 1 – I, 1)
    Next
    Return OutString
    End Function

    Ese es el código correcto, ahora ya está comprobado que funciona.

  4. Ala! Pues ya lo aviso yo, esto me pasa por no probarlo antes de enviarlo 🙂

    Public Function ReverseString(ByVal InString As String)
    Dim OutString As String = «»
    For I As Integer = 0 To InString.Length – 1
    OutString &= InString.Substring(InString.Length – 1 – I, 1)
    Next
    Return OutString
    End Function

    Ese es el código correcto, ahora ya está comprobado que funciona.

  5. Hola Ricardo,

    Para mi la cualidad principal que debe de tener un buen programador no es diferente a la de cualquier otra persona que se enfrenta a retos diarios en su trabajo. La mejor cualidad en un programador es, sin duda, que sepa hacer buenas preguntas a las personas que tengan las respuestas, así de sencillo. Claro que hay muchas cualidades más técnicas que tener en cuenta pero creo que todas esas se suman a la principal. Lo realmente importante es no perder la capacidad de comunicarse y afrontar en equipo, cada uno asumiendo su rol, cualquier problema al que se les ponga delante.

    Saludos.

  6. Bueno ahora entro a Geeks.ms y me topo con la noticia de que este chico maravilla (como dijo Unai) de la programacion esta entre nosotros.

    Bienvenido TITAN!!!

    Saludos desde Rep. Dom. [:D]

  7. Esto no me lo esperaba (ni siquiera me lo había planteado) pero el código para el caso 2 en VB.NET es una traducción directa del de C++…

    Sub Main()
    Console.WriteLine(«Enter a number (0-255):»)
    Dim characters As String = Console.ReadLine()
    Dim byteNumber As Byte = Byte.Parse(characters)
    Dim output As Byte

    For i As Byte = 0 To 7
    ‘ Mask to the i bit
    Dim mask As Byte = 1 << i Dim masked As Byte = byteNumber And mask If (masked) Then ' bit i is set output += 1 << (7 - i) End If Next Console.WriteLine("Resultado: " + output.ToString()) Console.ReadLine() End Sub 😉

  8. buenas crases!

    Muchísisimas gracias por vuestro tiempo! A los que ya se han animado a probar, espero que el problema os pareciera interesante y/o que hayáis repasado o aprendido algo nuevo en el proceso! A los que no… a qué esperáis! }:D

    Miguel: te he dejado una pistilla sobre donde te has quedado atascado en tu blog

    Eugenio: ves? si yo siempre te digo que no me gusta VB };P … Peeero, por respeto a mis compis MVPs (encabezados por el gran Guille), mis comentarios:
    a) usar substring es un poco matar moscas a cañonazos, prueba a ir directamente por los caracteres de «alguna de las otras formas»
    b) palabras-pista a las que «puedes» echar un ojo: String.toCharArray, String.Chars, System.Text.StringBuilder

    Alejandro: first post! Tengo que ver que te puedo dar de premio por ser el primerísimo que acepta el reto! }:)
    De momento: fantástica la idea de usar C/C++. Mis comentarios aquí debajo:
    a) Sobre darle la vuelta a la cadena: tienes un algoritmo de O(n)… serías capaz de hacerlo en O(n/2)? };)
    b) Sobre darle la vuelta a un byte, te propongo dos pequeñas variaciones sobre la idea (que es correcta): una, darle la vuelta usando el mismo byte de entrada; dos, cuando el bit i-ésimo está a 1 (// bit i is set)… hay algo mejor que una suma?

    Seguiré pendiente de nuevas soluciones, ya sea por comentarios o por crosslink a vuestros blogs };D

  9. Bueno, seguro que hay mjores maneras de hacerlo, a pero esta ha sido la primera intentona y ha funcionado 🙂

    public float TestFloat(float f)
    {
    Byte[] bits;

    bits = BitConverter.GetBytes(f);
    BitArray ba = new BitArray(bits);
    BitArray ba2 = new BitArray(32);

    Console.WriteLine(«Source»);

    for (int i = 0; i < 32; i++) { Console.Write("{0}", (ba[i] == true ? 1 : 0)); } Console.WriteLine(); // sign ba2[31] = !ba[31]; // exp for (int i = 0; i < 8; i++) { ba2[30-i] = ba[23+i]; } // mantissa for (int i = 0; i < 23; i++) { ba2[22-i] = ba[i]; } Console.WriteLine("Result"); for (int i = 0; i < 32; i++) { Console.Write("{0}", (ba2[i] == true ? 1 : 0)); } Console.WriteLine("nFloat Result"); ba2.CopyTo(bits, 0); float ff = BitConverter.ToSingle(bits, 0); Console.WriteLine(ff); return ff; } Luego si me da tiempo lo optimizo 🙂 Saludos,

  10. Buenas!

    Para dar la vuelta a un string… Una pila sería lo más sencillo… Pero sería 2 * O(n) = O(n).

    Una pista para dar la vuelta a los bits de un byte en O(n/2)… Divide y vencerás… 😉

  11. Gracias Ricardo, jejeje la verdad no me lo pare a pensar mucho, fue lo primero que se me ocurrio hablando con unos amigos por el messenger.

    Que conste que me gusta más C# (lo veo más util, no voy a exponer mis razones ya que no quiero empezar con la típica polémica VB vs. C#) pero… llevo más tiempo con VB y de momento me desenvuelvo mejor en él (por poco tiempo :P).

    Saludos y gracias por tus consejos, que sepas que voy seguir tu blog como fan nº 1. Felices fiestas!!!

  12. Después de empollarme como funciona el IEEE 754 aquí tengo la respuesta del tercer problema, en un programita completo en C++:

    #include «stdafx.h»
    #include

    using namespace std;

    int reverseInt(int Int);
    void printBits(int Int);

    union IEEESingle
    {
    float valuef;
    int valuei;
    };

    int _tmain(int argc, _TCHAR* argv[])
    {
    float *input = new float;
    *input = -118.625F;

    IEEESingle inputValue;
    inputValue.valuef = *input;

    // Signo
    unsigned int signo = (inputValue.valuei & 0x80000000) == 0x80000000 ? 1 : 0;

    // Exponente
    int exponente = ((inputValue.valuei >> 23) & 0x000000ff);
    // Exponente normalizado
    int exponenteNormalizado = ((inputValue.valuei >> 23) & 0x000000ff) – 127;

    // Fraccion
    unsigned int fraccion = inputValue.valuei & 0x007fffff;

    // Invertir exponente
    int reversedExponent = ((reverseInt(exponente) >> 24)) & 0x000000ff;

    // Invertir fracción, shift 32-23 bits right
    int reversedFraction = ((reverseInt(fraccion) >> (32-23)) & 0x007fffff);

    // Componer la salida
    int singleOutput = (signo < < 31) + (reversedExponent << 23) + reversedFraction; IEEESingle outputValue; outputValue.valuei = singleOutput; cout << " Input bits: "; printBits(inputValue.valuei); cout << endl; cout << "Output bits: "; printBits(outputValue.valuei); cout << endl; cout << "Valor: " << outputValue.valuef; delete input; return 0; } void printBits(int Int) { int mask; int masked; for(int i=31; i>=0; i–)
    {
    mask = (1 << i); masked = Int & mask; if(masked != 0) { // bit i is set cout << "1"; }else { cout << "0"; } if(i == 31) cout << " "; if(i == 23) cout << " "; } } int reverseInt(int Int) { int mask; int masked; int output = 0; for(int i=0; i<32; i++) { mask = (1 << i); masked = Int & mask; if(masked != 0) { // bit i is set output += 1 << (31 -i); } } return output; } El 'truco' del union es lo que me ha salvado aquí...

  13. En cuanto a las modificaciones que dices, para O(n/2) podría ser algo así:

    void reverseStringBuffers(char* inputString, char* outputString)
    {
    int len = strlen(inputString);
    for(int i=len-1; i >= 0; i -= 2)
    {
    outputString[len – i -1] = inputString[i];
    if(i > 0)
    outputString[len – i] = inputString[i-1];
    }
    }

    La otra la miro cuando tenga un rato 😉

  14. Esta versión es mejor:

    void reverseString2(char* inputString)
    {
    int len = strlen(inputString);
    for(int i=0; i < len/2; i ++) { // Guardar el char en el null char final. inputString[len] = inputString[i]; inputString[i] = inputString[len-i-1]; inputString[len-i-1] = inputString[len]; } // Restablecer el null char final inputString[len] = ''; }

  15. Mi Reverse de cadenas… (el truco del XOR lo vi en el blog de Eugenio, creo…)

    void reverseString(char strToReverse[])
    {
    int len = (int) strlen(strToReverse) – 1;

    for(int i = 0; i < len; i++ , len--) { strToReverse[i] ^= strToReverse[len]; strToReverse[len] ^= strToReverse[i]; strToReverse[i] ^= strToReverse[len]; } } ...y uno curioso: Esto lee de teclado hasta que se introduzca un asterisco, sin usar variables: getchr() { c=getch(); if(c != '*') getchr(); putchar(c); }

  16. Alejandro: el null char final no hace falta tocarlo, las dos cadenas tendran el ‘’ en el mismo sitio… basta con usar los indices apropiados (pero, si haciendo el intercambio es como se hace en O(n/2)

    Augusto: en la funcion getchr() que has puesto (que es un buen ejemplo de recursividad… aunque de eso ya hablaremos en algun otro problema };D), no entiendo como dices que lo hace sin variables… No deberia ser c una variable de tipo char?

Responder a phobeo Cancelar respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *