En esta página
Ejercicio 05: ft_print_comb
Muestra todas las combinaciones únicas de tres dígitos distintos, con los dígitos de cada combinación en orden ascendente y las combinaciones también ordenadas de forma ascendente.
| Dato | Valor |
|---|---|
| Carpeta | ex05/ |
| Archivo | ft_print_comb.c |
| Prototipo | void ft_print_comb(void); |
| Funciones autorizadas | solo write |
Hecho con amor para Laura, por Bogdan.
El archivo
#include <unistd.h>
/*
** Copia local de ft_putchar: el enunciado solo permite entregar este archivo.
** Escribe un unico caracter en la salida estandar.
*/
static void put_char(char c)
{
write(1, &c, 1);
}
/*
** Imprime una combinacion. El separador va delante de todas menos la primera,
** para que la salida no termine en ", ".
*/
static void print_combination(char a, char b, char c, int first)
{
if (!first)
{
put_char(',');
put_char(' ');
}
put_char(a);
put_char(b);
put_char(c);
}
/*
** Tres bucles anidados, uno por digito, con cada digito por delante del
** anterior. Asi no se repite ninguna combinacion y salen en orden.
** La 'a' no pasa de '7' porque detras hacen falta dos digitos mas.
*/
void ft_print_comb(void)
{
char a;
char b;
char c;
int first;
first = 1;
a = '0';
while (a <= '7')
{
b = a + 1;
while (b <= '8')
{
c = b + 1;
while (c <= '9')
{
print_combination(a, b, c, first);
first = 0;
c++;
}
b++;
}
a++;
}
put_char('\n');
}
Línea por línea
#include <unistd.h>
Declara write, la única función de librería permitida. La explicación está en
el ejercicio 00.
static void put_char(char c)
Copia privada de ft_putchar. static la encierra en este archivo, para que no
choque con la de otros ejercicios si compilas varias juntas.
static void print_combination(char a, char b, char c, int first)
Esta función imprime una combinación y decide si lleva separador delante. Recibe cuatro datos:
| Parámetro | Qué es |
|---|---|
a, b, c | los tres dígitos, ya en orden |
first | 1 si es la primera combinación, 0 si no |
Es static porque es un ayudante interno: nadie de fuera del archivo la usa.
if (!first)
! es el operador de negación: !first vale verdadero cuando first es 0. La
idea se lee al revés de lo habitual: si no es la primera, escribe el
separador.
Así la salida empieza directamente en 012 y no termina en ", ". Poner el
separador detrás de cada combinación es el error clásico de este ejercicio.
put_char(','); y put_char(' ');
El separador son dos caracteres: una coma y un espacio. No existe un carácter «coma y espacio»; hay que escribir los dos.
put_char(a); put_char(b); put_char(c);
Los tres dígitos seguidos. a, b y c ya son caracteres, no números, así que
no hay que convertirlos.
void ft_print_comb(void)
La función pública del ejercicio. No devuelve nada y no recibe parámetros.
char a; char b; char c;
Los tres dígitos de la combinación actual. Se declaran arriba, como pide el estilo de la piscina, y se inicializan dentro de los bucles.
int first;
La bandera que recuerda si ya se imprimió algo. Es int y no char porque su
valor es 1 o 0, y así se lee como un booleano.
first = 1;
Antes de la primera combinación, no se ha impreso nada: first vale 1.
a = '0'; y while (a <= '7')
El primer dígito recorre de '0' a '7'. No llega a '8' ni a '9' porque
detrás hacen falta dos dígitos mayores: si a fuera '8', no habría forma de
completar la terna.
b = a + 1; y while (b <= '8')
b arranca uno por encima de a. Eso es lo que garantiza que no se repita
ningún dígito: si a vale '3', b empieza en '4'. Su tope es '8' porque
detrás queda c.
c = b + 1; y while (c <= '9')
c arranca uno por encima de b y llega hasta '9', el último dígito
disponible.
print_combination(a, b, c, first);
Imprime la terna con su separador si toca. Los tres dígitos se pasan por valor: la función recibe copias y no puede cambiarlos.
first = 0;
Después de la primera combinación, ya no es la primera. Esta línea está dentro
del bucle de c, así que se ejecuta en cada terna.
c++; b++; a++;
Cada bucle avanza su variable. Sin estos incrementos el programa no termina nunca: es el error más caro de este ejercicio porque hay tres bucles y basta con olvidar uno.
put_char('\n');
El salto de línea final va después del último while, no dentro. El
enunciado muestra la salida con cat -e y el $ final indica que hay un salto.
Por qué no hay ni un if de comparación
Todo el orden sale de la estructura de los bucles:
| Regla del enunciado | Cómo se cumple |
|---|---|
| dígitos distintos | b empieza en a + 1 y c en b + 1 |
| dígitos ascendentes | los bucles solo generan a < b < c |
| combinaciones ascendentes | recorrido lexicográfico de a, luego b, luego c |
| sin repetir combinaciones | cada terna se genera exactamente una vez |
987 no aparece porque los bucles nunca generan un orden descendente. 999 no
aparece porque b y c siempre son mayores que el anterior. No hace falta
comprobarlo: la estructura ya lo impide.
Salida esperada
El principio y el final. Los puntos suspensivos son la abreviatura del enunciado, no salida real:
012, 013, 014, 015, 016, 017, 018, 019, 023, ..., 789
Salen 120 combinaciones: las formas de elegir 3 dígitos entre 10, que es
10 × 9 × 8 / (3 × 2 × 1).
Casos límite
| Detalle | Decisión |
|---|---|
| Separador final | no se imprime: va delante, no detrás |
| Salto de línea final | sí, porque el enunciado lo muestra con cat -e |
| Repeticiones | imposibles por el orden de los bucles |
Tope de a | '7', no '9': hay que dejar sitio a b y c |
Errores típicos
| Mensaje o síntoma | Causa | Solución |
|---|---|---|
termina en , | separador después de cada terna | ponlo delante con first |
salen 011, 022… | b empieza en '0' | b = a + 1 |
| salen ternas desordenadas | c no depende de b | c = b + 1 |
se corta en 789 sin salto | falta put_char('\n') | añádelo al final |
| no termina | falta algún ++ | revisa los tres bucles |
Comprobarlo
cat > /tmp/main.c <<'EOF'
void ft_print_comb(void);
int main(void)
{
ft_print_comb();
return (0);
}
EOF
cc -Wall -Wextra -Werror ex05/ft_print_comb.c /tmp/main.c -o /tmp/ex05
/tmp/ex05 > /tmp/salida.txt
wc -c /tmp/salida.txt
Compara contra la salida esperada con cmp, que no imprime nada cuando los dos
archivos son idénticos:
cmp /tmp/salida.txt /tmp/esperado.txt