[ Foro de Pascal ]

Pasar variables por valor y referencia. Dos alternativas para ficheros de texto

22-Jun-2011 16:24
Fulanito de Tal
4 Respuestas

Hola. Tengo un par de preguntas para hacer al foro. Las dos son bastante teóricas, aunque la segunda tiene implicaciones prácticas para mí.

La primera más que una pregunta es un tema para debatir y opinar. Cuando se declara un procedimiento hay que especificar mediante la palabra "Var" que la variable la vamos a pasar por referencia. Pero en teoría habría otra forma de hacerlo: no especificar nada cuando se declara el procedimiento, y más adelante en el cuerpo del programa principal cada vez que se hace una llamada al procedimiento especificar si la variable la pasamos por valor o por referencia. Así podríamos pasar la variable unas veces de una forma y otras veces de otra, según nos convenga en cada momento. Me parece que algunas versiones de Basic funcionan así. Me imagino que la razón es que en Pascal hay que especificarlo cuando se declara el procedimiento para que el programa sea más robusto o más seguro, pero creo que deberían dejar al programador la opción de elegir una forma u otra.

La segunda pregunta tiene que ver con manejo de ficheros. Estoy haciendo una agenda donde los datos se guardan en un fichero de texto. En cada fila se escribe una "ficha" con los diferentes campos (nombre, ciudad, etc.) separados por una tabulación. Así los campos pueden ser de longitud tan larga como quiera, puedo editar el fichero "a mano" y añadir o quitar cosas, etc. Además los ficheros de texto ocupan menos espacio en disco que los binarios. Y si quiero escribir una nueva agenda utilizando otro lenguaje de programación puedo leer el mismo fichero que he creado con la agenda escrita en pascal, y viceversa.  En fin, que me parecen más manejables los ficheros de texto que los ficheros binarios. Pero tienen varios inconvenientes, el mayor que me he encontrado es que para escribir "en medio" de un fichero, por ejemplo para corregir un dato, hay que ingeniárselas y ponerse a programar, no hay ninguna instrucción que lo haga directamente, cosa que sí ocurre en un fichero binario. Ese tema y otros similares los resolví y la agenda está casi terminada, sólo me quedan detalles.
La cuestión que me surge es la siguiente. Para construir una ficha, es decir, para leer una linea del fichero y separar los distintos campos, el programa crea un vector de strings, con tantos elementos como campos tenga la linea, en este caso concreto son 6 campos. Primero la línea se guarda en la variable "linea", luego se va leyendo caracter a caracter hasta que encuentra un tabulador, entonces ese elemento del vector ha terminado y empieza a construirse el siguiente elemento. Así se va haciendo con todas las líneas del fichero. La pregunta entoces es: ¿no sería mejor poner un campo en cada línea, y de esta forma en la variable "linea" se almacenaría directamente el dato, sin tener que construirlo caracter a caracter? El programa sería más rápido y sencillo, pero los ficheros serían mucho más largos. Ese es el dilema que tengo que resolver. ¿Qué se suele usar en la "vida real" cuando se hacen programas con ficheros de texto?
Gracias de antemano al foro, y especialmente a los profesores Antonio y Nacho por el tiempo que dedican a difundir este lenguaje. Y como siempre pido disculpas porque tengo tendencia a enrollarme y no parar de hablar.

Adjunto un fragmento del código donde se construye el vector "ficha" para cada línea del fichero. Es obvio que antes se ha abierto un fichero para lectura, y que después se cierra, y que se ha leído una línea del mismo y almacenado en la variable "linea".


n := 1;

ficha [n] := '';

for bucle :=  1 to length (linea) do
  begin
  if linea [bucle] <> chr(9) then
     begin
     ficha [n] := ficha [n] + linea [bucle];
     end
  else
     begin
     n := n + 1;
     ficha [n] := '';
     end;
  end;

