elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: (TUTORIAL) Aprende a emular Sentinel Dongle By Yapis


  Mostrar Mensajes
Páginas: 1 2 3 4 5 6 7 8 9 10 11 [12] 13 14 15 16 17 18 19 20 21 22 23 24 25 26
111  Programación / Programación C/C++ / Re: Duda al escribir ficheros en: 24 Marzo 2014, 17:21 pm
Mi opinión personal: Un programa como éste yo lo haría siempre en C. Si quiero compilar con un compilador de C++ cambiaría las cabeceras a <cstdio>, <cstdlib>, etc., y listo.

¿La razón? Como bien dijo un autor que respeto, C es un lenguaje intencionalmente escueto. Se maneja con un conjunto mínimo de funciones de la biblioteca estándar. Ese conjunto está reducido a ta punto que es difícil lograr que la tarea de una función la realice otra (salvo algunas excepciones de <stdio.h> como usar fgetc(stdin) en lugar de getchar()), es decir, prácticamente las funciones de su biblioteca estándar no se duplican. De hecho, no hay duplicidad en el ejemplo que cité, porque sólo existe fgetc como función de biblioteca mientras getchar() está implementado como una macro a partir de aquélla.

Yo creo que no se puede decir lo mismo de C++. El mundo en este lenguaje es inmenso. Hay clases y métodos para todos gustos, tamaños y colores. Lo cual puede ser abrumante.

Me explico mejor, supóngase que un programador principiante busca ayuda en internet sobre un código que realice determinada tarea en C. Por ejemplo: "leer un fichero de texto y escribir su contenido en otro fichero, convertiendo las palabras a mayúsculas". Si el código es "óptimo" o "correcto", y se limita a la biblioteca estándar, lo más probable es que al menos el 50% de los resultados de su búsqueda sean similares o muy similares entre sí. Es que C es tan mínimo que casi hay una sola manera "perfecta" de hacer las cosas. Cuando mucho dos.

Si buscas lo mismo en C++, creo que habrá mucha más variedad y diversidad de maneras de resolver el problema (con o sin flujos, usando o no la clase string, etc.). Entonces, el principiante de 10 resultados que busca en internet para este problema, ¡ encuentra 10 soluciones diferentes ! Y Aquí empieza a embromarse. ¿cuál es mejor, cuál es peor, por qué una y no la otra?

Simplemente uso C++ cuando se requieren clases, de otro modo para operaciones básicas de procesar cadenas, números, ficheros, E/S por consola, etc., me remito a C. No se si estas opiniones son porque me gusta llegar a la forma más simple y breve de resolver las cosas, o porque soy un romántico que decidió apegarse al pasado de C, jejeje.

En cuanto a tu programa, me permito algunas recomendaciones para perfeccionarlo:

(1) Abre el fichero que entrada en modo de solo lectura:

fichero = fopen( nombre, "r" )

con "r" en lugar de "r+".

(2) Abre el fichero de salida en modo de escritura:

fichero2 = fopen( nombre2, "w")

(3) Para leer una línea del flujo, puedes usar la función fgets(char *buffer, int n, FILE *stream). El primer argumento buffer debe ser un arreglo de char, de al menos n caracteres de capacidad. El segundo argumento es uno más que los caracteres a leer (se deja uno más, para que fgets() coloque automáticamente el nulo de terminación). El tercer argumento es el apuntador a archivo abierto. Ten en cuenta que el salto de línea '\'n se copia al arreglo sólo si tiene espacio "para caber", o sea, si la cantidad de caracteres leídos antes de él es menor a (n-1). Además, si fgets() no pudo leer (si encuentra el fin de archivo), devuelve NULL.

