¿Cuál es la diferencia entre los lenguajes de programación funcionales e imperativos?

159

La mayoría de los lenguajes principales, incluidos los lenguajes de programación orientada a objetos (OOP) como C #, Visual Basic, C ++ y Java, fueron diseñados para soportar principalmente la programación imperativa (de procedimiento), mientras que los lenguajes similares a Haskell / gofer son puramente funcionales. ¿Alguien puede explicar cuál es la diferencia entre estas dos formas de programación?

Sé que depende de los requisitos del usuario elegir la forma de programación, pero ¿por qué se recomienda aprender lenguajes de programación funcionales?

Swapnil Kotwal
fuente
1
posible duplicado de programación funcional vs programación orientada a objetos
Floris Velleman
1
revisa este otro [post] [1] Describe claramente las diferencias. [1]: stackoverflow.com/questions/602444/…
theta

Respuestas:

160

Definición: Un lenguaje imperativo usa una secuencia de enunciados para determinar cómo alcanzar un objetivo determinado. Se dice que estas declaraciones cambian el estado del programa a medida que cada una se ejecuta a su vez.

Ejemplos: Java es un lenguaje imperativo. Por ejemplo, se puede crear un programa para agregar una serie de números:

 int total = 0;
 int number1 = 5;
 int number2 = 10;
 int number3 = 15;
 total = number1 + number2 + number3; 

Cada declaración cambia el estado del programa, desde la asignación de valores a cada variable hasta la adición final de esos valores. Usando una secuencia de cinco declaraciones, se le dice explícitamente al programa cómo sumar los números 5, 10 y 15.

Lenguajes funcionales: el paradigma de programación funcional se creó explícitamente para admitir un enfoque funcional puro para la resolución de problemas. La programación funcional es una forma de programación declarativa.

Ventajas de las funciones puras: La razón principal para implementar transformaciones funcionales como funciones puras es que las funciones puras son componibles: es decir, autónomas y sin estado. Estas características aportan una serie de beneficios, que incluyen los siguientes: Mayor legibilidad y facilidad de mantenimiento. Esto se debe a que cada función está diseñada para realizar una tarea específica dados sus argumentos. La función no depende de ningún estado externo.

Desarrollo reiterativo más fácil. Debido a que el código es más fácil de refactorizar, los cambios en el diseño a menudo son más fáciles de implementar. Por ejemplo, suponga que escribe una transformación complicada y luego se da cuenta de que parte del código se repite varias veces en la transformación. Si refactoriza a través de un método puro, puede llamar a su método puro a voluntad sin preocuparse por los efectos secundarios.

Pruebas y depuración más fáciles. Debido a que las funciones puras se pueden probar más fácilmente de forma aislada, puede escribir código de prueba que llame a la función pura con valores típicos, casos límite válidos y casos límite no válidos.

Para personas OOP o lenguajes imperativos:

Los lenguajes orientados a objetos son buenos cuando tiene un conjunto fijo de operaciones sobre cosas y a medida que su código evoluciona, principalmente agrega cosas nuevas. Esto se puede lograr agregando nuevas clases que implementen métodos existentes y las clases existentes se dejen solas.

Los lenguajes funcionales son buenos cuando tiene un conjunto fijo de cosas y, a medida que su código evoluciona, agrega principalmente nuevas operaciones en cosas existentes. Esto se puede lograr agregando nuevas funciones que computan con los tipos de datos existentes y las funciones existentes se dejan solas.

Contras:

Depende de los requisitos del usuario para elegir la forma de programación, por lo que hay daño solo cuando los usuarios no eligen la forma correcta.

Cuando la evolución va por el camino equivocado, tienes problemas:

  • Agregar una nueva operación a un programa orientado a objetos puede requerir editar muchas definiciones de clase para agregar un nuevo método
  • Agregar un nuevo tipo de cosas a un programa funcional puede requerir editar muchas definiciones de funciones para agregar un nuevo caso.