Añado otro dato. Como sospechaba que este programa iba a ser muy lento para leer ficheros muy grandes, construí un fichero enorme con un millón de líneas, y en cada línea 10 campos, cada uno de ellos con un número aleatorio de caracteres entre 15 y 30. El resultado fue que para leer todo el fichero el programa tardó unos 8 segundos. No creo que llegue nunca a tener una agenda con un millón de contactos, por lo cual me parece que el programa tal y como lo tengo me vale perfectamente pero tengo la duda si no sería mejor un campo en cada línea. ¿Cuál de los dos sería la forma más elegante o más eficiente de programar la agenda?
¡¡Gracias y hasta la próxima!!


24-Jun-2011 17:29
Nacho Cabanes (+84)

Con relación a tu primera pregunta, sobre si pasar variables por parámetro o por referencia:

- En la gran mayoría de lenguajes actuales (no recuerdo ninguno que no lo haga), no puedes escoger: o bien has previsto que se pasen por valor o por referencia cuando has definido la función, y usas esa misma política cuando llamas a esa función (o procedimiento).

Y en ese caso, la forma de decidir sale a ser ésta:

* Si es un parámetro cuyo valor hay que leer desde dentro de la función, pero en el que no es necesario hacer cambios, o si se hacen no es necesario guardarlos, se pasa por valor (lo "normal").

* Si hay que devolver un valor, se puede hacer como valor de retorno de la función (lo habitual), o pasando ese parámetro por referencia (más rebuscado en general).

* Si hay que devolver dos o más valores, lo más natural es pasar todos ellos por referencia.

* Si los parámetros son de gran tamaño (grandes arrays o structs) y quieres optimizar la velocidad, puedes pasarlos por referencia, para evitar el trabajo que supone duplicarlos.

Pero eso son sólo las ideas generales, y puede haber ligeras diferencias entre un lenguaje y otro. Por ejemplo, en muchos lenguajes, los arrays y los objetos se pasan por referencia siempre, sin necesidad de que lo indiques.

Por eso creo que no es grave que "tengas que elegir" si el parámetro va a ser por valor o por referencia, porque es algo que ya has pensado cuando has diseñado la lógica de tu programa.

----

En cuanto a lo que dices de ficheros:

- Sí, en general los ficheros de texto son más fáciles para manipulaciones básicas, se pueden modificar con multitud de editores de texto básicos (como el propio Bloc de Notas de Windows), pero también son menos versátiles.

- Como tú mismo has visto, para añadir al final del fichero suelen existir instrucciones que lo hacen en cualquiel lenguaje, pero no para "insertar" en el medio, y para eso tienes que crear un programa que vaya leyendo secuencialmente de un fichero, volcando a otro y modificando lo que sea necesario.

- Efectivamente, yo no usaría separadores en una línea, que luego complican la manipulación. Esa descomposición es muy simple en lenguajes más modernos. Por ejemplo, en C# existe una orden "split" que separa una cadena en varios trozos, considerando los separadores que tú le digas, y guarda el resultado en un array de cadenas. Pero en lenguajes como Pascal tendrías que crear tú una función para hacerlo... o tomar la opción sencilla, que es guardar cada campo en una línea. Los ficheros no son más largos, porque el tabulador es un carácter y el avance de línea también es un único carácter (en Linux y Mac OS) o como mucho dos caracteres (en Windows).

- Aun así, en un programa actual, no deberías preocuparte demasiado por el espacio. Por ejemplo, podrías dejar una línea en blanco entre una ficha y otra, para mejorar la legibilidad y hacer más fácil que se pueda editar "externamente". Ese coste de 1 o 2 bytes extra por ficha es simbólico.

- En cuanto a la vida real y los ficheros de texto, dos matices:

* En la vida real, en cualquier programa de una cierta complejidad no se usan ficheros, sino que se accede a una base de datos, bien externa como MySQL, Oracle o MS SQL Server, o bien incrustada en la aplicación, como SQLite.