Te preparé un sencillo código que abre el fichero como lectura, toma sus líneas y las escribe al fichero2, además imprime por pantalla lo que va leyendo para verificar como marcha del programa.
Código
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4.  
  5. int main( ) {
  6.  
  7.   FILE *fichero;
  8.   FILE *fichero2;
  9.   char buffer[1001]; /* bufer de linea, 1000 caracteres */
  10.  
  11.   char nombre[11] = "datos.txt";
  12.   char nombre2[11]= "salida.txt";
  13.  
  14.   fichero = fopen( nombre, "r" );  // Fichero datos.txt desde el que leo los datos.
  15.   fichero2= fopen( nombre2, "w");  // Fichero salida.txt donde escribire los datos.
  16.   if ( fichero == NULL ) {
  17.      printf( "Fichero '%s' no se pudo abrir.\n", nombre ); /* error */
  18.      return -1;
  19.   }
  20.   if ( fichero2 == NULL ) {
  21.      printf( "Fichero '%s' no se pudo abrir.\n", nombre2 ); /* error */
  22.      return -1;
  23.   }
  24.  
  25.   /* Mensaje al usuario */
  26.   printf( "\nLeyendo los datos del fichero \"%s\":\n", nombre );
  27.  
  28.   /* Leer linea a linea, ponerlo en buffer y pasarlo al fichero2 convertido
  29.     * a mayusculas */
  30.  linea = 1;
  31.   while ( !feof(fichero) ) {
  32.      if ( fgets( buffer, 1001, fichero ) != NULL )
  33.         printf("Linea %d: \n\t%s", linea++, buffer );
  34.   }
  35.  
  36.   /* cerramos los ficheros abiertos */
  37.   if( !fclose(fichero) )
  38.      printf( "Fichero '%s' cerrado con exito\n", nombre );
  39.   else
  40.   {
  41.      printf( "Error: fichero '%s' NO CERRADO\n", nombre );
  42.      return -1;
  43.   }
  44.  
  45.   if( !fclose(fichero2) )
  46.      printf( "Fichero '%s' cerrado con exito\n", nombre2 );
  47.   else
  48.   {
  49.      printf( "Error: fichero '%s' NO CERRADO\n", nombre2 );
  50.      return -1;
  51.   }
  52.  
  53.   getchar();
  54.   return 0;
  55. }

Para el fichero de entrada:

Hola
Mundo
Hugo Paco Luis

el programa produce la salida:
Código:
Leyendo los datos del fichero "datos.txt":
Linea 1:
Hola
Linea 2:
Mundo
Linea 3:
Hugo Paco Luis
Linea 4:

Fichero 'datos.txt' cerrado con exito
Fichero 'salida.txt' cerrado con exito
Nota que al final imprime una línea vacía, ésto es porque el fichero está terminado en un return (un '\n'). Se podría eliminar ese defecto inspeccionando el contenido copiado al buffer. El valor de retorno de main() en caso de error es -1, que es más usual en lugar de 1.

==================
NOTAS. Una alternativa más breve a:

   fichero = fopen( nombre, "r" );
   if ( fichero == NULL ) {
   printf( "Fichero '%s' no se pudo abrir.\n", nombre );      /* error */
   return -1;
   }

es:

   if ( ( fichero = fopen( nombre, "r" ) ) == NULL ) {
   printf( "Fichero '%s' no se pudo abrir.\n", nombre );      /* error */
   return -1;
   }

112  Sistemas Operativos / GNU/Linux / Arrancar con gnome por defecto en lugar de kde en: 23 Marzo 2014, 15:16 pm
Hola a todos. Hace tiempo instalé el escritorio kde pero no me agradó. Mi pregunta es ¿qué ficheros de configuración de inicio debo modificar en Ubuntu, para que arranque con escritorio gnome por defecto, en lugar de kde?
113  Programación / Programación C/C++ / Re: Proyecto calculadora: Convertir infijo a posfijo. en: 23 Marzo 2014, 02:51 am
Gracias por el token = c !!!

Ahora dos detallitos:

(1) Si podrías hacer el código más portable, que no requiera necesariamente C++11. Yo uso Ubuntu 10.04, de 2010, y g++ no está actualizado a 2011. Tengo problemas con el "auto it", en el otro código tive que cambiar todas las expresiones de ese tipo al tipo "class<T> :: it". Quiero que sea portable porque la idea de ésto es primero usarlo como objeto académico-pedagógico en la Universidad por ejemplo, y no se si todos los estudiantes están actualizado a la última versión del software.

(2) La invocación de la conversión a número empieza cuando se detecta un carácter entre '0' y '9', no con el signo '+' ó '-'. ¿Eso no hará que por ejemplo al recibir 2 * -3, lo interprete como 2 * 3?
114  Programación / Programación C/C++ / Re: ¿Cómo crear procesos hijos en C para Windows? en: 23 Marzo 2014, 00:59 am
Windows API's: ............ Ya lo sabía: Windows es el campeón en desobedecer los estándares.

O como bien dices: Microsoft es un artista en el arte de complicarse la vida, todo lo que veas desarrollado por él suele estar complicado hasta el exceso.

