programming languages - science - Comprender verdaderamente la diferencia entre procedural y funcional
types of programming (8)
Realmente estoy teniendo dificultades para entender la diferencia entre los paradigmas de programación funcional y de procedimiento .
Aquí están los primeros dos párrafos de la entrada de la Wikipedia sobre programación funcional :
En informática, la programación funcional es un paradigma de programación que trata la computación como la evaluación de las funciones matemáticas y evita los datos de estado y mutables. Enfatiza la aplicación de funciones, en contraste con el estilo de programación imperativa, que enfatiza los cambios de estado. La programación funcional tiene sus raíces en el cálculo lambda, un sistema formal desarrollado en la década de 1930 para investigar la definición de funciones, la aplicación de funciones y la recursión. Muchos lenguajes de programación funcionales se pueden ver como elaboraciones del cálculo lambda.
En la práctica, la diferencia entre una función matemática y la noción de "función" utilizada en la programación imperativa es que las funciones imperativas pueden tener efectos secundarios, cambiando el valor del estado del programa. Debido a esto, carecen de transparencia referencial, es decir, la expresión del mismo idioma puede dar lugar a valores diferentes en diferentes momentos dependiendo del estado del programa en ejecución. Por el contrario, en el código funcional, el valor de salida de una función depende solo de los argumentos que ingresan a la función, por lo que llamar a una función
f
dos veces con el mismo valor para un argumentox
producirá el mismo resultadof(x)
ambas veces. La eliminación de los efectos secundarios puede hacer que sea mucho más fácil comprender y predecir el comportamiento de un programa, que es una de las motivaciones clave para el desarrollo de la programación funcional.
En el párrafo 2 donde dice
Por el contrario, en el código funcional, el valor de salida de una función depende solo de los argumentos que ingresan a la función, por lo que llamar a una función
f
dos veces con el mismo valor para un argumentox
producirá el mismo resultadof(x)
ambas veces.
¿No es el mismo caso exacto para la programación de procedimientos?
¿Qué debe uno buscar en el procedimiento vs funcional que se destacan?
En el paradigma de procedimiento (¿debo decir "programación estructurada" en su lugar?), Ha compartido la memoria mutable y las instrucciones que la leen / escriben en una secuencia (una después de la otra).
En el paradigma funcional, tiene variables y funciones (en el sentido matemático: las variables no varían con el tiempo, las funciones solo pueden calcular algo en función de sus entradas).
(Esto se simplifica demasiado, por ejemplo, los FPL suelen tener instalaciones para trabajar con memoria mutable, mientras que los lenguajes de procedimiento a menudo pueden admitir procedimientos de orden superior, por lo que las cosas no son tan nítidas, pero esto debería darte una idea)
En la programación funcional para razonar sobre el significado de un símbolo (variable o nombre de función), solo necesita saber 2 cosas: alcance actual y el nombre del símbolo. Si tiene un lenguaje puramente funcional con inmutabilidad, ambos son conceptos "estáticos" (lo siento por un nombre mal sobrecargado), lo que significa que puede ver ambos, el alcance actual y el nombre, con solo mirar el código fuente.
En la programación de procedimientos, si desea responder a la pregunta cuál es el valor detrás de x
, también necesita saber cómo llegó allí, el alcance y el nombre por sí solos no son suficientes. Y esto es lo que vería como el mayor desafío porque esta ruta de ejecución es una propiedad de "tiempo de ejecución" y puede depender de tantas cosas diferentes, que la mayoría de la gente aprende a depurarla y no a intentar recuperar la ruta de ejecución.
La verdadera diferencia entre la programación funcional y la imperativa es la mentalidad: los programadores imperativos piensan en variables y bloques de memoria, mientras que los programadores funcionales piensan: "¿Cómo puedo transformar mis datos de entrada en mis datos de salida?", Su "programa" es la tubería. y un conjunto de transformaciones en los datos para llevarlo de la Entrada a la Salida. Esa es la parte interesante de la OMI, no el bit "No usarás variables".
Como consecuencia de este modo de pensar, los programas de PF suelen describir lo que sucederá, en lugar del mecanismo específico de cómo sucederá, esto es poderoso porque si podemos decir claramente qué significa "Seleccionar" y "Dónde" y "Agregar", son libres de cambiar sus implementaciones, al igual que hacemos con AsParallel () y de repente nuestra aplicación de un único subproceso se amplía a n núcleos.
No estoy de acuerdo con la respuesta de WReach. Vamos a deconstruir su respuesta un poco para ver de dónde viene el desacuerdo.
Primero, su código:
function allOdd(words) {
var result = true;
for (var i = 0; i < length(words); ++i) {
var len = length(words[i]);
if (!odd(len)) {
result = false;
break;
}
}
return result;
}
y
function allOdd(words) {
return apply(and, map(compose(odd, length), words));
}
Lo primero que debe notar es que está confundiendo:
- Funcional
- Orientado a la expresión
- Iterador céntrico
programación, y falta la capacidad de la programación de estilo iterativo para tener un flujo de control más explícito que un estilo funcional típico.
Hablemos de esto rápidamente.
El estilo centrado en la expresión es uno en el que las cosas, en la medida de lo posible, evalúan las cosas. Aunque los lenguajes funcionales son famosos por su amor por las expresiones, en realidad es posible tener un lenguaje funcional sin expresiones configurables. Voy a hacer una, donde no hay expresiones, simplemente declaraciones.
lengths: map words length
each_odd: map lengths odd
all_odd: reduce each_odd and
Esto es prácticamente lo mismo que se ha dado antes, excepto que las funciones están encadenadas puramente a través de cadenas de enunciados y enlaces.
Un estilo de programación centrada en un iterador podría ser uno tomado por Python. Usemos un estilo puramente iterativo, centrado en el iterador:
def all_odd(words):
lengths = (len(word) for word in words)
each_odd = (odd(length) for length in lengths)
return all(each_odd)
Esto no es funcional, porque cada cláusula es un proceso iterativo, y están unidas por pausa explícita y reanudación de los marcos de pila. La sintaxis puede estar inspirada parcialmente en un lenguaje funcional, pero se aplica a una realización completamente iterativa de la misma.
Por supuesto, puedes comprimir esto:
def all_odd(words):
return all(odd(len(word)) for word in words)
Imperativo no se ve tan mal ahora, ¿eh? :)
El punto final fue sobre un flujo de control más explícito. Vamos a reescribir el código original para hacer uso de esto:
function allOdd(words) {
for (var i = 0; i < length(words); ++i) {
if (!odd(length(words[i]))) {
return false;
}
}
return true;
}
Usando iteradores puedes tener:
function allOdd(words) {
for (word : words) { if (!odd(length(word))) { return false; } }
return true;
}
Entonces, ¿qué sentido tiene un lenguaje funcional si la diferencia es entre:
return all(odd(len(word)) for word in words)
return apply(and, map(compose(odd, length), words))
for (word : words) { if (!odd(length(word))) { return false; } }
return true;
La principal característica definitiva de un lenguaje de programación funcional es que elimina la mutación como parte del típico modelo de programación. La gente suele interpretar que un lenguaje de programación funcional no tiene enunciados o utiliza expresiones, pero se trata de simplificaciones. Un lenguaje funcional reemplaza el cálculo explícito con una declaración de comportamiento, que el lenguaje luego realiza una reducción.
Restringirse a este subconjunto de funcionalidades le permite tener más garantías sobre el comportamiento de sus programas, y esto le permite componerlos más libremente.
Cuando tienes un lenguaje funcional, crear nuevas funciones generalmente es tan simple como componer funciones estrechamente relacionadas.
all = partial(apply, and)
Esto no es simple, o quizás imposible, si no ha controlado explícitamente las dependencias globales de una función. La mejor característica de la programación funcional es que puede crear consistentemente más abstracciones genéricas y confiar en que se pueden combinar en un todo mayor.
Recientemente he estado pensando en la diferencia en términos del problema de expresión . La descripción de Phil Wadler es frecuentemente citada, pero la respuesta aceptada a esta pregunta es probablemente más fácil de seguir. Básicamente, parece que los lenguajes imperativos tienden a elegir un enfoque para el problema, mientras que los lenguajes funcionales tienden a elegir el otro.
The Charming Python: la programación funcional en Python de IBM Developerworks realmente me ayudó a entender la diferencia.
Especialmente para alguien que conoce un poco a Python, los ejemplos de código en este artículo en los que se contrastan diferentes aspectos funcionales y de procedimiento pueden aclarar la diferencia entre la programación de procedimiento y la funcional.
Programación Funcional
La programación funcional se refiere a la capacidad de tratar funciones como valores.
Consideremos una analogía con valores "regulares". Podemos tomar dos valores enteros y combinarlos usando el operador +
para obtener un nuevo entero. O podemos multiplicar un número entero por un número de coma flotante para obtener un número de coma flotante.
En la programación funcional, podemos combinar dos valores de función para producir un nuevo valor de función usando operadores como compose o lift . O podemos combinar un valor de función y un valor de datos para producir un nuevo valor de datos usando operadores como map o fold .
Tenga en cuenta que muchos idiomas tienen capacidades de programación funcional, incluso lenguajes que generalmente no se consideran lenguajes funcionales. Incluso el abuelo FORTRAN apoyó los valores de las funciones, aunque no ofreció mucho en la forma de combinar funciones. Para que un lenguaje se denomine "funcional", debe abarcar las capacidades de programación funcional de una manera importante.
Programación procesal
La programación procedimental se refiere a la capacidad de encapsular una secuencia común de instrucciones en un procedimiento para que esas instrucciones puedan invocarse desde muchos lugares sin recurrir a copiar y pegar. Como los procedimientos fueron un desarrollo muy temprano en la programación, la capacidad está casi invariablemente vinculada con el estilo de programación que exige la programación en máquina o ensamblador: un estilo que enfatiza la noción de ubicaciones de almacenamiento e instrucciones que mueven datos entre esas ubicaciones.
Contraste
Los dos estilos no son realmente opuestos: son diferentes entre sí. Hay idiomas que abarcan por completo ambos estilos (LISP, por ejemplo). El siguiente escenario puede dar una idea de algunas diferencias en los dos estilos. Vamos a escribir un código para un requisito sin sentido donde queremos determinar si todas las palabras en una lista tienen un número impar de caracteres. Primero, estilo de procedimiento:
function allOdd(words) {
var result = true;
for (var i = 0; i < length(words); ++i) {
var len = length(words[i]);
if (!odd(len)) {
result = false;
break;
}
}
return result;
}
Consideraré que este ejemplo es comprensible. Ahora, estilo funcional:
function allOdd(words) {
return apply(and, map(compose(odd, length), words));
}
Trabajando desde adentro hacia afuera, esta definición hace las siguientes cosas:
-
compose(odd, length)
combina las funcionesodd
y delength
para producir una nueva función que determina si la longitud de una cuerda es impar. -
map(..., words)
llama a esa nueva función para cada elemento enwords
, y finalmente devuelve una nueva lista de valores booleanos, cada uno indicando si la palabra correspondiente tiene un número impar de caracteres. -
apply(and, ...)
aplica el operador "y" a la lista resultante, y -ing todos los booleanos para obtener el resultado final.
Puede ver a partir de estos ejemplos que la programación de procedimientos está muy preocupada por mover los valores en variables y describir explícitamente las operaciones necesarias para producir el resultado final. Por el contrario, el estilo funcional enfatiza la combinación de funciones requeridas para transformar la entrada inicial a la salida final.
El ejemplo también muestra los tamaños relativos típicos de código de procedimiento frente a código funcional. Además, demuestra que las características de rendimiento del código de procedimiento pueden ser más fáciles de ver que las del código funcional. Considere: ¿las funciones calculan las longitudes de todas las palabras de la lista, o se detienen inmediatamente después de encontrar la primera palabra de longitud uniforme? Por otro lado, el código funcional permite una implementación de alta calidad para realizar una optimización bastante seria, ya que expresa principalmente el intento en lugar de un algoritmo explícito.
Otras lecturas
Esta pregunta surge mucho ... ver, por ejemplo:
- ¿Cuál es la diferencia entre la programación de procedimientos y la programación funcional?
- ¿Puede alguien darme ejemplos de programación funcional versus programación imperativa / procesal?
- OOP vs Programación funcional vs Procedural
La conferencia del premio Turing de John Backus detalla las motivaciones para la programación funcional en gran detalle:
¿Se puede liberar la programación del estilo von Neumann?
Realmente no debería mencionar ese documento en el presente contexto porque se vuelve bastante técnico, bastante rápido. Simplemente no pude resistir porque creo que es verdaderamente fundamental.
Adición - 2013
Los comentaristas señalan que los lenguajes contemporáneos populares ofrecen otros estilos de programación además de los procedimentales y funcionales. Tales idiomas a menudo ofrecen uno o más de los siguientes estilos de programación:
- consulta (por ejemplo, listas de comprensión, consulta integrada al lenguaje)
- flujo de datos (por ejemplo, iteración implícita, operaciones masivas)
- orientado a objetos (por ejemplo, datos y métodos encapsulados)
- orientado al lenguaje (por ejemplo, sintaxis específica de la aplicación, macros)
Consulte los comentarios a continuación para ver ejemplos de cómo los ejemplos de pseudocódigo en esta respuesta pueden beneficiarse de algunas de las instalaciones disponibles de esos otros estilos. En particular, el ejemplo de procedimiento se beneficiará de la aplicación de prácticamente cualquier construcción de nivel superior.
Los ejemplos expuestos deliberadamente evitan la mezcla en estos otros estilos de programación con el fin de enfatizar la distinción entre los dos estilos en discusión.
Isn''t that the same exact case for procedural programming?
No, porque el código de procedimiento puede tener efectos secundarios. Por ejemplo, puede almacenar estado entre llamadas.
Dicho esto, es posible escribir código que satisfaga esta restricción en los lenguajes considerados de procedimiento. Y también es posible escribir código que rompa esta restricción en algunos lenguajes considerados funcionales.