code_fun

Convertir números a letras, recursivo

Hace unos años en primer semestre de la carrera de ingeniería en sistemas, un maestro dio lo que para mi es la mejor explicación de lo que es la recursividad, preguntó:

— ¿Como harías una pila de 10 ladrillos de forma iterativa?

La respuesta es obvia: coloco un ladrillo, luego otro arriba, luego otro más y así hasta completar 10.

— Pero entonces —preguntó—, ¿como harías una pila de 10 ladrillos de forma recursiva?

Muy fácil, haces una pila de 9 ladrillos y le pones uno arriba.

Pero, ¿como harías una pila de 9 ladrillos de forma recursiva?… Haces una pila de 8 y le pones uno arriba.

Y, ¿como haces una pila de 1 ladrillo de forma recursiva?… Colocas un ladrillo en el suelo.

Es muy interesante como esta sencillísima forma de ver las cosas explica de una forma perfectamente entendible, que la recursividad es: definir algo en términos de si mismo.

Las características de una función recursiva son las siguientes:

– Debe llamarse a si misma.
– Debe tener una condición de salida en algún momento.

Ahora al tema de este post.

Les comparto una funcioncita que toma un numero y devuelve el nombre en español de ese numero… ¡¡¡Totalmente recursiva!!!

Es mucho más corta, eficiente y entendible que las funciones convencionales que realizan esta tarea, simple y sencillamente por que la nomenclatura de los números en nuestro idioma es recursiva por naturaleza.