Mejor digo chau al tema y me decido a seguir programando para Linux y otros que respeten POSIX, jajaja.
115  Programación / Programación C/C++ / Re: Proyecto calculadora: Convertir infijo a posfijo. en: 23 Marzo 2014, 00:24 am
Por ahora la mayor debilidad del programa anterior (inspirado en un ejercicio propuesto en un libro) es que procesa números formados por un solo dígito.

Sucede que dicho programa, aunque en una versión muy reducida, implementa funciones de un compilador. Debe pasar por una fase de análisis léxico, y una de análisis sintáctico.

El análisis léxico consiste en identificar los tokens, elementos léxicos o lexemas, que son las palabras básicas en que se escribe el lenguaje. En nuestro caso, los tokens por los momentos serían:
  • paréntesis
  • números
  • operadores aritméticos binarios: + - * / % ^
  • operadores unarios (por incluir en el futuro): sin(), cos(), exp(), log(), etc.
  • caracteres de espacio, mismos que son ignorados

El análizador léxico pasa sus resultados al analizador sintáctico o parser que en el programa que presenté es el que manejas las pilas de conversión de infijo a postfijo, y la pila de evaluación de resultados.

Lo que requiero por los momentos es una idea para construir un analizador léxico más completo, que reconozca los tokens anteriormente descritos, y sea capaz de entregarlos al parser para su procesamiento. Estos tokens pueden ser monocarácter o multicarácter. El problema mayor se presenta al reconocer los números.

Definiríamos un número como cualquier cadena de caracteres que empiece opcionalmente con un signo '+' ó '-', luego una cadena de caracteres numéricos comprendidos entre '0' y '9', luego opcionalmente un carácter de punto decimal '.', y opcionalmente una cadena de caracteres numéricos para la parte decimal. Queda a criterio del diseñador si quiere considerar expresiones en notación exponencial como 12.34E+5
Serían errores de sintaxis duplicar el carácter de signo, o el carácter de punto decimal, e.g. ++15.7, ó -18.71.3
Obsérvese que la expresión:

3 + +10

se considera correcta, porque "+10" se interpreta como "10". Si embargo, el primer "+" debe ser interpretado como símbolo de suma, mientras el segundo es un signo de positivo. Así que un mismo carácter puede tener distintos significados.

En contraposición, la expresión

3 + + 10

es incorrecta porque el segundo '+' está separado del 10 por un espacio, entonces no se considera signo de positivo, sino símbolo de suma por lo que hay doble suma.

Esta parte entonces, y para lo que invoco la ayuda de la comunidad, debe recibir una cadena en infijo, reconocer los distintos tokens según las reglas ya explicadas, y presentarlos por la salida estándar indicando su tipo y valor. Ejemplo (3.5 + 4.15) * -2^3 debe presentar:

token: (,  tipo parentesis
token: 3.5,  tipo numero
token: +,  tipo suma
token: 4.25,  tipo numero
token: ),  tipo parentesis
token: *,  tipo producto
token: -2,  tipo numero
token: ^,  tipo potenciacion
token: 3,  tipo numero

Favor código claro y legible, no construcciones crípticas. Favor abstenerse personas con una actitud orgullosa que vienen a demostrar que pueden programar de una manera tan incomprensible que sólo se entienden ellos mimos, jaja  :D

Yo mientras, trataré de trabajar en el analizador sintáctico para incorporar los operadores unarios y eliminar la cadena intermedia postfix !!!



Hola amchacon!!! Saludos como siempre!

Investigué un poco sobre el tema y parece que la manera en que ésto se resuelve más comúnmente es con pilas. De hecho, así trabajan los compiladores para traducir código fuente a lenguaje máquina, y casi aseguría que así lo hacen todas las calculadoras (lo aseguro porque es el método que enseñan todos los libros, jeje).

Pero te cuento que hace tiempo y como desconocía el método de las pilas, ideé una solución ad-hoc parecida a la tuya, ... pero como que así resulta más complicado. Fíjate que mi programa no tiene problema con uno o varios niveles de anidamiento, por ejemplo:

2*(1 + 3*( 4 + 5) )

y después de mucho bla bla te dice:
Código:
Posfijo es: 
2 1 3 4 5 + * + *

Evaluando la expresion ...
operar 4+5 = 9
operar 3*9 = 27
operar 1+27 = 28
operar 2*28 = 56
El resultado es: 56

