Qual é a diferença entre linguagens de programação funcionais e imperativas?

159

A maioria das linguagens convencionais, incluindo linguagens de programação orientada a objetos (OOP), como C #, Visual Basic, C ++ e Java, foram projetadas para oferecer suporte principalmente à programação imperativa (procedural), enquanto as linguagens Haskell / gofer são puramente funcionais. Alguém pode elaborar qual é a diferença entre essas duas formas de programação?

Sei que depende dos requisitos do usuário para escolher o modo de programação, mas por que é recomendável aprender linguagens de programação funcionais?

Swapnil Kotwal
fonte
1
possível duplicata de programação funcional vs programação Orientada a Objetos
Floris Velleman
1
verifique este outro [postar] [1]. Descreve claramente as diferenças. [1]: stackoverflow.com/questions/602444/…
theta

Respostas:

160

Definição: Uma linguagem imperativa usa uma sequência de instruções para determinar como atingir um determinado objetivo. Diz-se que essas declarações alteram o estado do programa à medida que cada uma é executada.

Exemplos: Java é uma linguagem imperativa. Por exemplo, um programa pode ser criado para adicionar uma série de números:

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

Cada instrução altera o estado do programa, desde a atribuição de valores a cada variável até a adição final desses valores. Usando uma sequência de cinco instruções, o programa é explicitamente informado sobre como adicionar os números 5, 10 e 15.

Linguagens funcionais: o paradigma de programação funcional foi criado explicitamente para oferecer suporte a uma abordagem funcional pura para a solução de problemas. A programação funcional é uma forma de programação declarativa.

Vantagens das funções puras: A principal razão para implementar transformações funcionais como funções puras é que as funções puras são composíveis: ou seja, independentes e sem estado. Essas características trazem vários benefícios, incluindo o seguinte: Maior legibilidade e capacidade de manutenção. Isso ocorre porque cada função é projetada para realizar uma tarefa específica, dados seus argumentos. A função não depende de nenhum estado externo.

Desenvolvimento reiterativo mais fácil. Como o código é mais fácil de refatorar, as alterações no design geralmente são mais fáceis de implementar. Por exemplo, suponha que você escreva uma transformação complicada e perceba que algum código é repetido várias vezes na transformação. Se você refatorar através de um método puro, poderá chamá-lo à vontade sem se preocupar com os efeitos colaterais.

Teste e depuração mais fáceis. Como funções puras podem ser mais facilmente testadas isoladamente, você pode escrever um código de teste que chama a função pura com valores típicos, casos de borda válidos e casos de borda inválidos.

Para pessoas OOP ou idiomas imperativos:

Linguagens orientadas a objetos são boas quando você tem um conjunto fixo de operações e, à medida que seu código evolui, você adiciona principalmente coisas novas. Isso pode ser conseguido adicionando novas classes que implementam métodos existentes e as classes existentes são deixadas em paz.

Linguagens funcionais são boas quando você tem um conjunto fixo de coisas e, à medida que seu código evolui, você adiciona principalmente novas operações às coisas existentes. Isso pode ser conseguido adicionando novas funções que computam com os tipos de dados existentes e as funções existentes são deixadas em paz.

Contras:

Depende dos requisitos do usuário para escolher a maneira de programar, para que haja danos apenas quando os usuários não escolherem a maneira correta.

Quando a evolução segue o caminho errado, você tem problemas:

  • Adicionar uma nova operação a um programa orientado a objetos pode exigir a edição de muitas definições de classe para adicionar um novo método
  • Adicionar um novo tipo de coisa a um programa funcional pode exigir a edição de muitas definições de funções para adicionar um novo caso.
