Si mi función cumple con dos requisitos por debajo, creo que la función Sum
devuelve la suma de los elementos en una lista en la que el elemento se evalúa como verdadero para la condición dada califica para ser referida como función pura, ¿no es así?
1) Para un conjunto dado de i / p, se devuelve la misma o / p independientemente del tiempo cuando se llama a la función
2) No tiene ningún efecto secundario
public int Sum(Func<int,bool> predicate, IEnumerable<int> numbers){
int result = 0;
foreach(var item in numbers)
if(predicate(item)) result += item;
return result;
}
Ejemplo: Sum(x=>x%2==0, new List<int> {1,2,3,4,5...100});
La razón por la que hago esta pregunta es porque veo casi todas las personas que aconsejan evitar el operador de asignación y los bucles porque es un estilo de programación imperativo. Entonces, ¿qué puede salir mal con el ejemplo anterior que utiliza bucles y operadores de asignación en el contexto de la programación de funciones?
fuente
item
variable muta en el bucle.Respuestas:
¿Qué hay en la programación funcional que hace la diferencia?
La programación funcional es por principio declarativa . Dices cuál es tu resultado en lugar de cómo calcularlo.
Echemos un vistazo a la implementación realmente funcional de su fragmento. En Haskell sería:
¿Está claro cuál es el resultado? Entonces, es la suma de los números que se encuentran con el predicado. ¿Cómo se calcula? No me importa, pregunta al compilador.
Posiblemente podría decir que usar
sum
yfilter
es un truco y no cuenta. Deje que se implemente sin estos ayudantes (aunque la mejor forma sería implementarlos primero)La solución "Functional Programming 101" que no utiliza
sum
es con recursividad:Todavía está bastante claro cuál es el resultado en términos de llamada de función única. Es
0
, orecursive call + h or 0
, dependiendo depred h
. Sigue siendo bastante sencillo, incluso si el resultado final no es inmediatamente obvio (aunque con un poco de práctica, esto realmente se lee como unfor
bucle).Compare eso con su versión:
Cual es el resultado? Oh, ya veo: una sola
return
declaración, sin sorpresas aquí:return result
.Pero que es
result
?int result = 0
? No parece correcto Haces algo más tarde con eso0
. Ok, le agregasitem
s. Y así.Por supuesto, para la mayoría de los programadores, es bastante obvio lo que sucede en una función simple como esta, pero agregue una
return
declaración adicional o algo así y de repente se vuelve más difícil de rastrear. Todo el código trata sobre cómo y qué queda para que el lector descubra: este es claramente un estilo muy imperativo .Entonces, ¿las variables y los bucles están mal?
No.
Hay muchas cosas que son mucho más fáciles de explicar por ellos, y muchos algoritmos que requieren que el estado mutable sea rápido. Pero las variables son inherentemente imperativas, explicando cómo en lugar de qué , y dando poca predicción de cuál puede ser su valor unas pocas líneas más tarde o después de algunas iteraciones de bucle. Los bucles generalmente requieren que el estado tenga sentido, por lo que también son inherentemente imperativos.
Las variables y los bucles simplemente no son programación funcional.
Resumen
La programación funcional contemporánea es un poco más de estilo y una forma útil de pensar que un paradigma. Esta mentalidad tiene una fuerte preferencia por las funciones puras, pero en realidad es solo una pequeña parte.
Los lenguajes más extendidos le permiten usar algunas construcciones funcionales. Por ejemplo, en Python puede elegir entre:
o
o
Estas expresiones funcionales se ajustan bien a ese tipo de problemas y simplemente acortan el código (y más corto es bueno ). No debe reemplazar irremediablemente el código imperativo con ellos, pero cuando encajan, casi siempre son una mejor opción.
fuente
El uso del estado mutable generalmente se desaconseja en la programación funcional. Los bucles se desaconsejan como consecuencia, porque los bucles solo son útiles en combinación con el estado mutable.
La función en su conjunto es pura, lo cual es excelente, pero el paradigma de la programación funcional no solo se aplica a nivel de funciones completas. También desea evitar el estado mutable también a nivel local, dentro de las funciones. Y el razonamiento es básicamente el mismo: evitar el estado mutable hace que el código sea más fácil de entender y evita ciertos errores.
En su caso, podría escribir,
numbers.Where(predicate).Sum()
que es claramente mucho más simple. Y más simple significa menos errores.fuente
Where
innumbers.Where(predicate).Sum()
, hace uso deforeach
loop.Si bien tiene razón en que desde el punto de vista de un observador externo, su
Sum
función es pura, la implementación interna claramente no es pura: tiene un estado almacenado en elresult
que muta repetidamente. Una de las razones para evitar el estado mutable es porque produce una mayor carga cognitiva en el programador, lo que a su vez conduce a más errores [cita requerida] .Si bien en un ejemplo simple como este, la cantidad de estado mutable que se almacena es probablemente lo suficientemente pequeña como para que no cause problemas graves, el principio general aún se aplica. Un ejemplo de juguete como
Sum
probablemente no sea la mejor manera de ilustrar la ventaja de la programación funcional sobre el imperativo: intente hacer algo con mucho estado mutable y las ventajas pueden volverse más claras.fuente