Yo creo que incorporar los operadores unarios sin(), cos(), abs(), log(), y las macros que por fortuna vienen definidas en la propia biblioteca de C como M_PI, M_E será una buena idea y no tan difícil.

En mi caso creo que ahorita lo más difícil será analizar léxicamente la expresión para detectar números decimales, signados o no, y por eso acabo de proponer un reto a la comunidad, espero me ayudes con un aporte ...  :D
116  Programación / Programación C/C++ / Proyecto calculadora: Convertir infijo a posfijo. en: 22 Marzo 2014, 23:56 pm
Hola a todos. Mi idea a mediano plazo es construir una calculadora "en linea", o sea por consola. No funcionaría con botones, sino que el usuario debe escribir una expresión algebraica, e.g. 2 + 3*(3-6)^2 la cual será evaluada. En el futuro, incluir funciones trigonométricas, exponenciales, etc., lo cual no debe ser difícil una vez se tenga la estructura básica funcionando. Quizá hasta con la opción de graficar. Es un proyecto bonito, porque al estar desarrollado en C estándar, se pudiera incluso compilar en un microprocesador de bajo costo (compatible con C) y hacer así una calculadora científica. Claro, esto sería bien en el futuro pero con algo hay que comenzar.

El primer reto a que debemos enfrentarnos es el tema de conversión de la notación "infija a posfija". Explico a continuación (es un poco extenso, los impacientes favor ir a dar una vuelta, jeje):

Para los humanos es natural la notación 'infija', donde el operador va en medio de los operandos: "5 + 2*3". En cambio para la máquina es natural la notación 'posfija' donde primero van los operandos y luego el operador. Esto es así porque a nivel de máquina primero deben copiarse los argumentos en los registros adecuados del procesador, y luego invocar la instrucción de suma (binaria).  En posfijo, la anterior expresión se escribiría como:

5 2 3 * +

y obśervese como primero debe ejecutarse el operador de mayor prioridad *, y luego +, para cumplir adecuadamente con la asociatividad implícita 5 + (2 * 3).

Ya encontré un algoritmo que explica cómo hacer el paso de una notación a la otra usando pilas, pero más que repetirlo textualmente quisiera encontrarle la lógica. Supóngase que tenemos una expresión del tipo:

A op1 B op2 C

donde A, B, C son los operandos, mientras op1 y op2 son operadores. A menos que existan paréntesis explícitos, la asociatividad viene determinada por la precedencia de dichos operadores. Ahora, disponemos de uns string para la notación infija, un string para la posfija (que debemos ir rellenando) y una pila para ir depositando algunos elementos transitorimente.

Caso 1.  Si op1 tiene mayor precedencia que op2, ejemplo "A * B + C", primero debe evaluarse el resultado de op1, luego el de op2. Entonces, copiamos A al string de posfijo, metemos op1 en la pila, copiamos B al string posfijo, sacamos op1 de la pila y lo pasamos al posfijo, metemos op2 en la pila, copiamos C al string posfijo, sacamos op2 de la pila y lo pasamos al string. Para explicarlo más claramente en forma de tabla.

Paso    |    Pila    |    Posfijo
-------------------------------------
 1      |            |    A
 2      |     op1    |   
 3      |            |    A B
 4      |     op2    |    A B op1         <-- sacamos op1 de la pila y lo pasamos al string
 5      |     op2    |    A B op1 C       
 6      |            |    A B op1 C op2

Caso 2.  Cuando op1 y op2 tienen igual precedencia, ejemplo "A + B + C". En este caso, la asociatividad debe ser de izquierda a derecha "(A + B) + C", por lo que se evalúa primero op1 y luego op2. Se procede exactamente de la misma manera que en el caso 1.

Caso 3.  Cuando op1 es de menor prioridad que op2, ejemplo "A + B * C". Aquí debe evaluarse "A + (B * C)". Esto significa que op1 debe esperar por el resultado de op2, el string posfijo deberá quedar "A B C op2 op1". En forma de tabla:

Paso    |    Pila    |    Posfijo
-------------------------------------
 1      |            |    A
 2      |     op1    |   
 3      |            |    A B
 4      |  op1, op2  |    A B           <-- no se saca op1 al momento de colocar op2
 5      |            |    A B op2 op1   <-- vaciamos la pila

La diferencia con el caso 1 es que al momento de introducir op2 en la pila, no se saca antes op1. Esto es porque op2 tiene mayor precedencia que op1 entonces debe evaluarse primero, es decir, debe quitarse primero op2 de la pila y luego op1.