Chris
fonte
10
Uma função pura neste caso é o equivalente a uma função matemática. As mesmas entradas sempre serão mapeadas para as mesmas saídas. Eles também não possuem efeitos colaterais (exceto o retorno de um valor ou valores), o que significa que o compilador pode fazer algumas otimizações interessantes e facilita a execução da função em paralelo, pois não há nada com o que lidar.
WorBlux
Portanto, as maneiras corretas e as melhores práticas de compor aplicativos sustentáveis ​​e testáveis ​​tendem a projetar código imperativo com um estado declinativo de espírito?
Kemal Gültekin
4
Não vejo uma diferença clara no texto em que os recursos de cada programação estão destacados. A maior parte da descrição da programação processual pode ser trocada pelo texto imperativo da programação e vice-versa.
AxeEffect
7
Essa resposta tenta esclarecer o que é programação funcional, mas nem se importa em definir o que é uma função pura. Não vejo como alguém poderia ler esta resposta e sair confiante em saber a diferença entre programação declarativa e programação processual.
Ringo
230

Aqui está a diferença:

Imperativo:

  • Começar
  • Ligue os sapatos, tamanho 9 1/2.
  • Crie espaço no seu bolso para manter uma matriz [7] de chaves.
  • Coloque as chaves no quarto para as chaves no bolso.
  • Entre na garagem.
  • Garagem aberta.
  • Digite carro.

... E assim por diante ...

  • Coloque o leite na geladeira.
  • Pare.

Declarativa, da qual funcional é uma subcategoria:

  • O leite é uma bebida saudável, a menos que você tenha problemas para digerir a lactose.
  • Normalmente, um armazena leite na geladeira.
  • Uma geladeira é uma caixa que mantém as coisas frescas.
  • Uma loja é um local onde os itens são vendidos.
  • Por "venda" entendemos a troca de coisas por dinheiro.
  • Além disso, a troca de dinheiro por coisas é chamada de "compra".

... E assim por diante ...

  • Certifique-se de que temos leite na geladeira (quando precisamos - para idiomas funcionais preguiçosos).

Resumo: em linguagens imperativas, você informa ao computador como alterar bits, bytes e palavras na memória e em que ordem. Nos funcionais, dizemos ao computador o que são coisas, ações etc. Por exemplo, dizemos que o fatorial de 0 é 1 e o fatorial de qualquer outro número natural é o produto desse número e o fatorial de seu antecessor. Não dizemos: Para calcular o fatorial de n, reserve uma região de memória e armazene 1 lá, multiplique o número nessa região de memória pelos números 2 para ne armazene o resultado no mesmo local e, no final, a região da memória conterá o fatorial.

Ingo
fonte
1
Obrigado. Essa é uma ótima maneira de ver isso.
L-Samuels
5
Gostei da sua explicação @Igno, mas algo ainda não está claro para mim. Em Declarativo, mesmo que você apenas diga coisas, mas ainda precisa alterar bits e fazer alterações nos estados da máquina para prosseguir corretamente. Me deixa confuso, que de alguma forma Declarativo seja semelhante à Programação Procedimental (como Funções C) , e ainda existe uma grande diferença entre eles internamente. As funções C não são iguais às funções na programação funcional (no nível da máquina)?
Phoenisx
11
@ Igno, como Subroto, eu realmente não entendo sua explicação. Parece que o que você escreveu pode ser resumido como: Precisa de resposta ... obtenha resposta. parece ignorar os bits importantes que é como. Eu não entendo como você pode simplesmente esconder essa parte do usuário, em algum momento alguém precisa saber como isso foi feito ... você não pode manter o assistente atrás da cortina para sempre.
Brett Thomas
3
Isso não é remotamente o que eu entendo ser programação funcional. Eu pensei que a programação funcional era a remoção de entradas e saídas ocultas das funções.
Ringo
7
Explicação complicada.
JoeTidee 5/05
14

A maioria das linguagens modernas é, em graus variados, imperativa e funcional, mas para entender melhor a programação funcional, é melhor usar um exemplo de linguagem funcional pura como Haskell, em contraste com o código imperativo em linguagem não tão funcional como java / c #. Eu acredito que é sempre fácil explicar pelo exemplo, então abaixo está um.

Programação funcional: calcule fatorial de n ie n! ou seja, 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

Observe que Haskel permite sobrecarregar a função até o nível do valor do argumento. Agora abaixo está um exemplo de código imperativo em crescente grau de imperatividade:

