¿Cuál es la diferencia entre programación procedimental y programación funcional? [cerrado]
He leído los artículos de Wikipedia tanto sobre programación procedimental como sobre programación funcional , pero todavía estoy un poco confundido. ¿Alguien podría reducirlo al núcleo?
Un lenguaje funcional (idealmente) le permite escribir una función matemática, es decir, una función que toma n argumentos y devuelve un valor. Si se ejecuta el programa, esta función se evalúa lógicamente según sea necesario. 1
Un lenguaje procedimental, por otro lado, realiza una serie de pasos secuenciales . (Existe una forma de transformar la lógica secuencial en lógica funcional llamada estilo de paso de continuación ).
Como consecuencia, un programa puramente funcional siempre produce el mismo valor para una entrada y el orden de evaluación no está bien definido; lo que significa que los valores inciertos como la entrada del usuario o los valores aleatorios son difíciles de modelar en lenguajes puramente funcionales.
1 Como todo lo demás en esta respuesta, eso es una generalización. Esta propiedad, que evalúa un cálculo cuando se necesita su resultado en lugar de secuencialmente cuando se solicita, se conoce como "pereza". No todos los lenguajes funcionales son universalmente vagos, ni la pereza se limita a la programación funcional. Más bien, la descripción dada aquí proporciona un "marco mental" para pensar en diferentes estilos de programación que no son categorías distintas y opuestas, sino ideas fluidas.
Nunca he visto esta definición en ningún otro lugar, pero creo que esto resume bastante bien las diferencias que se dan aquí:
La programación funcional se centra en expresiones .
La programación procesal se centra en declaraciones.
Las expresiones tienen valores. Un programa funcional es una expresión cuyo valor es una secuencia de instrucciones que debe ejecutar la computadora.
Las declaraciones no tienen valores y en cambio modifican el estado de alguna máquina conceptual.
En un lenguaje puramente funcional no habría declaraciones, en el sentido de que no hay forma de manipular el estado (aún podrían tener una construcción sintáctica llamada "declaración", pero a menos que manipule el estado, no lo llamaría declaración en este sentido). ). En un lenguaje puramente procedimental no habría expresiones, todo sería una instrucción que manipula el estado de la máquina.
Haskell sería un ejemplo de lenguaje puramente funcional porque no hay forma de manipular el estado. El código de máquina sería un ejemplo de lenguaje puramente procedimental porque todo en un programa es una declaración que manipula el estado de los registros y la memoria de la máquina.
La parte confusa es que la gran mayoría de los lenguajes de programación contienen expresiones y declaraciones, lo que permite mezclar paradigmas. Los lenguajes se pueden clasificar como más funcionales o más procedimentales en función de cuánto fomentan el uso de declaraciones frente a expresiones.
Por ejemplo, C sería más funcional que COBOL porque una llamada a una función es una expresión, mientras que llamar a un subprograma en COBOL es una declaración (que manipula el estado de las variables compartidas y no devuelve un valor). Python sería más funcional que C porque le permite expresar la lógica condicional como una expresión usando una evaluación de cortocircuito (prueba && ruta1 || ruta2 en lugar de declaraciones if). Scheme sería más funcional que Python porque todo en Scheme es una expresión.
Aún puedes escribir en un estilo funcional en un lenguaje que fomente el paradigma procedimental y viceversa. Simplemente es más difícil y/o más incómodo escribir en un paradigma que el lenguaje no fomenta.
Básicamente los dos estilos son como el Yin y el Yang. Uno es organizado, mientras que el otro es caótico. Hay situaciones en las que la programación funcional es la opción obvia y otras situaciones en las que la programación procedimental es la mejor opción. Es por eso que hay al menos dos lenguajes que recientemente han lanzado una nueva versión que abarca ambos estilos de programación. ( Perl 6 y D 2 )
Procesal:
- La salida de una rutina no siempre tiene una correlación directa con la entrada.
- Todo se hace en un orden específico.
- La ejecución de una rutina puede tener efectos secundarios.
- Tiende a enfatizar la implementación de soluciones de forma lineal.
Perla 6
sub factorial ( UInt:D $n is copy ) returns UInt {
# modify "outside" state
state $call-count++;
# in this case it is rather pointless as
# it can't even be accessed from outside
my $result = 1;
loop ( ; $n > 0 ; $n-- ){
$result *= $n;
}
return $result;
}
re 2
int factorial( int n ){
int result = 1;
for( ; n > 0 ; n-- ){
result *= n;
}
return result;
}
Funcional:
- A menudo recursivo.
- Siempre devuelve la misma salida para una entrada determinada.
- El orden de evaluación suele ser indefinido.
- Debe ser apátrida. es decir, ninguna operación puede tener efectos secundarios.
- Buen ajuste para ejecución paralela
- Tiende a enfatizar un enfoque de divide y vencerás.
- Puede tener la característica de Evaluación diferida.
Haskell
(copiado de Wikipedia );
fac :: Integer -> Integer
fac 0 = 1
fac n | n > 0 = n * fac (n-1)
o en una línea:
fac n = if n > 0 then n * fac (n-1) else 1
Perla 6
proto sub factorial ( UInt:D $n ) returns UInt {*}
multi sub factorial ( 0 ) { 1 }
multi sub factorial ( $n ) { $n * samewith $n-1 } # { $n * factorial $n-1 }
re 2
pure int factorial( invariant int n ){
if( n <= 1 ){
return 1;
}else{
return n * factorial( n-1 );
}
}
Nota al margen:
Factorial es en realidad un ejemplo común para mostrar lo fácil que es crear nuevos operadores en Perl 6 de la misma manera que crearía una subrutina. Esta característica está tan arraigada en Perl 6 que la mayoría de los operadores en la implementación de Rakudo se definen de esta manera. También le permite agregar sus propios candidatos múltiples a los operadores existentes.
sub postfix:< ! > ( UInt:D $n --> UInt )
is tighter(&infix:<*>)
{ [*] 2 .. $n }
say 5!; # 120
Este ejemplo también muestra la creación de rangos ( 2..$n
) y el metaoperador de reducción de listas ( [ OPERATOR ] LIST
) combinado con el operador de multiplicación infija numérica. ( *
)
También muestra que puede poner --> UInt
la firma en lugar de returns UInt
después.
(Puede salirse con la suya comenzando el rango 2
ya que el "operador" de multiplicación regresará 1
cuando se llame sin ningún argumento)
Programación funcional
num = 1
def function_to_add_one(num):
num += 1
return num
function_to_add_one(num) # expression that evaluates to 2
function_to_add_one(num) # expression that evaluates to 2
function_to_add_one(num) # expression that evaluates to 2
function_to_add_one(num) # expression that evaluates to 2
function_to_add_one(num) # expression that evaluates to 2
#Final Output: 2
Programación procesal
num = 1
def procedure_to_add_one():
global num
num += 1
return num
procedure_to_add_one() # statement that changes internal state
procedure_to_add_one() # statement that changes internal state
procedure_to_add_one() # statement that changes internal state
procedure_to_add_one() # statement that changes internal state
procedure_to_add_one() # statement that changes internal state
#Final Output: 6
function_to_add_one
es una función
procedure_to_add_one
es un procedimiento
Incluso si ejecuta la función cinco veces, cada vez devolverá 2
Si ejecuta el procedimiento cinco veces, al final de la quinta ejecución le dará 6 .
Expresiones vs declaraciones
Como puede ver en el código anterior, las funciones siempre se evalúan con la misma salida dada la misma entrada. Y como se evalúan como un valor, en términos técnicos son expresiones.
Por otro lado, los procedimientos no se evalúan como un valor, es posible que no devuelvan nada, solo cambien el estado interno y, por lo tanto, NO son expresiones sino declaraciones.
Después de ver estos ejemplos, la respuesta de Omnimike será aún más fácil de entender.
DESCARGO DE RESPONSABILIDAD: Obviamente esta es una visión hipersimplificada de la realidad. Esta respuesta sólo da una idea de las "funciones" en contraposición a los "procedimientos". Nada mas. Una vez que hayas probado esta intuición superficial pero profundamente penetrante, comienza a explorar los dos paradigmas y empezarás a ver la diferencia con bastante claridad.
Ayuda a mis alumnos, espero que a ti también te ayude.