Uno puede resumir diciendo que al momento de colocar un nuevo operador en la pila, se sacan todos los operadores anteriormente depositados y que tengan mayor o igual jerarquía a la suya (y que deben evaluarse antes), estos operadores son pasados al string posfijo y el nuevo operador es depositado en la pila.

Con esta explicación precedente, se puede presentar el algoritmo para convertir infijo a posfijo. Debemos leer la expresión infija de izquierda a derecha y luego

* Los espacios son despreciados.
* Si el argumento leido es un operando (número), pasarlo al string posfijo.
* Si el argumento leido es un operador entonces:
  - Si la pila no está vacía, retirar todos los operadores con una precedencia
    mayor o igual al operador actual y pasarlos al posfijo, en el orden en que son retirados.
  - Colocar el nuevo operador en la pila.
* Si se lee un paréntesis izquierdo '(', depositarlo en la pila.
* Si se lee un paréntesis derecho ')', retirar de la pila todos los elementos hasta encontrar
  un '('. Dichos elementos son pasados al posfijo, menos el paréntesis '(' que es descartado.
* Al finalizar el proceso, pasar al posfijo cualquier elemento que haya quedado en la pila.

Entonces me puse manos a la obra. El programa está hecho en C++, que al usar clases STL se simplifica bastante (estoy pensando una versión en C puro, con código más elemental, útil quizás si se piensa migrar a un hardware más básico como un uC).

El programa recibe una cadena de notación infija y la convierte a posfija. Para propósitos de depuración se dan mensajes en pantalla sobre los pasos que van efectuando (funcionalidad que se debe quitar más adelante). Al final, evalúa el resultado y lo imprime. Por ejemplo, una cadena sencilla como:

5 + 2 * 3

produce la salida:
Código:
Intro expresion infija: 5 + 2 * 3

Analizando token: '5'
es numero: pasado a posfijo

Analizando token: '+'
es operador:
colocar '+' en la pila

Analizando token: '2'
es numero: pasado a posfijo

Analizando token: '*'
es operador:
colocar '*' en la pila

Analizando token: '3'
es numero: pasado a posfijo

Pasado operador '*' de la pila a posfijo

Pasado operador '+' de la pila a posfijo

Posfijo es:
5 2 3 * +

Evaluando la expresion ...
operar 2*3 = 6
operar 5+6 = 11
El resultado es: 11

y un caso más complicado:

5 + 3*(4 - 7)^2 - 1

produce:
Código:
Analizando token: '5'
es numero: pasado a posfijo

Analizando token: '+'
es operador:
colocar '+' en la pila

Analizando token: '3'
es numero: pasado a posfijo

Analizando token: '*'
es operador:
colocar '*' en la pila

Analizando token: '('
pasado a posfijo

Analizando token: '4'
es numero: pasado a posfijo

Analizando token: '-'
es operador:
colocar '-' en la pila

Analizando token: '7'
es numero: pasado a posfijo

Analizando token: ')'
pasado operador '-' de la pila a posfijo

Analizando token: '^'
es operador:
colocar '^' en la pila

Analizando token: '2'
es numero: pasado a posfijo

Analizando token: '-'
es operador:
pasado operador '^' de la pila a posfijo
pasado operador '*' de la pila a posfijo
pasado operador '+' de la pila a posfijo
colocar '-' en la pila

Analizando token: '1'
es numero: pasado a posfijo

Pasado operador '-' de la pila a posfijo

Posfijo es:
5 3 4 7 - 2 ^ * + 1 -

Evaluando la expresion ...
operar 4-7 = -3
operar -3^2 = 9
operar 3*9 = 27
operar 5+27 = 32
operar 32-1 = 31
El resultado es: 31