''' Este codigo se distribuye bajo la licencia zlib/libpng.''' Para ver la licencia completa visita: http://www.opensource.org/licenses/Zlib''' <summary>''' Funciones para convertir números a letras.''' [ecrespo] creado hace bastantes años migrado múltiples veces a diferentes lenguajes.''' </summary>''' <remarks></remarks>Public Module ImporteLetras	'Constantes para los textos	Private strCentenas(9) As String	Private strDecenas(9) As String	Private strDecenas10(9) As String	Private strUnidades(9) As String	Private Sub Inicializar()		'Comprueba si ya se han inicializado los valores		If Not strCentenas(1) = "" Then Exit Sub		'Asignacion de textos de las centenas		strCentenas(1) = "ciento "		strCentenas(2) = "doscientos "		strCentenas(3) = "trescientos "		strCentenas(4) = "cuatrocientos "		strCentenas(5) = "quinientos "		strCentenas(6) = "seiscientos "		strCentenas(7) = "setecientos "		strCentenas(8) = "ochocientos "		strCentenas(9) = "novecientos "		'Asignacion de textos de las decenas		strDecenas(1) = "diez "		strDecenas(2) = "veinte "		strDecenas(3) = "treinta "		strDecenas(4) = "cuarenta "		strDecenas(5) = "cincuenta "		strDecenas(6) = "sesenta "		strDecenas(7) = "setenta "		strDecenas(8) = "ochenta "		strDecenas(9) = "noventa "		'Asignacion de textos de las decenas de diez		strDecenas10(0) = "diez "		strDecenas10(1) = "once "		strDecenas10(2) = "doce "		strDecenas10(3) = "trece "		strDecenas10(4) = "catorce "		strDecenas10(5) = "quince "		strDecenas10(6) = "dieciseis "		strDecenas10(7) = "diecisiete "		strDecenas10(8) = "dieciocho "		strDecenas10(9) = "diecinueve "		'Asignacion de textos de las unidades		strUnidades(1) = "un "		strUnidades(2) = "dos "		strUnidades(3) = "tres "		strUnidades(4) = "cuatro "		strUnidades(5) = "cinco "		strUnidades(6) = "seis "		strUnidades(7) = "siete "		strUnidades(8) = "ocho "		strUnidades(9) = "nueve "	End Sub	Public Function NumerosLetras(ByVal Importe As Double, ByVal FormatoTxt As VbStrConv) _										As String		Dim strTmp As String		'Inicializa los valores de texto		Inicializar()		strTmp = ImporteLetras(Importe)		strTmp = Trim$(Replace$(strTmp, "  ", " "))		If strTmp = "" Then			NumerosLetras = StrConv("cero pesos" & Centavos(Importe), FormatoTxt)		ElseIf strTmp = "un" Then			NumerosLetras = StrConv(strTmp & " peso" & Centavos(Importe), FormatoTxt)		ElseIf Right$(strTmp, 4) = "ones" Or Right$(strTmp, 2) = "on" Then			NumerosLetras = StrConv(strTmp & " de pesos" & Centavos(Importe), FormatoTxt)		Else			NumerosLetras = StrConv(strTmp & " pesos" & Centavos(Importe), FormatoTxt)		End If	End Function	Private Function ImporteLetras(ByVal Importe As Double) As String		Dim strImporte As String		Dim strTmp As String		strImporte = Format$(Importe, "0.00")		'Checar Billones		If Int(Importe / 1000000000000.0#) = 1 Then	'si el importe es un billon algo...			ImporteLetras = "un billon " & ImporteLetras(Right$(strImporte, 15))		ElseIf Int(Importe / 1000000000000.0#) > 1 Then			ImporteLetras = ImporteLetras(Int(Importe / 1000000000000.0#)) & _											" billones " & ImporteLetras(Right$(strImporte, 15))		Else			'Checar Millones			If Int(Importe / 1000000) = 1 Then 'si el importe es un millon algo...				ImporteLetras = "un millon " & ImporteLetras(Right$(strImporte, 9))			ElseIf Int(Importe / 1000000) > 1 Then				ImporteLetras = ImporteLetras(Int(Importe / 1000000)) & _												" millones " & ImporteLetras(Right$(strImporte, 9))			Else				'Checar Millares				If Int(Importe / 1000) = 1 Then					ImporteLetras = "mil " & ImporteLetras(Right$(strImporte, 6))				ElseIf Int(Importe / 1000) > 1 Then					ImporteLetras = ImporteLetras(Int(Importe / 1000)) & _													" mil " & ImporteLetras(Right$(strImporte, 6))				Else					'Checar centenas					If Digito(Importe, 3) = 1 Then						If Left$(Format$(Importe, "0.00"), 3) = 100 Then 'solo cuando es cien es diferente							strTmp = "cien "						Else							strTmp = strCentenas(1)						End If					Else						strTmp = strCentenas(Digito(Importe, 3))					End If					'Checar Decenas					If Digito(Importe, 2) = 1 Then 'si es uno le asigna su texto a las unidades						strTmp = strTmp & strDecenas10(Digito(Importe, 1))					Else 'si no es uno asigna decenas y despues las unidades						If Digito(Importe, 2) = 2 Then 'el veinte es especial							If Digito(Importe, 1) = 0 Then								strTmp = strTmp & strDecenas(2)							Else								strTmp = strTmp & "veinti"							End If						ElseIf Digito(Importe, 2) = 0 Then 'el cero lo ignora						Else							If Digito(Importe, 1) = 0 Then								strTmp = strTmp & strDecenas(Digito(Importe, 2))							Else								strTmp = strTmp & strDecenas(Digito(Importe, 2)) & " y "							End If						End If						'Si las decenas no es uno asigna unidades.						strTmp = strTmp & strUnidades(Digito(Importe, 1))					End If					ImporteLetras = strTmp				End If			End If		End If	End Function	'Funcion para devolver un digito en la posicion solicitada	Private Function Digito(ByVal Importe As Double, ByVal Posicion As Integer) As Integer		Dim strImporte As String		strImporte = Format$(Importe, "0.00")		Posicion = Posicion + 3		If Posicion > Len(strImporte) Then			Digito = 0 'si la posocion esta fuera de la cadena, no devuelve nada		Else			Digito = Val(Mid$(strImporte, Len(strImporte) - Posicion + 1, 1))		End If	End Function	'Funcion para poner la terminación de la cadena en centavos.	Private Function Centavos(ByVal Importe As Double) As String		Centavos = " " & Right$(Format$(Importe, "0.00"), 2) & "/100 m. n."	End Function	End Module
/// <summary>/// Funciones para convertir números a letras./// </summary>/// <remarks>/// Este codigo se distribuye bajo la licencia zlib/libpng./// Para ver la licencia completa visita: http://www.opensource.org/licenses/Zlib/// </remarks>/// <history>///  [ecrespo]	Creado hace bastantes años, migrado múltiples veces a diferentes lenguajes.///  [eferron]	01.03.2013	Migrado a C#/// </history>public static class StringExtensions{	#region . Cadenas .	private static string[] strCentenas = new string[]	{		"ciento ",		"doscientos ",		"trescientos ",		"cuatrocientos ",		"quinientos ",		"seiscientos ",		"setecientos ",		"ochocientos ",		"novecientos "	};	private static string[] strDecenas = new string[]	{		"diez ",		"veinte ",		"treinta ",		"cuarenta ",		"cincuenta ",		"sesenta ",		"setenta ",		"ochenta ",		"noventa "	};	private static string[] strDecenas10 = new string[]	{		"diez ",		"once ",		"doce ",		"trece ",		"catorce ",		"quince ",		"dieciseis ",		"diecisiete ",		"dieciocho ",		"diecinueve "	};	private static string[] strUnidades = new string[]	{		"un ",		"dos ",		"tres ",		"cuatro ",		"cinco ",		"seis ",		"siete ",		"ocho ",		"nueve "	};	#endregion	#region . Enumerados .	public enum Currency	{		PESOS,		DOLARES	}	#endregion	#region . Extensiones .	/// <summary>	/// Devuleve un número de caracteres a la izquierda de la cadena.	/// </summary>	/// <param name="cantidad">	/// El número de caracteres a recuperar.	/// </param>	public static string Left(this string str, int length)	{		return str.Substring(0, length);	}	/// <summary>	/// Devuleve un número de caracteres a la derecha de la cadena.	/// </summary>	/// <param name="cantidad">	/// El número de caracteres a recuperar.	/// </param>	public static string Right(this string cadena, int cantidad)	{		return cadena.Substring(cadena.Length - cantidad, cantidad);	}	/// <summary>	/// Devuelve un entero representando el contenido de la cadena.	/// </summary>	/// <remarks>	/// Esta rutina lanza una excepción si la cadena no es un entero válido.	/// </remarks>	public static int ToInt(this string cadena)	{		return System.Convert.ToInt32(cadena);	}	/// <summary>	/// Devuelve un valor decimal representando el contenido de la cadena.	/// </summary>	/// <remarks>	/// Esta rutina lanza una excepción si la cadena no es un número válido.	/// </remarks>	public static int ToInt(this string cadena, int predeterminado)	{		int.TryParse(cadena, out predeterminado);		return predeterminado;	}	/// <summary>	/// Devuelve un entero representando el contenido de la cadena.	/// </summary>	/// <remarks>	/// Esta rutina lanza una excepción si la cadena no es un entero válido.	/// </remarks>	public static decimal ToDecimal(this string cadena)	{		return System.Convert.ToDecimal(cadena);	}	/// <summary>	/// Devuelve un valor decimal representando el contenido de la cadena.	/// </summary>	/// <remarks>	/// Esta rutina lanza una excepción si la cadena no es un número válido.	/// </remarks>	public static decimal ToDecimal(this string cadena, decimal predeterminado)	{		decimal.TryParse(cadena, out predeterminado);		return predeterminado;	}	/// <summary>	/// Devuelve un entero representando el contenido de la cadena.	/// </summary>	/// <remarks>	/// Esta rutina lanza una excepción si la cadena no es un entero válido.	/// </remarks>	public static double ToDouble(this string cadena)	{		return System.Convert.ToDouble(cadena);	}	/// <summary>	/// Devuelve un valor decimal representando el contenido de la cadena.	/// </summary>	/// <remarks>	/// Esta rutina lanza una excepción si la cadena no es un número válido.	/// </remarks>	public static double ToDouble(this string cadena, double predeterminado)	{		double.TryParse(cadena, out predeterminado);		return predeterminado;	}	/// <summary>	/// Devuelve TRUE si la cadena puede considerarse como verdadera.	/// </summary>	public static bool isTrue(this string cadena)	{		return !string.IsNullOrEmpty(cadena) && string.Compare(cadena, "false", true) != 0 && cadena != "0";	}	/// <summary>	/// Conviete a palabras la cantidad representada en la cadena.	/// </summary>	public static string ToWords(this string cadena, Currency moneda = Currency.PESOS)	{		return NumerosLetras(cadena.ToDouble(), moneda);	}	/// <summary>	/// Conviete a palabras la cantidad representada en la variable de tipo double.	/// </summary>	public static string ToWords(this double cadena, Currency moneda = Currency.PESOS)	{		return NumerosLetras(cadena, moneda);	}	/// <summary>	/// Conviete a palabras la cantidad representada en la variable de tipo decimal.	/// </summary>	public static string ToWords(this decimal cadena, Currency moneda = Currency.PESOS)	{		return NumerosLetras(System.Convert.ToDouble(cadena), moneda);	}	/// <summary>	/// Conviete a palabras la cantidad representada en la variable de tipo int.	/// </summary>	public static string ToWords(this int cadena, Currency moneda = Currency.PESOS)	{		return NumerosLetras(System.Convert.ToDouble(cadena), moneda);	}	/// <summary>	/// Conviete a palabras la cantidad representada en la variable de tipo uint.	/// </summary>	public static string ToWords(this uint cadena, Currency moneda = Currency.PESOS)	{		return NumerosLetras(System.Convert.ToDouble(cadena), moneda);	}	#endregion	#region . Rutinas de conversión .	/// <summary>	/// Convierte una cantidad a su representación en palabras.	/// </summary>	public static string NumerosLetras(double Importe, Currency moneda = Currency.PESOS)	{		// recupera la cadena que representa esta cantidad		string strTmp = ImporteLetras(Importe).Trim().Replace("  ", " ");		string strMoneda = moneda == Currency.PESOS ? "pesos" : "dólares";				// agrega la porción de la moneda y centavos		if (string.IsNullOrEmpty(strTmp))			return string.Format("cero {0} {1}", strMoneda, Centavos(Importe, moneda));		else if (strTmp == "un")			return string.Format("un {0} {1}", strMoneda, Centavos(Importe, moneda));		else if (strTmp.Right(4) == "ones" | strTmp.Right(2) == "on")			return String.Format("{0} de {1} {2}", strTmp, strMoneda, Centavos(Importe, moneda));		return String.Format("{0} {1} {2}", strTmp, strMoneda, Centavos(Importe, moneda));	}	/// <summary>	/// Esta es la rutina recursiva que convierte cada segmento en palabras	/// </summary>	private static string ImporteLetras(double Importe)	{		Int32 cociente = 0;		string strImporte = Importe.ToString("000.00");		//Checar Billones		//si el importe es un billon algo...		if ((int)(Importe / 1000000000000.0) == 1)			return "un billon " + ImporteLetras(strImporte.Right(15).ToDouble());				else if ((cociente = (int)(Importe / 1000000000000.0)) > 1)			return string.Format("{0} billones {1}", ImporteLetras(cociente), ImporteLetras(strImporte.Right(15).ToDouble()));		else		{			//Checar Millones			//si el importe es un millon algo...			if ((int)(Importe / 1000000) == 1)				return "un millon " + ImporteLetras(strImporte.Right(9).ToDouble());						else if ((cociente = (int)(Importe / 1000000)) > 1)				return string.Format("{0} millones {1}", ImporteLetras(cociente), ImporteLetras(strImporte.Right(9).ToDouble()));			else			{				//Checar Millares				if ((int)(Importe / 1000) == 1)					return "mil " + ImporteLetras(strImporte.Right(6).ToDouble());								else if ((cociente = (int)(Importe / 1000)) > 1)					return string.Format("{0} mil {1}", ImporteLetras(cociente), ImporteLetras(strImporte.Right(6).ToDouble()));				else				{					var result = new StringBuilder();					int digito1 = System.Convert.ToInt32(strImporte[0].ToString());					int digito2 = System.Convert.ToInt32(strImporte[1].ToString());					int digito3 = System.Convert.ToInt32(strImporte[2].ToString());					//Checar centenas					if (digito1 == 1)					{						//solo cuando es cien exacto es diferente						if (Math.Abs(Importe) - 100 == 0)							result.Append("cien ");						else							result.Append(strCentenas[0]);					}					else if (digito1 > 1)						result.Append(strCentenas[digito1 - 1]);					//Checar Decenas					//si es uno le asigna su texto de una vez a las unidades					if (digito2 == 1)					{						result.Append(strDecenas10[digito3]);					}					else					{						//el veinte es especial						if (digito2 == 2)						{							if (digito3 == 0)								result.Append(strDecenas[1]);							else								result.Append("veinti");						}						else if (digito2 == 0)						{							//el cero lo ignora						}						else						{							result.Append(strDecenas[digito2 - 1]);							if (digito3 != 0)								result.Append(" y ");						}						//Si las decenas no es uno asigna unidades.						if (digito3 > 0)							result.Append(strUnidades[digito3 - 1]);					}					return result.ToString();				}			}		}	}	/// <summary>	/// Determina la como va la sección de los centavos.	/// </summary>	private static string Centavos(double Importe, Currency moneda = Currency.PESOS)	{		return string.Format("{0}/100 {1}", Importe.ToString("0.00").Right(2), moneda == Currency.PESOS ? " m.n." : string.Empty);	}	#endregion}