//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;
}

Essa leitura pode ser uma boa referência para entender como o código imperativo se concentra mais em como parte, estado da máquina (i in for loop), ordem de execução, controle de fluxo.

O exemplo posterior pode ser visto como código java / c # lang de maneira grosseira e como primeira parte como limitação da própria linguagem, em contraste com Haskell, para sobrecarregar a função por valor (zero) e, portanto, pode-se dizer que não é uma linguagem funcional purista, por outro mão, você pode dizer que suporta prog funcional. até certo ponto.

Divulgação: nenhum dos códigos acima é testado / executado, mas espero que seja bom o suficiente para transmitir o conceito; Também gostaria de receber comentários por qualquer correção :)

monge velho
fonte
1
Não deveria ser return n * factorial(n-1);?
jinawee
@jinawee, obrigado por apontar, eu o corrigi den * (n-1)
old-monk
10

A Programação Funcional é uma forma de programação declarativa, que descreve a lógica da computação e a ordem de execução é completamente enfatizada.

Problema: quero mudar esta criatura de cavalo para girafa.

  • Alongar o pescoço
  • Alongar as pernas
  • Aplicar manchas
  • Dê à criatura uma língua negra
  • Remover rabo de cavalo

Cada item pode ser executado em qualquer ordem para produzir o mesmo resultado.

A programação imperativa é processual. Estado e ordem são importantes.

Problema: quero estacionar meu carro.

  1. Observe o estado inicial da porta da garagem
  2. Pare o carro na garagem
  3. Se a porta da garagem estiver fechada, abra a porta da garagem, lembre-se do novo estado; caso contrário, continue
  4. Puxe o carro para a garagem
  5. Feche a porta da garagem

Cada etapa deve ser executada para chegar ao resultado desejado. Puxar para dentro da garagem enquanto a porta está fechada resultaria em uma porta quebrada.

Jakub Keller
fonte
Eu vejo apenas a diferença entre async vs. sync.
Vladimir Vukanac 25/03/19
@VladimirVukanac assíncrono / sincronização é um mecanismo, não é uma forma de programação
Jakub Keller
2
Oh, obrigado, vou pesquisar mais sobre isso. Você seria gentil em atualizar o problema 1 para ser o mesmo que o problema 2 "Quero estacionar meu carro", mas escrito de maneira funcional para programação? Em seguida, o paralelismo será excluído.
Vladimir Vukanac 29/03/19
6

A programação funcional é "programação com funções", onde uma função tem algumas propriedades matemáticas esperadas, incluindo transparência referencial. A partir dessas propriedades, outras propriedades fluem, em particular etapas familiares de raciocínio possibilitadas pela substituibilidade que levam a provas matemáticas (isto é, justificando a confiança em um resultado).

Daqui resulta que um programa funcional é meramente uma expressão.

Você pode ver facilmente o contraste entre os dois estilos observando os lugares em um programa imperativo em que uma expressão não é mais referencialmente transparente (e, portanto, não é construída com funções e valores, e não pode ser parte de uma função). Os dois lugares mais óbvios são: mutação (por exemplo, variáveis) outros efeitos colaterais fluxo de controle não local (por exemplo, exceções)

Nesta estrutura de programas como expressões, composta de funções e valores, é construído todo um paradigma prático de linguagens, conceitos, "padrões funcionais", combinadores e vários sistemas de tipos e algoritmos de avaliação.

Pela definição mais extrema, quase qualquer linguagem - mesmo C ou Java - pode ser chamada de funcional, mas geralmente as pessoas reservam o termo para linguagens com abstrações especificamente relevantes (como fechamentos, valores imutáveis ​​e auxílios sintáticos, como a correspondência de padrões). No que diz respeito ao uso da programação funcional, envolve o uso de funções e cria código sem efeitos colaterais. usado para escrever provas

Romil pawar
fonte
3

O estilo de programação imperativa foi praticado no desenvolvimento web desde 2005 até 2013.