Chris
fuente
10
Una función pura en este caso es el equivalente de una función matemática. Las mismas entradas siempre se asignarán a las mismas salidas. También carecen de efectos secundarios (aparte de devolver un valor o valores), lo que significa que el compilador puede hacer algunas optimizaciones geniales, y hace que sea más fácil ejecutar la función en paralelo ya que no hay nada con lo que lidiar.
WorBlux
Entonces, ¿las formas correctas y las mejores prácticas de componer aplicaciones oop mantenibles y comprobables tienden a diseñar código imperativo con un estado mental declerativo?
Kemal Gültekin
44
No veo una diferencia clara en el texto donde se resaltan las características de cada programación. La mayor parte de la descripción de la programación de procedimientos puede intercambiarse por el texto de programación imperativo y viceversa.
AxeEffect
77
Esta respuesta intenta aclarar qué es la programación funcional, pero ni siquiera se molesta en definir qué es una función pura. No veo cómo alguien podría leer esta respuesta y salir con la confianza de saber la diferencia entre la programación declarativa y la de procedimiento.
Ringo
230

Aquí está la diferencia:

Imperativo:

  • comienzo
  • Enciende tus zapatos talla 9 1/2.
  • Haga espacio en su bolsillo para guardar un conjunto [7] de llaves.
  • Pon las llaves en la habitación para las llaves en el bolsillo.
  • Entrar al garaje.
  • Garaje abierto.
  • Entra en el coche.

... y así sucesivamente ...

  • Pon la leche en el refrigerador.
  • Detener.

Declarativo, de lo que funcional es una subcategoría:

  • La leche es una bebida saludable, a menos que tenga problemas para digerir la lactosa.
  • Por lo general, uno almacena la leche en un refrigerador.
  • Un refrigerador es una caja que mantiene las cosas frescas.
  • Una tienda es un lugar donde se venden artículos.
  • Por "vender" nos referimos al intercambio de cosas por dinero.
  • Además, el intercambio de dinero por cosas se llama "comprar".

... y así sucesivamente ...

  • Asegúrese de tener leche en el refrigerador (cuando la necesitemos, para los lenguajes funcionales perezosos).

Resumen: En los idiomas imperativos, le dice a la computadora cómo cambiar bits, bytes y palabras en su memoria y en qué orden. En los funcionales, le decimos a la computadora qué cosas, acciones, etc. son. Por ejemplo, decimos que el factorial de 0 es 1, y el factorial de cualquier otro número natural es el producto de ese número y el factorial de su predecesor. No decimos: Para calcular el factorial de n, reserve una región de memoria y almacene 1 allí, luego multiplique el número en esa región de memoria con los números 2 a n y almacene el resultado en el mismo lugar, y al final, la región de memoria contendrá el factorial.

Ingo
fuente
1
Gracias. Esa es una excelente manera de verlo.
L-Samuels
55
Me gustó tu explicación @Igno, pero algo todavía no está claro para mí. En Declarative, a pesar de que solo cuenta cosas, aún necesita cambiar bits y hacer cambios en los estados de la máquina para proceder correctamente. Me confunde que, de alguna manera, Declarativo es similar a la Programación de procedimientos (como las Funciones C) , y aún existe una gran diferencia entre ellos internamente. ¿No son las funciones C las mismas que las funciones en la programación funcional (a nivel de máquina)?
phoenisx
11
@Igno, como Subroto, realmente no entiendo tu explicación. Parece que lo que ha escrito puede resumirse como: Necesita respuesta ... obtenga respuesta. parece ignorar los bits importantes que es cómo. No entiendo cómo puedes esconder esa parte del usuario, en algún momento alguien tiene que saber cómo se hizo ... no puedes mantener al asistente detrás de la cortina para siempre.
Brett Thomas el
3
Esto no es remotamente lo que entiendo que es la programación funcional. Pensé que la programación funcional era la eliminación de entradas y salidas ocultas de las funciones.
Ringo
77
Explicación complicada.
JoeTidee
14

La mayoría de los lenguajes modernos son en cierto grado imperativos y funcionales, pero para comprender mejor la programación funcional, será mejor tomar un ejemplo de lenguaje funcional puro como Haskell en contraste con el código imperativo en un lenguaje no tan funcional como java / c #. Creo que siempre es fácil de explicar con un ejemplo, así que a continuación hay uno.

Programación funcional: calcular factorial de n es decir n! es decir, nx (n-1) x (n-2) x ... x 2 X 1

-- | Haskell comment goes like
-- | below 2 lines is code to calculate factorial and 3rd is it's execution  

factorial 0 = 1
factorial n = n * factorial (n - 1)
factorial 3

