Filter, Map y Reduce
Los tres sirven para procesar arreglos:
FILTER → selecciona elementos
MAP → transforma elementos
REDUCE → combina elementos en un resultado
FILTER
[1 2 3 4 5 6]
↓
[2 4 6]
MAP
[1 2 3 4 5]
↓
[2 4 6 8 10]
REDUCE
[1 2 3 4 5]
↓
15
En C no existen como funciones estándar llamadas exactamente filter, map y reduce. Nosotros podemos construirlas.
Y para hacerlas generales, usamos punteros a funciones.
FILTER
filter recorre un arreglo y selecciona solamente los elementos que cumplen una condición.
Por ejemplo:
10 3 8 7 4 11
Queremos los pares:
10 8 4
La condición sería:
intesPar(intnumero)
{
return numero%2==0;
}
Filter usando punteros
Podemos hacer:
void filter(int*vector,intcantidad,int*resultado,int*cantidadResultado,int (*condicion)(int))
{
int*p=vector;
int*r=resultado;
*cantidadResultado=0;
while (p<vector+cantidad)
{if (condicion(*p))
{*r=*p;
r++;
(*cantidadResultado)++;
}
p++;
}
}
Fijate en esta parte:
if (condicion(*p))
p es un puntero.
*p
es el valor actual.
Y:
condicion(*p)
ejecuta la función que pasamos.
Ejemplo completo de FILTER
#include <stdio.h>
int esPar(int numero)
{
return numero % 2 == 0;
}
void filter(
int *vector,
int cantidad,
int *resultado,
int *cantidadResultado,
int (*condicion)(int)
)
{
int *p = vector;
int *r = resultado;
*cantidadResultado = 0;
while (p < vector + cantidad)
{
if (condicion(*p))
{
*r = *p;
r++;
(*cantidadResultado)++;
}
p++;
}
}
void mostrar(int *vector, int cantidad)
{
int *p = vector;
while (p < vector + cantidad)
{
printf("%d ", *p);
p++;
}
printf("\n");
}
int main()
{
int vector[] = {10, 3, 8, 7, 4, 11};
int resultado[6];
int cantidadResultado;
filter(
vector,
6,
resultado,
&cantidadResultado,
esPar
);
mostrar(resultado, cantidadResultado);
return 0;
}
Resultado:
10 8 4
MAP
Ahora vamos con map.
¿Qué hace?
map transforma cada elemento.
Por ejemplo:
1 2 3 4 5
Queremos multiplicar cada elemento por 2:
2 4 6 8 10
Tenemos una función:
int duplicar(int numero)
{
return numero*2;
}
MAP usando punteros
void map(
int *vector,
int cantidad,
int (*transformacion)(int)
)
{
int *p = vector;
while (p < vector + cantidad)
{
*p = transformacion(*p);
p++;
}
}
Esta línea es fundamental:
*p=transformacion(*p);
Significa:
Tomo el valor apuntado por
p, lo transformo y vuelvo a guardarlo en el mismo lugar.
Ejemplo completo de MAP
#include <stdio.h>
int duplicar(int numero)
{
return numero * 2;
}
void map(
int *vector,
int cantidad,
int (*transformacion)(int)
)
{
int *p = vector;
while (p < vector + cantidad)
{
*p = transformacion(*p);
p++;
}
}
void mostrar(int *vector, int cantidad)
{
int *p = vector;
while (p < vector + cantidad)
{
printf("%d ", *p);
p++;
}
printf("\n");
}
int main()
{
int vector[] = {1, 2, 3, 4, 5};
map(vector, 5, duplicar);
mostrar(vector, 5);
return 0;
}
Resultado:
2 4 6 8 10
REDUCE
Ahora viene reduce.
¿Qué hace?
Reduce toma muchos elementos y genera un solo resultado.
Por ejemplo:
1 2 3 4 5
Queremos sumar:
1 + 2 + 3 + 4 + 5
Resultado:
15
Reduce usando punteros
Podemos definir una función que indique cómo combinar dos valores:
int sumar(inta, intb){returna+b;}
Y nuestro reduce:
int reduce(
int *vector,
int cantidad,
int inicial,
int (*operacion)(int, int)
)
{
int resultado = inicial;
int *p = vector;
while (p < vector + cantidad)
{
resultado = operacion(resultado, *p);
p++;
}
return resultado;
}
La parte importante es:
resultado=operacion(resultado, *p);
Ejemplo completo
#include <stdio.h>
int sumar(int a, int b)
{
return a + b;
}
int reduce(
int *vector,
int cantidad,
int inicial,
int (*operacion)(int, int)
)
{
int resultado = inicial;
int *p = vector;
while (p < vector + cantidad)
{
resultado = operacion(resultado, *p);
p++;
}
return resultado;
}
int main()
{
int vector[] = {1, 2, 3, 4, 5};
int resultado = reduce(
vector,
5,
0,
sumar
);
printf("Resultado: %d\n", resultado);
return 0;
}
Resultado:
Resultado: 15
¿Por qué pasamos 0?
Tenemos:
reduce(vector, 5, 0, sumar);
El 0 es el valor inicial.
Internamente pasa esto:
resultado = 0
resultado = 0 + 1 → 1
resultado = 1 + 2 → 3
resultado = 3 + 3 → 6
resultado = 6 + 4 → 10
resultado = 10 + 5 → 15
Por eso devuelve:
15
Podemos hacer otros Reduce
Lo interesante es que no tenemos que hacer solamente sumas.
Podemos hacer:
int multiplicar(int a, int b)
{
return a*b;
}
Entonces:
int resultado=reduce(vector, 5, 1, multiplicar);
Con:
1 2 3 4 5
obtenemos:
1 × 2 × 3 × 4 × 5 = 120
FILTER + MAP + REDUCE juntos
Ahora viene la parte más interesante.
Supongamos que tenemos:
1 2 3 4 5 6 7 8
Queremos:
- Quedarnos con los pares.
- Multiplicarlos por 10.
- Sumarlos.
Paso 1 — FILTER
1 2 3 4 5 6 7 8
↓
2 4 6 8
Paso 2 — MAP
2 4 6 8
↓
20 40 60 80
Paso 3 — REDUCE
20 + 40 + 60 + 80
Resultado:
200
11. Programa completo
#include <stdio.h>
int esPar(int numero)
{
return numero % 2 == 0;
}
int multiplicarPorDiez(int numero)
{
return numero * 10;
}
int sumar(int a, int b)
{
return a + b;
}
void filter(
int *vector,
int cantidad,
int *resultado,
int *cantidadResultado,
int (*condicion)(int)
)
{
int *p = vector;
int *r = resultado;
*cantidadResultado = 0;
while (p < vector + cantidad)
{
if (condicion(*p))
{
*r = *p;
r++;
(*cantidadResultado)++;
}
p++;
}
}
void map(
int *vector,
int cantidad,
int (*transformacion)(int)
)
{
int *p = vector;
while (p < vector + cantidad)
{
*p = transformacion(*p);
p++;
}
}
int reduce(
int *vector,
int cantidad,
int inicial,
int (*operacion)(int, int)
)
{
int resultado = inicial;
int *p = vector;
while (p < vector + cantidad)
{
resultado = operacion(resultado, *p);
p++;
}
return resultado;
}
void mostrar(int *vector, int cantidad)
{
int *p = vector;
while (p < vector + cantidad)
{
printf("%d ", *p);
p++;
}
printf("\n");
}
int main()
{
int vector[] = {1, 2, 3, 4, 5, 6, 7, 8};
int filtrados[8];
int cantidadFiltrados;
/*
FILTER
Nos quedamos solamente
con los números pares.
*/
filter(
vector,
8,
filtrados,
&cantidadFiltrados,
esPar
);
printf("Despues de FILTER:\n");
mostrar(filtrados, cantidadFiltrados);
/*
MAP
Multiplicamos cada elemento
por 10.
*/
map(
filtrados,
cantidadFiltrados,
multiplicarPorDiez
);
printf("Despues de MAP:\n");
mostrar(filtrados, cantidadFiltrados);
/*
REDUCE
Sumamos todos los elementos.
*/
int resultado = reduce(
filtrados,
cantidadFiltrados,
0,
sumar
);
printf("Despues de REDUCE:\n");
printf("%d\n", resultado);
return 0;
}
Resultado:
Despues de FILTER:2 4 6 8
Despues de MAP:20 40 60 80
Despues de REDUCE:200
Lo más importante: punteros a funciones
Cuando ves:
int (*condicion)(int)
significa:
condiciones un puntero a una función que recibe uninty devuelve unint.
Cuando ves:
int (*transformacion)(int)
significa:
transformacionapunta a una función que recibe uninty devuelve unint.
Y:
int (*operacion)(int, int)
significa:
operacionapunta a una función que recibe dosinty devuelve unint.
Cómo saber qué poner
Esta tabla te puede salvar en un ejercicio:
| Caso | Puntero a función |
|---|---|
| Preguntar si cumple condición | int (*condicion)(int) |
| Transformar un elemento | int (*transformacion)(int) |
| Combinar dos elementos | int (*operacion)(int, int) |
Por ejemplo:
FILTER
intesPar(int x)
Entonces:
int (*condicion)(int)
MAP
intduplicar(int x)
Entonces:
int (*transformacion)(int)
REDUCE
intsumar(int a, int b)
Entonces:
int (*operacion)(int, int)
Resumen
Si te preguntan «¿qué es Filter, Map y Reduce?», pensalo así:
FILTER Muchos → algunos
MAP Muchos → muchos transformados
REDUCE Muchos → uno
Y con punteros:
FILTER →int (*condicion)(int)
MAP →int (*transformacion)(int)
REDUCE →int (*operacion)(int, int)
La clave es que C permite pasar funciones como parámetros mediante punteros a funciones. Eso hace posible implementar estos tres patrones de manera general, en lugar de escribir una función distinta para cada condición, transformación o operación.