Com a programação imperativa, escrevemos código que listava exatamente o que nosso aplicativo deveria fazer, passo a passo.

O estilo de programação funcional produz abstração através de maneiras inteligentes de combinar funções.

Há menção de programação declarativa nas respostas e, em relação a isso, direi que a programação declarativa lista algumas regras que devemos seguir. Em seguida, fornecemos o que chamamos de estado inicial de nosso aplicativo e permitimos que essas regras definam como o aplicativo se comporta.

Agora, essas descrições rápidas provavelmente não fazem muito sentido, então vamos percorrer as diferenças entre programação imperativa e declarativa, percorrendo uma analogia.

Imagine que não estamos construindo software, mas fazemos tortas para ganhar a vida. Talvez sejamos padeiros ruins e não sabemos como fazer uma torta deliciosa do jeito que deveríamos.

Então, nosso chefe nos dá uma lista de instruções, o que sabemos como receita.

A receita nos dirá como fazer uma torta. Uma receita é escrita em um estilo imperativo assim:

  1. Misture 1 xícara de farinha
  2. Adicione 1 ovo
  3. Adicione 1 xícara de açúcar
  4. Despeje a mistura em uma panela
  5. Coloque a panela no forno por 30 minutos e 350 graus F.

A receita declarativa faria o seguinte:

1 xícara de farinha, 1 ovo, 1 xícara de açúcar - Estado inicial

Regras

  1. Se tudo estiver misturado, coloque na panela.
  2. Se tudo não estiver misturado, coloque na tigela.
  3. Se tudo na panela, coloque no forno.

As abordagens imperativas são caracterizadas por abordagens passo a passo. Você começa com o passo um e vai para o passo 2 e assim por diante.

Você acaba com algum produto final. Então, fazendo esta torta, pegamos esses ingredientes, misturamos, colocamos em uma panela e no forno e você conseguiu seu produto final.

Em um mundo declarativo, é diferente. Na receita declarativa, separaríamos nossa receita em duas partes separadas, comece com uma parte que liste o estado inicial da receita, como as variáveis. Portanto, nossas variáveis ​​aqui são as quantidades de nossos ingredientes e seu tipo.

Tomamos o estado inicial ou os ingredientes iniciais e aplicamos algumas regras a eles.

Portanto, tomamos o estado inicial e as repassamos repetidamente por essas regras até prepararmos uma torta de morango pronta para comer ou qualquer outra coisa.

Portanto, em uma abordagem declarativa, precisamos saber como estruturar adequadamente essas regras.

Portanto, as regras que podemos examinar nossos ingredientes ou declarar, se misturados, colocá-los em uma panela.

Com o nosso estado inicial, isso não corresponde porque ainda não misturamos nossos ingredientes.

A regra 2 diz que, se não estiverem misturados, misture-os em uma tigela. Ok, sim, esta regra se aplica.

Agora temos uma tigela de ingredientes misturados como nosso estado.

Agora aplicamos esse novo estado às nossas regras novamente.

Então a regra 1 diz que se os ingredientes forem misturados, coloque-os em uma panela, ok, sim, agora a regra 1 se aplica, vamos fazê-lo.

Agora temos esse novo estado em que os ingredientes são misturados e em uma panela. A regra 1 não é mais relevante, a regra 2 não se aplica.

A regra 3 diz que se os ingredientes estiverem em uma panela, coloque-os no forno, ótimo essa regra é o que se aplica a esse novo estado, vamos fazê-lo.

E acabamos com uma deliciosa torta de maçã quente ou o que seja.

Agora, se você é como eu, pode estar pensando: por que ainda não fazemos uma programação imperativa? Isso faz sentido.

Bem, para fluxos simples, sim, mas a maioria dos aplicativos da Web possui fluxos mais complexos que não podem ser capturados adequadamente pelo design de programação imperativa.

Em uma abordagem declarativa, podemos ter alguns ingredientes iniciais ou estado inicial textInput=“”, como uma única variável.

Talvez a entrada de texto comece como uma sequência vazia.