-- | for brevity let's call factorial as f; And x => y shows order execution left to right
-- | above executes as := f(3) as 3 x f(2) => f(2) as 2 x f(1) => f(1) as 1 x f(0) => f(0) as 1  
-- | 3 x (2 x (1 x (1)) = 6

Tenga en cuenta que Haskel permite la sobrecarga de funciones al nivel del valor del argumento. A continuación se muestra un ejemplo de código imperativo para aumentar el grado de imperativo:

//somewhat functional way
function factorial(n) {
  if(n < 1) {
     return 1;
  }
  return n * factorial(n-1);   
}
factorial(3);

//somewhat more imperative way
function imperativeFactor(n) {
  int f = 1
  for(int i = 1; i <= n; i++) {
     f = f * i
  }
  return f;
}

Esta lectura puede ser una buena referencia para comprender cómo el código imperativo se enfoca más en cómo parte, estado de la máquina (en bucle), orden de ejecución, control de flujo.

El ejemplo posterior puede verse como el código java / c # lang aproximadamente y la primera parte como una limitación del lenguaje en sí mismo en contraste con Haskell para sobrecargar la función por valor (cero) y, por lo tanto, se puede decir que no es un lenguaje funcional purista, por otro lado mano se puede decir que es compatible con el programa funcional. hasta cierto punto.

Divulgación: ninguno de los códigos anteriores se prueba / ejecuta, pero es de esperar que sea lo suficientemente bueno como para transmitir el concepto; También agradecería comentarios para cualquier corrección :)

viejo monje
fuente
1
¿No debería ser return n * factorial(n-1);?
jinawee
@jinawee, gracias por señalar, que han corregido desden * (n-1)
viejo monje-
10

La programación funcional es una forma de programación declarativa, que describe la lógica de la computación y el orden de ejecución se desestima por completo.

Problema: quiero cambiar esta criatura de un caballo a una jirafa.

  • Alargar el cuello
  • Alargar las piernas
  • Aplicar manchas
  • Dale a la criatura una lengua negra
  • Retire la cola de caballo

Cada elemento se puede ejecutar en cualquier orden para producir el mismo resultado.

La programación imperativa es de procedimiento. El estado y el orden son importantes.

Problema: quiero estacionar mi auto.

  1. Tenga en cuenta el estado inicial de la puerta del garaje
  2. Pare el auto en el camino de entrada
  3. Si la puerta del garaje está cerrada, abra la puerta del garaje, recuerde el nuevo estado; de lo contrario continuar
  4. Tire del auto al garaje
  5. Cerrar puerta de garaje

Cada paso debe hacerse para llegar al resultado deseado. Entrar al garaje mientras la puerta del garaje está cerrada daría como resultado una puerta de garaje rota.

Jakub Keller
fuente
Solo veo la diferencia en asíncrono vs. sincronización.
Vladimir Vukanac
@VladimirVukanac async / sync es un mecanismo, no una forma de programación
Jakub Keller
2
Oh, gracias, investigaré más al respecto. ¿Sería tan amable de actualizar el problema 1 para que sea igual al problema 2 "Quiero estacionar mi auto" pero escrito en forma de programación funcional? Entonces se excluirá el paralelismo.
Vladimir Vukanac
6

La programación funcional es "programación con funciones", donde una función tiene algunas propiedades matemáticas esperadas, incluida la transparencia referencial. A partir de estas propiedades, fluyen propiedades adicionales, en particular pasos de razonamiento familiares habilitados por la sustituibilidad que conducen a pruebas matemáticas (es decir, justifican la confianza en un resultado).

Se sigue que un programa funcional es simplemente una expresión.

Puede ver fácilmente el contraste entre los dos estilos observando los lugares en un programa imperativo donde una expresión ya no es referencialmente transparente (y, por lo tanto, no se construye con funciones y valores, y no puede ser parte de una función). Los dos lugares más obvios son: mutación (por ejemplo, variables) otros efectos secundarios del flujo de control no local (por ejemplo, excepciones)

En este marco de programas como expresiones que se componen de funciones y valores, se construye un paradigma práctico completo de lenguajes, conceptos, "patrones funcionales", combinadores y varios sistemas de tipos y algoritmos de evaluación.

Según la definición más extrema, casi cualquier lenguaje, incluso C o Java, puede llamarse funcional, pero por lo general las personas reservan el término para lenguajes con abstracciones específicamente relevantes (como cierres, valores inmutables y ayudas sintácticas como la coincidencia de patrones). En lo que respecta al uso de la programación funcional, implica el uso de functinas y el código de compilación sin efectos secundarios. solía escribir pruebas

Romil pawar
fuente
3

El estilo de programación imperativo se practicó en el desarrollo web desde 2005 hasta 2013.

Con la programación imperativa, escribimos el código que enumeraba exactamente lo que nuestra aplicación debería hacer, paso a paso.

El estilo de programación funcional produce abstracción a través de formas inteligentes de combinar funciones.

Se menciona la programación declarativa en las respuestas y respecto a eso diré que la programación declarativa enumera algunas reglas que debemos seguir. Luego proporcionamos lo que llamamos un estado inicial de nuestra aplicación y dejamos que esas reglas definan cómo se comporta la aplicación.

Ahora, estas descripciones rápidas probablemente no tengan mucho sentido, así que veamos las diferencias entre la programación imperativa y declarativa al pasar por una analogía.

Imagine que no estamos construyendo software, sino que horneamos pasteles para vivir. Quizás somos malos panaderos y no sabemos cómo hornear un delicioso pastel como deberíamos.

Entonces nuestro jefe nos da una lista de instrucciones, lo que conocemos como una receta.

La receta nos dirá cómo hacer un pastel. Una receta está escrita en un estilo imperativo como este:

  1. Mezclar 1 taza de harina
  2. Añadir 1 huevo
  3. Agrega 1 taza de azúcar
  4. Vierte la mezcla en una sartén
  5. Coloque la sartén en el horno durante 30 minutos y 350 grados F.

La receta declarativa haría lo siguiente:

1 taza de harina, 1 huevo, 1 taza de azúcar - Estado inicial

Reglas

  1. Si todo está mezclado, colóquelo en la sartén.
  2. Si todo está sin mezclar, colóquelo en un tazón.
  3. Si todo está en la sartén, colóquelo en el horno.

Por lo tanto, los enfoques imperativos se caracterizan por enfoques paso a paso. Comienzas con el paso uno y vas al paso 2 y así sucesivamente.

Eventualmente terminas con algún producto final. Entonces, al hacer este pastel, tomamos estos ingredientes, los mezclamos, los ponemos en una sartén y en el horno y usted obtiene su producto final.

En un mundo declarativo, es diferente. En la receta declarativa separaríamos nuestra receta en dos partes separadas, comenzando con una parte que enumere el estado inicial de la receta, como las variables. Entonces, nuestras variables aquí son las cantidades de nuestros ingredientes y su tipo.

Tomamos el estado inicial o los ingredientes iniciales y les aplicamos algunas reglas.

Así que tomamos el estado inicial y los pasamos a través de estas reglas una y otra vez hasta que tengamos un pastel de fresa de ruibarbo listo para comer o lo que sea.

Entonces, en un enfoque declarativo, tenemos que saber cómo estructurar adecuadamente estas reglas.

Entonces, las reglas que podríamos querer examinar nuestros ingredientes o nuestro estado, si se mezclan, póngalos en una sartén.

Con nuestro estado inicial, eso no coincide porque aún no hemos mezclado nuestros ingredientes.

Entonces, la regla 2 dice que si no se mezclan, mézclelos en un tazón. De acuerdo, sí, esta regla se aplica.

Ahora tenemos un tazón de ingredientes mezclados como nuestro estado.

Ahora aplicamos ese nuevo estado a nuestras reglas nuevamente.

Entonces, la regla 1 dice que si los ingredientes se mezclan, colóquelos en una sartén, está bien, sí, ahora la regla 1 se aplica, vamos a hacerlo.

Ahora tenemos este nuevo estado donde se mezclan los ingredientes y en una sartén. La regla 1 ya no es relevante, la regla 2 no se aplica.

La regla 3 dice que si los ingredientes están en una sartén, colóquelos en el horno, genial esa regla es lo que se aplica a este nuevo estado, vamos a hacerlo.

Y terminamos con una deliciosa tarta de manzana caliente o lo que sea.

Ahora, si eres como yo, puedes estar pensando, ¿por qué todavía no estamos haciendo una programación imperativa? Esto tiene sentido.

Bueno, para flujos simples, sí, pero la mayoría de las aplicaciones web tienen flujos más complejos que el diseño de programación imperativo no puede capturar adecuadamente.

En un enfoque declarativo, podemos tener algunos ingredientes iniciales o estado inicial como textInput=“”, una sola variable.

Tal vez el ingreso de texto comienza como una cadena vacía.

Tomamos este estado inicial y lo aplicamos a un conjunto de reglas definidas en su solicitud.

  1. Si un usuario ingresa texto, actualice el ingreso de texto. Bueno, ahora eso no se aplica.

  2. Si se representa la plantilla, calcule el widget.

  3. Si textInput se actualiza, vuelva a representar la plantilla.

Bueno, nada de esto aplica, por lo que el programa solo esperará a que suceda un evento.

Entonces, en algún momento, un usuario actualiza la entrada de texto y luego podemos aplicar la regla número 1.

Podemos actualizar eso a “abcd”

Así que acabamos de actualizar nuestro texto y las actualizaciones de entrada de texto, la regla número 2 no se aplica, la regla número 3 dice si la entrada de texto es actualizada, lo que acaba de ocurrir, luego vuelve a renderizar la plantilla y luego volvemos a la regla 2 que dice si la plantilla se representa , calcular el widget, está bien vamos a calcular el widget.

En general, como programadores, queremos luchar por diseños de programación más declarativos.

Imperativo parece más claro y obvio, pero un enfoque declarativo se adapta muy bien para aplicaciones más grandes.

Daniel
fuente
2

• Lenguajes imperativos:

  • Ejecución eficiente

  • Semántica compleja

  • Sintaxis compleja

  • La concurrencia está diseñada por programadores

  • Pruebas complejas, no tiene transparencia referencial, tiene efectos secundarios.

  • Tiene estado

• Idiomas funcionales:

  • Semántica simple

  • Sintaxis simple

  • Ejecución menos eficiente

  • Los programas se pueden hacer automáticamente concurrentes

  • Pruebas simples, tiene transparencia referencial, no tiene efectos secundarios.

  • No tiene estado
BoraKurucu
fuente
1

Creo que es posible expresar la programación funcional de manera imperativa:

  • Usando una gran cantidad de verificación de estado de objetos y if... else/ switchdeclaraciones
  • Algún mecanismo de tiempo de espera / espera para atender la asincronía

Hay grandes problemas con este enfoque:

  • Reglas / procedimientos se repiten
  • La afirmación deja posibilidades de efectos secundarios / errores

La programación funcional, el tratamiento de funciones / métodos como objetos y la adopción de la apatridia, nació para resolver esos problemas, creo.

Ejemplo de usos: aplicaciones frontend como Android, iOS o lógicas de aplicaciones web incl. comunicación con el backend.

Otros desafíos al simular la programación funcional con código imperativo / de procedimiento:

  • Condición de carrera
  • Combinación compleja y secuencia de eventos. Por ejemplo, el usuario intenta enviar dinero en una aplicación bancaria. Paso 1) Haga todo lo siguiente en paralelo, solo proceda si todo está bien a) Verifique si el usuario sigue siendo bueno (fraude, AML) b) verifique si el usuario tiene suficiente saldo c) Verifique si el destinatario es válido y bueno (fraude, AML) etc. Paso 2) realizar la operación de transferencia Paso 3) Mostrar actualizaciones sobre el saldo del usuario y / o algún tipo de seguimiento. Con RxJava, por ejemplo, el código es conciso y sensible. Sin él, puedo imaginar que habría mucho código, código desordenado y propenso a errores

También creo que al final del día, los compiladores traducirán el código funcional al código ensamblador o de máquina, que es imprescindible / procesal. Sin embargo, a menos que escriba ensamblado, ya que los humanos escriben código con lenguaje de alto nivel / legible para humanos, la programación funcional es la forma más apropiada de expresión para los escenarios enumerados

ericn
fuente
-1

Sé que esta pregunta es más antigua y otras ya la explicaron bien, me gustaría dar un ejemplo de problema que explica lo mismo en términos simples.

Problema: escribir la tabla del 1.

Solución: -

Por estilo imperativo: =>

    1*1=1
    1*2=2
    1*3=3
    .
    .
    .
    1*n=n 

Por estilo funcional: =>

    1
    2
    3
    .
    .
    .
    n

Explicación en estilo imperativo, escribimos las instrucciones más explícitamente y a las que podemos llamar de una manera más simplificada.

Donde, como en el estilo funcional, las cosas que se explican por sí mismas serán ignoradas.

Param NC
fuente