A continuación el código fuente, que es el trabajo de unas horas repartidas entre ayer y hoy:
Código
  1. #include <cstdlib>
  2. #include <iostream>
  3. #include <string>
  4. #include <stack>
  5. #include <cmath>
  6.  
  7. using namespace std;
  8.  
  9. /* Operadores matematicos */
  10. #define N_operators 6
  11. const string operators[N_operators] = {"+", "-", "*", "/", "%", "^"};
  12. int precedences[N_operators] = {1, 1, 2, 2, 2, 3};
  13.  
  14. bool is_operator( const string );
  15. int precedence( const string );
  16.  
  17. /* Mis sugerencias personales a este programa:
  18.  *
  19.  * - Considerar el cambio del nombre standby por aux_stack, u otro.
  20.  * - Incorporar el analizador lexico para expresiones numericas de punto
  21.  *   flotante, o que ocupen mas de un caracter.
  22.  * - Eliminar la cadena intermedia postfix, pasando directamente los
  23.  *   resultados de la pila de operadores extraidos de infix, a una pila
  24.  *   de evaluacion.
  25.  * - Incorporar operadores unarios, como sin(), cos(), exp(), log(), etc.
  26.  * - Detectar errores de sintaxis en la expresion infija.
  27.  * - Otros que sean recomendados.
  28.  */
  29.  
  30. int main() {
  31.  
  32. string infix, postfix, token;
  33. stack <string> standby;
  34. stack <double> result;
  35. size_t i;
  36. char c;
  37. double A, B;
  38.  
  39. /* Cadena de entrada */
  40. cout << "Intro expresion infija: ">
  41. getline( cin, infix );
  42. cout << endl;
  43.  
  44. /*************************************************************
  45.  PRIMERA PARTE: Procesar la cadena infijo, y crear posfijo
  46. *************************************************************/
  47. for ( i = 0; i < infix.size(); i++ ) {
  48. /* esto debe cambiar luego a un token o palabra devuelta por
  49. * el analizador léxico */
  50. c = infix[i];
  51. token.clear();
  52. token += c; /* parece burdo, pero no conozco mejor manera de
  53. * crear un string a partir de un unico caracter */
  54.  
  55. /* es un espacio: despreciar */
  56. if ( c == ' ' ) continue;
  57.  
  58. cout << "Analizando token: '" << c << "'" << endl;
  59.  
  60. /* es un carácter numérico: pasar al posfijo */
  61. if ( c >= '0' && c <= '9' ) {
  62. cout << "\tes numero: pasado a posfijo" << endl << endl;
  63. postfix = postfix + " " + c;
  64. continue;
  65. }
  66.  
  67. /* si se lee un operador: sacar de la pila y pasar al postfijo
  68. * todos los operadores con una precedencia mayor o igual a la
  69. * suya, y depositar el mismo en la pila */
  70. if ( is_operator( token ) ) {
  71. cout << "\tes operador:" << endl;
  72. while ( !standby.empty() && precedence( standby.top() )
  73. >= precedence( token ) ) {
  74. cout << "\tpasado operador '" + standby.top() +
  75. "' de la pila a posfijo" << endl;
  76. postfix = postfix + " " + standby.top();
  77. standby.pop();
  78. }
  79. standby.push( token );
  80. cout << "\tcolocar '" << token << "' en la pila" << endl << endl;
  81. continue;
  82. }
  83.  
  84. /* si se lee "(": colocar en la pila */
  85. if ( token == "(" ) {
  86. cout << "pasado a posfijo" << endl << endl;
  87. standby.push( token );
  88. continue;
  89. }
  90.  
  91. /* si se lee ")": retirar de la pila hasta encontrar '(', y pasar
  92. * los elementos retirados a posfijo, luego descartar el "(" */
  93. if ( token == ")" ) {
  94. while ( !standby.empty() && standby.top() != "(" ) {
  95. cout << "\tpasado operador '" + standby.top() +
  96. "' de la pila a posfijo" << endl << endl;
  97. postfix = postfix + " " + standby.top();
  98. standby.pop();
  99. }
  100. if ( !standby.empty() )
  101. standby.pop(); /* descartar el "(" */
  102. }
  103. }
  104.  
  105. /* extraer de la pila cualquier operador restante y pasarlo a la cadena posfijo */
  106. while ( !standby.empty() ) {
  107. cout << "Pasado operador '" + standby.top() +
  108. "' de la pila a posfijo" << endl << endl;
  109. postfix = postfix + " " + standby.top();
  110. standby.pop();
  111. }
  112.  
  113. /* Imprimir el posfijo */
  114. cout << "Posfijo es: \n\t" << postfix << endl << endl;
  115.  
  116. /****************************************************************
  117.  SEGUNDA PARTE: Procesar la cadena posfijo, y devolver resultado
  118. ****************************************************************/
  119.  
  120. A = 0;
  121. cout << "Evaluando la expresion ..." << endl;
  122. for ( i = 0; i < postfix.size(); i++ ) {
  123.  
  124. c = postfix[i];
  125. token.clear();
  126. token += c;
  127.  
  128. /* si se lee un operando (caracter numerico), depositar en la pila */
  129. if ( c >= '0' && c <= '9' ) {
  130. result.push( c - '0' );
  131. continue;
  132. }
  133.  
  134. /* si se lee un operador binario, poner en A y B los últimos dos argumentos
  135. * de la pila y operarlos, guardando el resultado en la pila */
  136. if ( is_operator( token ) ) {
  137. if ( !result.empty() ) {
  138. B = result.top();
  139. result.pop();
  140. }
  141. else {
  142. cout << "Argumentos insuficientes para '" << c << "'" << endl;
  143. return -1;
  144. }
  145.  
  146. if ( !result.empty() ) {
  147. A = result.top();
  148. result.pop();
  149. }
  150. else {
  151. cout << "Argumentos insuficientes para '" << c << "'" << endl;
  152. return -1;
  153. }
  154.  
  155. cout << "\toperar " << A << token << B << " = ";
  156. if ( token == "+" ) {
  157. A += B;
  158. result.push( A );
  159. }
  160. else if ( token == "-" ) {
  161. A -= B;
  162. result.push( A );
  163. }
  164. else if ( token == "*" ) {
  165. A *= B;
  166. result.push( A );
  167. }
  168. else if ( token == "/" ) {
  169. A /= B;
  170. result.push( A );
  171. }
  172. else if ( token == "%" ) {
  173. A = (int )A % (int )B;
  174. result.push( A );
  175. }
  176. else if ( token == "^" ) {
  177. A = pow(A, B);
  178. result.push( A );
  179. }
  180. cout << A << endl;
  181. }
  182. }
  183.  
  184. if ( !result.empty() )
  185. cout << endl << "El resultado es: " << result.top() << endl;
  186.  
  187. return 0;
  188. }
  189.  
  190. /* Verdadero si el token corresponde a un operador. */
  191. bool is_operator( const string token ) {
  192.  
  193. for ( int i = 0; i < N_operators; i++ )
  194. if ( operators[i] == token )
  195. return true;
  196.  
  197. return false;
  198. }
  199.  
  200. /* Devuelve la precedencia del operador descrito por el
  201.  * string token (-1 si no es un operador) */
  202. int precedence( const string token ) {
  203.  
  204. for ( int i = 0; i < N_operators; i++ )
  205. if ( operators[i] == token )
  206. return precedences[i];
  207.  
  208. return -1;
  209. }