Si quieres utilizar la función, solo tienes que llamarla como sigue:

NumerosLetras(1523854.45, VbStrConv.ProperCase)
(1523854.45).ToWords();"1523854.45".ToWords();double importe = 1523854.45;importe.ToWords();

Espero que esta funcioncita les sirva de algo, si tienen alguna pregunta no duden en comentar, estaré muy contento de responder a sus preguntas.

Acerca de Enrique Crespo

Apasionado del código fácil y el pensamiento recursivo, me encanta escribir controles y las bases de datos. Desarrollador de día y Barman de noche. También soy un poco gamer. Me gusta la poesia, las historias de terror y los zombies. En una sola palabra: polifacético.

3 comentarios en “Convertir números a letras, recursivo

  1. Hola, me parece muy copleta tu funcion, yo estoy empezando con el mundo del web y de php y me gustaria que me ayudaras a que esta funcion funcionara en php. Grazie.

  2. Muchas Gracias

    La corrección se encuentra donde dice

    “Checar Millares ” ya que al ser 1000 pesos se describirían como “Un Mil Pesos”

    Agregar la validacion para el numero 1000

    If Importe = 1000 Then
    ImporteLetras = ” Un mil ” & ImporteLetras(Right$(strImporte, 6))
    ElseIf Int(Importe / 1000) = 1 Then….

    Gracias Saludos

Deja un comentario

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

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>