* Si aun así quieres usar ficheros de texto, algo que cada vez más se usa sólo para configuraciones, puedes guardar cada dato en una línea (típico de los ficheros .INI) o bien usar formatos más estructurados, como el XML, que desperdicia más espacio a cambio de que se pueda ver incluso con un navegador web, y de que se puedan usar bibliotecas específicas que simplifiquen el acceso. Sería algo como

<ficha>
 <nombre>juan</nombre>
 <ciudad>madrid</ciudad>
</ficha>

Nuevamente, ese espacio extra es despreciable hoy en día en la mayoría de casos. Una agenda con 100.000 fichas de 200 letras cada una gastaría menos de 20 Mb. Si además tiene etiquetas XML quizá se acercara a los 40 Mb... con un ordenador actual de la gama de entrada, con 160 Gb de espacio, podrías guardar 4.000 ficheros de datos como ese.

- Y sobre la velocidad... ocurre lo mismo. Ya lo has podido comprobar. Y aun así, si la velocidad es vital, se tiende a leer todos el fichero de golpe y "cachearlo" en memoria, para que todas las consultas se hagan en memoria (nuevamente, 20 Mb es simbólico para un ordenador actual). Cada vez que haces una modificación, se guardan los cambios en fichero... preferiblemente en un segundo "hilo" (multitarea) para no necesitar parar el programa principal.

Aun así, en tu caso, si pones cada campo en una línea, el programa será ligeramente más rápido. Pero hazlo por sencillez de programación, no por rapidez. Me explico: hoy en día hay que intentar pensar más en la calidad y mantenibilidad del código que en la velocidad pura (en mi opinión): si lees 100.000 fichas de múltiples campos en 0,8 segundos... ¿cual será la mejora si optimizas buscando algo más rápido?  ¿se leerá en 0,7 segundos?  El usuario no va a notar la diferencia. Por eso, es preferible que, en vez de algoritmos complicados y super rápidos pienses en algoritmos simples, poco propensos a fallos.

Es una máxima que intento inculcar en mis alumnos: con la velocidad de proceso de un ordenador actual, es mucho más importante resolver el problema de la forma más sencilla posible, para que no falle, porque es habitual que la velocidad resultante sea más que razonable. Sólo en los (pocos) casos en los que la velocidad sea un requisito vital del proyecto, merece la pena optimizar... pero en una segunda etapa, lo que además ayuda a tener una primera fase con la que contrastar tanto los tiempos (para ver si la mejora es efectiva) como los resultados (para descubrir fallos con más facilidad)


26-Jun-2011 11:45
Fulanito de Tal

Gracias por responderme y por los consejos y sugerencias.


10-Oct-2011 09:40
Fulanito de Tal

Retomo este hilo para hablar sobre algo que supuse que era cierto pero me gustaría confirmarlo. Hablando de la ventaja de los ficheros de texto sobre los ficheros con tipo dije esto:
"Y si quiero escribir una nueva agenda utilizando otro lenguaje de programación puedo leer el mismo fichero que he creado con la agenda escrita en pascal, y viceversa".
Si escribo un programa en pascal donde se usen ficheros "con tipo", ¿luego esos ficheros se pueden leer con otro programa escrito en otro lenguaje? ¿O cada lenguaje escribe esos ficheros a su manera?
Gracias.


10-Oct-2011 22:03
Nacho Cabanes (+84)

Generalmente un "fichero con tipo" es específico de cada lenguaje, porque cada lenguaje tiene su forma de codificar internamente los datos.

Por ejemplo, las cadenas en C tienen longitud variable y terminan en un carácter nulo, mientras que en Pascal tienen longitud (máxima) fija y el primer byte es el que indica qué logitud real se está usando.

De igual modo, hay varios estándares para guardar números reales, y no todos los lenguajes usan el mismo.

Por eso, si quieres compartir datos, es preferible usar un fichero de texto.






(No se puede continuar esta discusión porque tiene más de dos meses de antigüedad. Si tienes dudas parecidas, abre un nuevo hilo.)