117  Programación / Programación C/C++ / ¿Cómo crear procesos hijos en C para Windows? en: 22 Marzo 2014, 22:55 pm
Una pregunta a la comunidad. Como sabemos, el estándar POSIX provee un conjunto de métodos para interacción con el sistema operativo (crear o ejecutar procesos, conocer estados y permisos de ficheros, etc). En implementaciones de C para Linux (e UNIX en general) estas funciones están definidas en <unistd.h>. Y sabemos también que Windows es el campeón en desobedecer los estándares.

Mi pregunta es cómo hacer algo similar, por ejemplo crear un proceso hijo con fork(), a través de un programa elaborado en C para el sistema operativo Windows. Y así por ejemplo crear demonios personalizados para tareas de sistema. O también podría ser crear tuberías, comunicar procesos, etc.

La distribucion MinGW para Windows contempla algunas funciones de este tipo, por ejemplo en <dirent.h> está la función de leer directorios readdir(), y en <unistd.h> tenemos chdir() para cambiarnos de directorio. Pero no vi nada de procesos hijos, tuberías, etc.
118  Foros Generales / Sugerencias y dudas sobre el Foro / Re: Propuesta para foro, cartelera destacada y/o "salón de la fama". en: 22 Marzo 2014, 22:37 pm
La verdad no había visto el recopilatorio de sources interesantes, lo estoy revisando  :D
119  Programación / Programación C/C++ / Re: Binomio de Newton, y triángulo de Pascal en: 21 Marzo 2014, 16:54 pm
Gh057, tienes razón y me disculpo, .... pero a veces se desborda la adrenalina. Ahora, dejando las emociones negativas a un lado y concentrándome en lo positivo  :D voy a retomar el asunto, para que así no se pierdan la pasión y la sutileza porque aquí hay muchas cosas valiosas que no merecen perderse por una tontería.

