Wow, muy interesante el reto, 34 lineas si que es muy poco código.
Saludos
Off-topic: De todo el país, Zacatecas y Chihuahua son los únicos Estados que no conozco.
Posted 07 July 2015 - 08:37 AM
Wow, muy interesante el reto, 34 lineas si que es muy poco código.
Saludos
Off-topic: De todo el país, Zacatecas y Chihuahua son los únicos Estados que no conozco.
Posted 07 July 2015 - 12:45 PM
Que hay de nuevo colegas, aquí reportando unas optimizaciones que me tomé la molestia de realizar hoy a mi código,
después de optimizarlo eliminando variables y factorizando código pude bajar drásticamente la cantidad de lineas.
Creo que es necesario que establezcamos los lineamientos de la participación, por ejemplo si es necesario el salto de linea cada tantas columnas, y hasta que numero es necesario hacer la conversión.
por ahora mi código lo pude reducir a 34 lineas. Utilicé la función IfThen para emular el operador ternario de c++ y ahorrar 6 lineas de código, así como que estoy contando sentencias, es decir, no hago saltos de linea cada tantas columnas, además que el algoritmo sólo sigue traduciendo hasta un máximo de 999,999,999.99 y el formato de salida es en español de México.
Saludos desde un lugar en Zacatecas México...
Otra cosa la unidad ya terminada cuenta 58 lineas con saltos de linea y formateada correctamente con JediCodeFormat CTRL+D en lazarus, en este si se realizan saltos de linea para que haya lineas largas. además que como unidad ya posee sus secciones de interface, implementación, usos, etc.
Pero supongo que deberíamos contar solo las lineas efectivas, en mi caso 34 lineas desde la declaración de la función, su begin y end;
Asombroso, realmente.
Lo que más me asombra es el alcance y que interpreta los decimales. Que es como dice Escafandra, una vez que tengas el algoritmo principal los decimales se tratan de la misma manera.
Aun así, como dices, había impuesto unas restricciones para que el desafío sea parejo. Te las aclaro nuevamente, ya que las nombras:
1. Son todas las líneas desde unit hasta end. Pues no sería justo que yo descontara mis líneas de constantes, por ejemplo.
2. Las columnas llegan a un máximo de 100 caracteres.
3. los if, then y else van en diferentes líneas:
if condición then sentencia1 else sentencia2; if condición1 then sentencia1 else if condición2 then sentencia3 else if ...
Pues de lo contrario pondríamos todo en una sola línea de if inicial y listo, tampoco sería justa una comparación con ideal buscado.
Esto se extiende a los bucles:
repeat {...} until condición while condición do begin {...} end;
4. Espacios entre las secciones, esto es obvio, son los espacios que genera el mismo IDE.
Y creo que esas eran todas.
En mi caso tengo cuatro declaraciones de constantes que usan dos líneas dada la limitación de los 100 caracteres. Esas son ocho líneas más, que junto al separador y la palabra reservada const hacen diez, que si no las contara sería un gran error.
Saludos
Posted 07 July 2015 - 01:34 PM
..... Lo que más me asombra es el alcance y que interpreta los decimales. Que es como dice Escafandra, una vez que tengas el algoritmo principal los decimales se tratan de la misma manera. .....
Tengo una duda
¿Como es que manejan ustedes los decimales?
Acá en México como ya he mencionado solo se colocan los centavos como fracción de 100 y las siglas de Moneda Nacional (25/100 M.N.)
Saludos
Posted 07 July 2015 - 03:48 PM
Tengo una duda
¿Como es que manejan ustedes los decimales?
Acá en México como ya he mencionado solo se colocan los centavos como fracción de 100 y las siglas de Moneda Nacional (25/100 M.N.)
Saludos
En Argentina, siempre en el ámbito comercial, se escribe algo parecido, es decir por ejemplo, para la cantidad 50,25$ que suelen escribir $50,25 (no sé por qué) será: "son pesos cincuenta con 25/100"
Saludos
Edited by cram, 07 July 2015 - 03:50 PM.
Posted 07 July 2015 - 04:31 PM
En Argentina, siempre en el ámbito comercial, se escribe algo parecido, es decir por ejemplo, para la cantidad 50,25$ que suelen escribir $50,25 (no sé por qué) será: "son pesos cincuenta con 25/100"
Saludos
Que curioso, acá se escribiría así
Cincuenta Pesos 25/100 M.N.
La palabra "Son" no es usada en las facturas, a veces se usa en los cheques, cuando se hacen ya que no es tan común su uso en estos días de operaciones electrónicas.
Saludos
Posted 07 July 2015 - 07:22 PM
Ya lo acomodé a un máximo de 100 columnas, en ese caso creo quedará entonces en 54 caracteres y la verdad no creo poder bajar de ahí. Otra cosa que no tuve en cuenta y no se si ustedes consideraron es el tipo de dato de entrada, también el manejo de excepciones y valores negativos o ceros. En mi caso solo acepta tipos de datos ligados a valores currency o float en su defecto, y con el casting automático acepta enteros, aparte de no manejar excepciones, la entrada siempre debe ser un número mayor a cero no negativo.
Posted 08 July 2015 - 07:17 AM
Entonces hay dos claros vencedores del desafío, sin contarme, pues yo lo propuse.
Más allá que es aparentemente imbatible el tamaño de código de Dragonlair, discretamente es un triunfo.
El otro código sería el propuesto por Escafandra.
Yo también bajé los 100 con todas las reglas impuestas (por mí mismo, que en realidad tienen que ver con prácticas de escritura de código más o menos estándares).
Lo que no me queda claro es ¿qué es la emulación del ifthen? Conozco el iif y la forma de C, pero en Pascal, nunca lo usé.
¡DESAFÍO MACHACADO!
Saludos
Posted 08 July 2015 - 07:41 AM
Bueno, yo tengo que buscar un rato para optimizar mi código. Lo que no tengo nada claro es que un código con menos líneas esté más optimizado. Trampas para bajar líneas hay muchas, otra cosa es como quede ese código compilado, habría que analizar el resultado en asm que es lo que va a presentar más o menos carga de trabajo al microprocesador y la velocidad de ejecución. En cualquier caso, el asunto de las 100 líneas es pan comido.
Sobre el asunto de decimales, en España escribimos el número hasta la coma, añadimos la palabra "con", y acto seguido tratamos la parte decimal como la parte entera, tenga los decimales que tenga.
Saludos.
Posted 08 July 2015 - 09:37 AM
Bueno, yo tengo que buscar un rato para optimizar mi código. Lo que no tengo nada claro es que un código con menos líneas esté más optimizado. Trampas para bajar líneas hay muchas, otra cosa es como quede ese código compilado, habría que analizar el resultado en asm que es lo que va a presentar más o menos carga de trabajo al microprocesador y la velocidad de ejecución. En cualquier caso, el asunto de las 100 líneas es pan comido.
Sobre el asunto de decimales, en España escribimos el número hasta la coma, añadimos la palabra "con", y acto seguido tratamos la parte decimal como la parte entera, tenga los decimales que tenga.
Saludos.
Más claro imposible y comparto tu opinión . En realidad es un desafío algo flaco.
Por ejemplo el uso de case y sets hace un código menos eficiente. Aun así, el desafío era ese, nada muy serio.
Por eso hice hincapié en el tema de las columnas de caracteres y los saltos de párrafos.
Por otra parte se supone que había que pasar un número entero, ¿no se de donde salió el tema de moneda? el asunto es que una vez hecho el algoritmo principal, se puede separar al número usando la coma, o agregarle leyendas como más y menos, porciento, pesos, etc. Pero ese no era el reto.
Aquí dejo mi código (nublado) quizás después lo ponga completo para compartirlo (qué más dá) y un ejemplo.
Función sobrecargada aceptando string o integer, para el caso de strings tiene una capacidad de 57 caracteres, es decir nonillones.
Alcanza las 76 líneas, que en realidad son 75 (un error en const de dos líneas sin necesidad).
cod_NumLetras.jpg 15.7KB 2 downloads
Saludos
Posted 08 July 2015 - 10:42 AM
el equivalente del operador ternario de c++
if trunc(Value) > 1000000 then Result += ' MILLONES' else Result += ' MILLON'; Result += IfThen(trunc(Value) > 1000000, ' MILLONES', ' MILLON');
Posted 08 July 2015 - 11:25 AM
el equivalente del operador ternario de c++
delphi
if trunc(Value) > 1000000 then Result += ' MILLONES' else Result += ' MILLON'; Result += IfThen(trunc(Value) > 1000000, ' MILLONES', ' MILLON');
Ahora entiendo, no lo conocía.
Cómodo en ciertos casos, pero solo es de FreePascal, no de ObjectPascal, por lo que veo.
Es una función de StrUtils y solo funciona para el tratamiento de cadenas de caracteres. ¡Si se pudiera anidar!
m:= ifthen (c<>1, 'a', 'b'); //m es de tipo string
si c= 1 entonces m valdrá 'b', caso contrario m valdrá 'a'
Pasa que soy algo novato en FreePascal.
Solo por curiosidad, ¿pasarías tu código a ObjectPascal?, y nos cuentas los resultados.
Saludos
Posted 09 July 2015 - 02:26 PM
He trabajado un poco más en mi código, aunque quizás se pueda optimizar un poco más en cuanto al número de líneas. Los resultados son estos:
1.- Abarca hasta un cuatrillón tipo 1E24, es decir cuatrillón español, y supongo que de todos los hispanoparlantes.
2.- No implementé decimales, dado que cram no lo incluyó en las bases.
3.- Lo he implementado en una unit a parte como estableció cram y abarca tras el formateo "oficial" 65 líneas y podría recortar otras 4.
4.- He usado delphi puro y duro, sin similitudes de compilación tipo C, lenguaje "experto" en recortar código.
5.- Si lo recorto a 999.9999.9999 entonces abarca 8 líneas menos, es decir, 57 líneas.
He usado trucos como sustiuir condicionales por operaciones lógicas y binarias en la misma expresión para calcular índices, esto ofusca bastante el código pero probablemente no le reste eficiencia para el micro.
Una muestra sería esto:
Result:=dec[ord(N[i])-48]+suf[WORD((N[i]='2')and(N[i-1]>'0'))+2*WORD(N[i]>'2')+WORD((N[i]>'2')and(N[i-1]='0'))]+Result;
También he usado recursividad pasando como parámetro trozos del número a calcular, es por eso que llegar al cuatrillón me consume 8 líneas.
Como dijo cram, a mí tampoco me importará publicar el código.
Saludos.
Posted 10 July 2015 - 06:26 AM
Posted 10 July 2015 - 08:11 AM
Muy interesante el reto, ya llegó a 61 lineas
Saludos
Posted 10 July 2015 - 12:16 PM
El código está en objectpascal, así me lo marca el IDE, tendría que escribirlo en delphi para poder ver que funciones que utilizo no están dentro de las librerías de delphi. Desafortunadamente no poseo una licencia de delphi para hacer la conversión.
A lo que alcanzo a apreciar en la funcion de scafandra puede que su código sea mas eficiente ya que el trabaja el valor ingresado como una cadena y al parecer trabaja con las posiciones de los caracteres para su representación literal, quizá mi código se salió de lo propuesto ya que ciertamente no se pedia la representación literal con decimales y mi participación los incluyó a raíz del uso para expedir notas de venta o facturas, de todos modos creo que cumplimos con el objetivo de competir con un código que no utilizara fuerza bruta. saludos y he aquí mi aporte a la comunidad.
unit Unit1; {$mode objfpc}{$H+} interface uses SysUtils, strutils; //SysUtils necesario para la funcion trim(), strutils necesario para la funcion ifthen(,,) function ValueToLetter(Value: currency): string; implementation function ValueToLetter(Value: currency): string; const // :) sUnits: array[0..15] of string = ('', 'UN', 'DOS', 'TRES', 'CUATRO', 'CINCO', 'SEIS', 'SIETE', 'OCHO', 'NUEVE', 'DIEZ', 'ONCE', 'DOCE', 'TRECE', 'CATORCE', 'QUINCE'); sTens: array[0..9] of string = ('', 'DIECI', 'VEINTI', 'TREINTA', 'CUARENTA', 'CINCUENTA', 'SESENTA', 'SETENTA', 'OCHENTA', 'NOVENTA'); sHundreds: array[0..9] of string = ('', 'CIEN', 'DOSCIENTOS', 'TRESCIENTOS', 'CUATROCIENTOS', 'QUINIENTOS', 'SEISCIENTOS', 'SETECIENTOS', 'OCHOCIENTOS', 'NOVECIENTOS'); Op: array[0..2] of integer = (1, 1000, 1000000); var iTemp, i: Integer; //itemp contendrá una representacion entera de un número base 100 para su posterior tratamiento cTemp: Currency; //para contener temporalmente al numero en operacion y mantener el valor original contenido en Value begin cTemp := Value; for i := 2 downto 0 do // el número ingresado siempre se tratará en parejas de 3, ejemplo 487562784.65, la primera parte que se tratará es 487. begin iTemp := trunc(cTemp / Op[i]); //para extraer la pareja de tres números dividimos, 487562784.65 / 1000000 = 487.56278465, truncamos 487. cTemp -= iTemp * Op[i]; //extraemos esa parte del número en operacion 487562784.65 - 487*1000000 = 562784.65 if (i > 0) and (iTemp < 1) then //si el resultado de la separación de la pareja es 0 y todavía no es la última iteracion entonces saltamos a la siguiente iteración, al no haber operaciones que realizar Continue; Result += ' ' + sHundreds[iTemp div 100]; // traducimos las centenas a su representacion literal correspondiente, simplemente extraemos el número correspondiente haciendo una division entera del número a 100 iTemp -= iTemp div 100 * 100; //restamos las centenas del número que estamos tratando case iTemp of //utilizando este case podemos tratar la última parte del número, decenas y unidades 0..15: Result += ' ' + sUnits[iTemp]; 16..19, 21..29: Result += ' ' + sTens[iTemp div 10] + sUnits[iTemp mod 10]; 20: Result += ' VEINTE'; 30, 40, 50, 60, 70, 80, 90: Result += sTens[iTemp div 10]; 31..39, 41..49, 51..59, 61..69, 71..79, 81..89, 91..99: Result += ' ' + sTens[iTemp div 10] + ' Y ' + sUnits[iTemp mod 10]; end; case i of // dada la parte que estemos tratando agregamos el sufijo correspondiente 2: Result += IfThen(trunc(Value) > 1999999, ' MILLONES', ' MILLON'); 1: Result += IfThen(trunc(Value) mod 1000000) > 999, ' MIL'); end; Result := Trim(Result); end; Result += ifthen(value = 0, 'CERO') + IfThen(trunc(Value) = 1, ' PESO', ' PESOS') + Format(' %.2d/100 M.N.', [trunc(cTemp * 100)]); //como al final substraimos toda la parte entera del valor original entonces sólo multiplicamos por 100 y truncamos para obtener la representación entera de los decimales end; end.
Repito que es muy probable que requiera cambios menores para su empleo en delphi pero andamos en el mismo barco "pascal".
Además reitero que tomé la lógica de las substracciones de los números de la función IntToRoman contenida en la librería strutils perteneciente a la RunTimeLibrary de freepascal.
Saludos a todos.
Posted 10 July 2015 - 01:30 PM
Si lo escribiste con Lazarus, en las opciones de compilación tienes esa configuración estilo "C".El código está en objectpascal, así me lo marca el IDE...
Estás en lo cierto, trabajo analizando los caracteres numéricos uno a uno y recursivamente en grupos.A lo que alcanzo a apreciar en la funcion de scafandra puede que su código sea mas eficiente ya que el trabaja el valor ingresado como una cadena y al parecer trabaja con las posiciones de los caracteres para su representación literal...
unit Literales; interface uses Windows, SysUtils, StrUtils; function ToLiteral(N: String): String; implementation function ToLiteral(N: String): String; function Numeros(N: String): String; const suf: array[0..7]of string=('e','i','ta y ','ta','','to ','ientos ','ientos '); uni: array[0..19]of string=('','uno','dos','tres','cuatro','cinco','seis','siete','ocho','nueve', 'diez','once','doce','trece','catorce','quince','dieciseis','diecisite','dieciocho','diecinueve'); dec: array[2..9]of string=('veint','trein','cuaren','cincuen','sesen','seten','ochen','noven'); cen: array[1..9]of string=('cien','dosc','tresc','cuatroc','quin','seisc','setec','ochoc','noevec'); var tm: String; i: integer; begin for i:= 1 to Length(N) do begin tm:= Numeros(copy(N, i, 3*WORD((i>3)and(i<5))+6*WORD(i>6))); // se copian 0, 3 ó 6 caracteres if i = 1 then // Unidades Result:= uni[ord(N[i])-48] else if (i = 2) and (N[i] = '1') then // Decenas Result:= uni[ord(N[1])-38] else if (i = 2) and (N[i] > '1') then Result:=dec[ord(N[i])-48]+suf[WORD((N[i]='2')and(N[i-1]>'0'))+2*WORD(N[i]>'2')+WORD((N[i]>'2') and(N[i-1]='0'))]+Result else if (i = 3) and(N[i] >= '1')then // Centenas Result:= cen[ord(N[i])-48]+suf[3+WORD(N[i]='1')+WORD((N[i]='1')and(N[i-1]>'0')or(N[i-2]>'0'))+ 3*WORD(N[i]>'1')]+Result else if (i = 4) and (tm = 'uno') then // Millares Result:= 'mil ' + Result else if (i = 4) and (tm <> '') then Result:= tm + ' mil ' + Result else if (i = 7) and (N[i] = '1') then //Millones Result:= 'un millón ' + Result else if (i = 7) and (tm <> '') then Result:= tm + ' millones ' + Result else if (i = 13) and (N[i] = '1') then //Billones Result:= 'un billón ' + Result else if (i = 13) and (tm <> '') then Result:= tm + ' billones ' + Result else if (i = 19) and (N[i] = '1') then //Trillones Result:= 'un trillón ' + Result else if (i = 19) and (tm <> '') then Result:= tm + ' trillones ' + Result; end; Result:= Trim(Result); end; begin Result:= Numeros(ReverseString(N)); if Result = '' then Result:= 'cero'; end; end.
Posted 10 July 2015 - 01:39 PM
Hola
Muy interesante la forma como analizas los números amigo escafandra.
Saludos
Posted 10 July 2015 - 01:50 PM
Muy interesante la forma como analizas los números amigo escafandra.
Posted 10 July 2015 - 07:51 PM
Me resta entonces agregar mi código.
Quiero aclararles que puedo eliminar la inclusión de la biblioteca SysUtils usando unos trucos con los ASCIIs. Pero lo prefiero así:
La variante que acepta un entero como parámetro de entrada solo puede procesaro hasta el máximo que acepte un entero, pero se supone que el número será pasado como cadena de caracteres y es aquí, donde el algoritmo permite interpretar hasta 57 cifras (nonillones).
Separa de a tres la cadena agregando uno o dos ceros al principio en caso que la longitud no sea múltiplo de tres (esto ahorra proceso).
Identifica la posición de los grupos de tres y agrega los sufijos mil o millón (millones) según el caso.
Identifica el caso de tener que escribir un o uno.
Lo más básico es que los nombres que hay que armar son en realidad los que forman las tres cifras, lo demás son sufijos.
unit NumLetras; interface uses implementation const 'seiscientos', 'setecientos', 'ochocientos', 'novecientos'); D: array[0..9] of String = ('', '', 'veinte', 'treinta', 'cuarenta', 'cincuenta', 'sesenta', 'setenta', 'ochenta', 'noventa'); 'nueve'); 'diecisiete', 'dieciocho', 'diecinueve'); ' septill', ' octill', ' nonill'); function NumALetras(N: Integer): String; begin Result:= NumALetras(IntToStr(N)); end; function NumALetras(S: String): String; var P, I, zU, zD, zC: Integer; R, L: String; begin L:= ''; P:= 0; if Length(S) mod 3 = 1 then S:= '00' + S else if Length(S) mod 3 = 2 then S:= '0' + S; I:= Length(S); while I > 0 do begin TryStrToInt(S[I], zU); TryStrToInt(S[I-1], zD); TryStrToInt(S[I-2], zC); R:= ''; if zC <> 1 then R:= C[zC] else if zU + zD > 0 then R:= 'ciento' else R:= C[zC]; if zD = 1 then R:= R + ' ' + D1[zU] else if zU = 0 then R:= R + ' ' + D[zD] else if zD = 2 then R:= R + ' ' + 'veinti' + U[zU] else if zD = 0 then R:= R + ' ' + D[zD] + U[zU] else R:= R + ' ' + D[zD] + ' y ' + U[zU]; if (P = 0) and (zU = 1) then R:= R + 'o'; if Odd(P) and (zU + zD + zC > 0) then R:= R + ' mil '; if not Odd(P) and (P > 1) then if (zU = 1) and (zD + zC = 0) then R:= R + M[P div 2] + 'ón ' else R:= R + M[P div 2] + 'ones '; L:= R + L; I:= I - 3; P:= P + 1; end; Result:= L; end; end.
Ah!... no interpreta el cero, pero es fácil de arreglar. El asunto es que no me interesa que lo procese, ya que puedo hacerlo fuera de la rutina.
Posted 10 July 2015 - 07:56 PM
Y así quedaría como lo tengo para usar:
unit NumLetras; interface uses implementation const 'seiscientos', 'setecientos', 'ochocientos', 'novecientos'); D: array[0..9] of String = ('', '', 'veinte', 'treinta', 'cuarenta', 'cincuenta', 'sesenta', 'setenta', 'ochenta', 'noventa'); U: array[0..9] of String = ('', 'un', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve'); 'diecisiete', 'dieciocho', 'diecinueve'); ' septill', ' octill', ' nonill'); function NumALetras(N: Integer): String; begin Result:= NumALetras(IntToStr(N)); end; function NumALetras(S: String): String; var P, I, zU, zD, zC: Integer; R, L: String; begin L:= ''; P:= 0; if Length(S) mod 3 = 1 then S:= '00' + S else if Length(S) mod 3 = 2 then S:= '0' + S; I:= Length(S); while I > 0 do begin TryStrToInt(S[I], zU); TryStrToInt(S[I-1], zD); TryStrToInt(S[I-2], zC); R:= ''; if zC <> 1 then R:= C[zC] else if zU + zD > 0 then R:= 'ciento' else R:= C[zC]; if zD = 1 then R:= R + ' ' + D1[zU] else if zU = 0 then R:= R + ' ' + D[zD] else if zD = 2 then R:= R + ' ' + 'veinti' + U[zU] else if zD = 0 then R:= R + ' ' + D[zD] + U[zU] else R:= R + ' ' + D[zD] + ' y ' + U[zU]; if (P = 0) and (zU = 1) then R:= R + 'o'; if Odd(P) and (zU + zD + zC > 0) then R:= R + ' mil '; if not Odd(P) and (P > 1) then if (zU = 1) and (zD + zC = 0) then R:= R + M[P div 2] + 'ón ' else R:= R + M[P div 2] + 'ones '; L:= R + L; I:= I - 3; P:= P + 1; end; Result:= L; end; end.