Tomamos esse estado inicial e o aplicamos a um conjunto de regras definidas em seu aplicativo.

  1. Se um usuário digitar texto, atualize a entrada de texto. Bem, agora isso não se aplica.

  2. Se o modelo for renderizado, calcule o widget.

  3. Se textInput for atualizado, renderize novamente o modelo.

Bem, nada disso se aplica; portanto, o programa irá esperar um evento acontecer.

Então, em algum momento, um usuário atualiza a entrada de texto e, em seguida, podemos aplicar a regra número 1.

Podemos atualizar isso para “abcd”

Assim, acabamos de atualizar nossas atualizações de texto e de texto, a regra número 2 não se aplica, a regra número 3 diz que se a entrada de texto for atualizada, o que acabou de ocorrer, em seguida, renderizamos novamente o modelo e, em seguida, voltamos à regra 2, que diz que o modelo é renderizado , calcule o widget, ok vamos calcular o widget.

Em geral, como programadores, queremos lutar por projetos de programação mais declarativos.

O imperativo parece mais claro e óbvio, mas uma abordagem declarativa é muito bem dimensionada para aplicativos maiores.

Daniel
fonte
2

• Línguas imperativas:

  • Execução eficiente

  • Semântica complexa

  • Sintaxe complexa

  • Simultaneidade é programador projetado

  • Testes complexos, sem transparência referencial, têm efeitos colaterais

  • Tem estado

• Idiomas funcionais:

  • Semântica simples

  • Sintaxe simples

  • Execução menos eficiente

  • Os programas podem se tornar automaticamente simultâneos

  • Testes simples, com transparência referencial, sem efeitos colaterais

  • Não tem estado
BoraKurucu
fonte
1

Eu acho que é possível expressar a programação funcional de uma maneira imperativa:

  • Usando muita verificação de estado de objetos e if... else/ switchinstruções
  • Algum mecanismo de tempo limite / espera para cuidar da assíncrona

Existem enormes problemas com essa abordagem:

  • Regras / procedimentos são repetidos
  • O estado deixa chances de efeitos colaterais / erros

A programação funcional, tratando funções / métodos como objetos e adotando apatridia, nasceu para resolver os problemas que eu acredito.

Exemplo de usos: aplicativos de interface como Android, iOS ou lógicas de aplicativos da Web, incl. comunicação com o back-end.

Outros desafios ao simular programação funcional com código imperativo / processual:

  • Condição de corrida
  • Combinação complexa e sequência de eventos. Por exemplo, o usuário tenta enviar dinheiro em um aplicativo bancário. Etapa 1) Faça o seguinte em paralelo, apenas proceda se tudo estiver bom a) Verifique se o usuário ainda está bom (fraude, AML) b) verifique se o usuário possui equilíbrio suficiente c) Verifique se o destinatário é válido e bom (fraude, AML) etc. Etapa 2) execute a operação de transferência Etapa 3) Mostrar atualização do saldo do usuário e / ou algum tipo de rastreamento. Com o RxJava, por exemplo, o código é conciso e sensível. Sem ele, posso imaginar que haveria muito código, código bagunçado e propenso a erros

Também acredito que, no final do dia, o código funcional será traduzido em código de montagem ou de máquina, que é imperativo / processual pelos compiladores. No entanto, a menos que você escreva assembly, como humanos escrevendo código com linguagem de alto nível / legível por humanos, a programação funcional é a maneira de expressão mais apropriada para os cenários listados.

ericn
fonte
-1

Sei que esta pergunta é mais antiga e outros já a explicaram bem, gostaria de dar um exemplo de problema que explica o mesmo em termos simples.

Problema: Escrevendo a tabela 1.

Solução: -

Por estilo imperativo: =>

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

Por estilo funcional: =>

    1
    2
    3
    .
    .
    .
    n

Explicação no estilo Imperativo, escrevemos as instruções mais explicitamente e que podem ser chamadas da maneira mais simplificada.

Onde, como no estilo Funcional, as coisas auto-explicativas serão ignoradas.

Param NC
fonte