Amchacón planteó que el código puede optimizarse para que no ocurra el desbordamiento. La enigmática palabra "optimizar" estuvo dando vueltas en mi cabeza desde que fue pronunciada, jajaja. Porque la verdad es que yo estaba enfocando este programa de una manera simplista y a lo "bestia".

Veamos, la fórmula del combinatorio <n,i> es:

    n!
----------
 i! (n-i)!

Dicha fórmula es algebraicamente concisa, pero numéricamente ineficiente. Analizando en sus entresijos la podemos llevar a:

 n * (n - 1) * (n - 2) * ... * (n - i + 1) * (n - i) * (n - i - 1) * ... * 1
------------------------------------------------------------------------------
            [1 * 2 * ... * i ] [(n - i) * (n - i - 1) * ... * 1]

y vemos como términos de n! se cancelan con términos de (n-i)!, quedando:

 n * (n - 1) * (n - 2) * ... * (n - i + 1)
----------------------------------------------
               1 * 2 * ... * i

y es lo que queremos.

Uno puede pensar en un "for" doble para evaluar productos, algo como:
Código
  1. for ( i = 1; i < n; i++ ) {
  2.    A = B = 1;
  3.    for ( j = i; j <= i; j++ ) {
  4.        A *= n - j + 1;
  5.        B *= j;
  6.    }
  7.    comb[i] = A / B;
  8.  
... pero repasemos aún más la situación. Escribiendo cómo va quedando el numerador para i = 1, i = 2, i = 3, ..., vemos cómo podemos reutilizar los resultados de un valor de "i" para el otro:

i=1: n
i=2: n * (n - 1)
i=3: n * (n - 1) * (n - 2)

así que podemos empezar con A = 1, y luego con cada "i" hacemos A *= (n - i + 1). Y de manera similar con el denominador. Nos ahorramos un "for". Además, aprovechamos el hecho de que la mitad izquierda de la fila es simétrica con la derecha. Nos queda un código tan simple como
Código
  1. long long * FilaTrianguloPascal( int n ) {
  2.  
  3.   int i, j;
  4.   long long A, B, * fila;
  5.  
  6.   if ( ( fila = (long long *) malloc( (n + 1) *sizeof(long long) ) ) == NULL )
  7.      return NULL;
  8.  
  9.   fila[0] = fila[n] = 1;
  10.  
  11.   A = 1; /* numerador */
  12.   B = 1; /* denominador */
  13.   for ( i = 1; i < n; i++ ) {
  14.      /* mitad izquierda de la fila */
  15.      if ( i <= n / 2 ) {
  16.         A *= n - i + 1;
  17.         B *= i;
  18.         fila[i] = A / B;
  19.      }
  20.      /* mitad derecha */
  21.      else
  22.         fila[i] = fila[n - i];
  23.   }
  24.  
  25.   return fila;
  26. }
que probé y produce los resultados correctos, por ejemplo para n=18:

1 18 153 816 3060 8568 18564 31824 43758 48620 43758 31824 18564 8568 3060 816 153 18 1

¿Más o menos así era como se quería?
120  Foros Generales / Sugerencias y dudas sobre el Foro / Propuesta para foro, cartelera destacada y/o "salón de la fama". en: 21 Marzo 2014, 15:40 pm
Buenos días moderadores, la presente es para proponer la siguiente idea para el foro. Habilitar una especie de Salón de la Fama para los temas más destacados. Por ejemplo he visto varios aquí en el foro de C/C++ que merecerían estar allí.

Temas que sobrepasen una simple duda o consulta, y más bien sean aplicaciones desarrolladas entre los distintos miembros de la comunidad, funcionales, depuradas y libres de errores, con sus respectivos y debidos comentarios, ficheros de cabecera, su código claro y legible, macros #define para hacerlo configurable e incluso capacidad para compilarse en varios sistemas, quizá su Makefile, etc., .... en fin algo completo y refinado.

Sería una manera de recompensar a los autores por tan prolijo trabajo, que no tiene que ser extenso, pero sí bien pulido y acabado, y por otra parte enriquecería considerablemente el foro con esos aportes.

Sería bonito, nos repartimos el trabajo entre varios del foro, cada quién hace una parte y luego la ensamblamos. Sería como para abrir una sección de temas y proyectos abiertos en el foro.

Es sólo una propuesta para ser considerada ..... Yoel.
Páginas: 1 2 3 4 5 6 7 8 9 10 11 [12] 13 14 15 16 17 18 19 20 21 22 23 24 25 26
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines