Pergunta

Eu li os artigos da Wikipedia para ambos programação processual e programação funcional, mas ainda estou um pouco confuso.Alguém poderia resumir isso ao âmago?

Foi útil?

Solução

Uma linguagem funcional (idealmente) permite escrever uma função matemática, ou seja,uma função que leva n argumentos e retorna um valor.Se o programa for executado, esta função será avaliada logicamente conforme necessário.1

Uma linguagem processual, por outro lado, executa uma série de sequencial passos.(Existe uma maneira de transformar a lógica sequencial em lógica funcional chamada estilo de passagem de continuação.)

Como consequência, um programa puramente funcional sempre produz o mesmo valor para uma entrada e a ordem de avaliação não está bem definida;o que significa que valores incertos, como entrada do usuário ou valores aleatórios, são difíceis de modelar em linguagens puramente funcionais.


1 Como tudo o mais nesta resposta, isso é uma generalização.A propriedade de avaliar um cálculo quando seu resultado é necessário, em vez de sequencialmente onde é chamado, é conhecida como “preguiça”, e nem todas as linguagens funcionais são universalmente preguiçosas, nem a preguiça está restrita à programação funcional.Em vez disso, a descrição dada aqui fornece uma “estrutura mental” para pensar sobre diferentes estilos de programação que não são categorias distintas e opostas, mas sim ideias fluidas.

Outras dicas

Basicamente, os dois estilos são como Yin e Yang.Um é organizado, enquanto o outro é caótico.Há situações em que a programação funcional é a escolha óbvia e outras situações em que a programação processual é a melhor escolha.É por isso que existem pelo menos duas linguagens que lançaram recentemente uma nova versão, que abrange ambos os estilos de programação. ( Perl 6 e D 2 )

Processual:

  • A saída de uma rotina nem sempre tem correlação direta com a entrada.
  • Tudo é feito em uma ordem específica.
  • A execução de uma rotina pode ter efeitos colaterais.
  • Tende a enfatizar a implementação de soluções de forma linear.

Perl 6

D2

int factorial( int n ){

  int result = 1;

  for( ; n > 0 ; n-- ){
    result *= n;
  }

  return result;
}

Funcional:

  • Muitas vezes recursivo.
  • Sempre retorna a mesma saída para uma determinada entrada.
  • A ordem de avaliação geralmente é indefinida.
  • Deve ser apátrida.ou sejaNenhuma operação pode ter efeitos colaterais.
  • Bom ajuste para execução paralela
  • Tende a enfatizar uma abordagem de dividir para conquistar.
  • Pode ter o recurso de avaliação preguiçosa.

Haskell

(copiado de Wikipédia );

fac :: Integer -> Integer

fac 0 = 1
fac n | n > 0 = n * fac (n-1)

ou em uma linha:

fac n = if n > 0 then n * fac (n-1) else 1

Perl 6

D2

pure int factorial( invariant int n ){
  if( n <= 1 ){
    return 1;
  }else{
    return n * factorial( n-1 );
  }
}

Nota:

Factorial é na verdade um exemplo comum para mostrar como é fácil criar novos operadores em Perl 6 da mesma forma que você criaria uma sub-rotina.Esse recurso está tão arraigado no Perl 6 que a maioria dos operadores na implementação do Rakudo são definidos dessa forma.Também permite que você adicione seus próprios candidatos múltiplos às operadoras existentes.

Este exemplo também mostra a criação de intervalo (2..$n) e o metaoperador de redução de lista ([ OPERATOR ] LIST) combinado com o operador de multiplicação de infixo numérico.(*)
Também mostra que você pode colocar --> UInt na assinatura em vez de returns UInt depois disso.

(Você pode começar o intervalo com 2 já que o "operador" de multiplicação retornará 1 quando chamado sem nenhum argumento)

Nunca vi essa definição dada em outro lugar, mas acho que isso resume bastante bem as diferenças apresentadas aqui:

Funcional a programação se concentra em expressões

Processual a programação se concentra em declarações

Expressões têm valores.Um programa funcional é uma expressão cujo valor é uma sequência de instruções que o computador deve executar.

As declarações não possuem valores e, em vez disso, modificam o estado de alguma máquina conceitual.

Em uma linguagem puramente funcional não haveria declarações, no sentido de que não há como manipular o estado (eles ainda podem ter uma construção sintática chamada "declaração", mas a menos que manipule o estado, eu não a chamaria de declaração neste sentido ).Numa linguagem puramente processual não existiriam expressões, tudo seria uma instrução que manipula o estado da máquina.

Haskell seria um exemplo de linguagem puramente funcional porque não há como manipular o estado.O código de máquina seria um exemplo de linguagem puramente processual porque tudo em um programa é uma instrução que manipula o estado dos registradores e da memória da máquina.

A parte confusa é que a grande maioria das linguagens de programação contém ambos expressões e enunciados, permitindo misturar paradigmas.As linguagens podem ser classificadas como mais funcionais ou mais procedimentais com base no quanto incentivam o uso de declarações em vez de expressões.

Por exemplo, C seria mais funcional que COBOL porque uma chamada de função é uma expressão, enquanto chamar um subprograma em COBOL é uma instrução (que manipula o estado de variáveis ​​compartilhadas e não retorna um valor).Python seria mais funcional que C porque permite expressar lógica condicional como uma expressão usando avaliação de curto-circuito (test && path1 || path2 em oposição a instruções if).O esquema seria mais funcional que o Python porque tudo no esquema é uma expressão.

Você ainda pode escrever em um estilo funcional em uma linguagem que incentive o paradigma processual e vice-versa.É apenas mais difícil e/ou estranho escrever em um paradigma que não é incentivado pela linguagem.

Na ciência da computação, a programação funcional é um paradigma de programação que trata a computação como a avaliação de funções matemáticas e evita dados de estado e mutáveis.Enfatiza a aplicação de funções, em contraste com o estilo de programação processual que enfatiza mudanças de estado.

Acredito que a programação processual/funcional/objetiva trata de como abordar um problema.

O primeiro estilo planejaria tudo em etapas e resolveria o problema implementando uma etapa (um procedimento) de cada vez.Por outro lado, a programação funcional enfatizaria a abordagem de dividir e conquistar, onde o problema é dividido em subproblemas, então cada subproblema é resolvido (criando uma função para resolver esse subproblema) e os resultados são combinados para crie a resposta para todo o problema.Por último, a programação objetiva imitaria o mundo real, criando um minimundo dentro do computador com muitos objetos, cada um dos quais com características (um tanto) únicas, e interagindo com outros.Dessas interações surgiria o resultado.

Cada estilo de programação tem suas próprias vantagens e fraquezas.Portanto, fazer algo como "programação pura" (ou seja,puramente processual - ninguém faz isso, aliás, o que é meio estranho - ou puramente funcional ou puramente objetivo) é muito difícil, se não impossível, exceto alguns problemas elementares especialmente concebidos para demonstrar a vantagem de um estilo de programação (portanto, chamamos quem gosta de pureza de "weenie" :D).

Então, a partir desses estilos, temos linguagens de programação projetadas para serem otimizadas para cada estilo.Por exemplo, Assembly tem tudo a ver com procedimentos.Ok, a maioria das primeiras linguagens são procedurais, não apenas Asm, como C, Pascal (e Fortran, ouvi dizer).Então, temos todos os famosos Java na escola objetiva (na verdade, Java e C# também estão em uma classe chamada “orientada ao dinheiro”, mas isso é assunto para outra discussão).Também objetivo é o Smalltalk.Na escola funcional, teríamos família Lisp e família ML "quase funcionais" (alguns os consideravam impuros) e muitos Haskell, Erlang, etc. "puramente funcionais".A propósito, existem muitas linguagens gerais, como Perl, Python, Ruby.

Para expandir o comentário de Konrad:

Como consequência, um programa puramente funcional produz sempre o mesmo valor para uma entrada e a ordem de avaliação não é bem definida;

Por causa disso, o código funcional geralmente é mais fácil de paralelizar.Como (geralmente) não há efeitos colaterais das funções, e elas (geralmente) apenas agem de acordo com seus argumentos, muitos problemas de simultaneidade desaparecem.

A programação funcional também é usada quando você precisa ser capaz de provando seu código está correto.Isso é muito mais difícil de fazer com programação processual (não é fácil com programação funcional, mas ainda é mais fácil).

Isenção de responsabilidade:Não uso programação funcional há anos e só recentemente comecei a examiná-la novamente, então posso não estar completamente correto aqui.:)

Uma coisa que eu não tinha visto realmente enfatizada aqui é que linguagens funcionais modernas, como Haskell, realmente se concentram mais em funções de primeira classe para controle de fluxo do que em recursão explícita.Você não precisa definir fatorial recursivamente em Haskell, como foi feito acima.Eu acho que algo como

fac n = foldr (*) 1 [1..n]

é uma construção perfeitamente idiomática e muito mais próxima do uso de um loop do que da recursão explícita.

Uma programação funcional é idêntica à programação processual na qual variáveis ​​globais são não sendo usado.

Linguagens processuais tendem a acompanhar o estado (usando variáveis) e a executar como uma sequência de etapas.Linguagens puramente funcionais não controlam o estado, usam valores imutáveis ​​e tendem a ser executadas como uma série de dependências.Em muitos casos, o status da pilha de chamadas conterá as informações que seriam equivalentes àquelas que seriam armazenadas em variáveis ​​de estado no código processual.

A recursão é um exemplo clássico de programação de estilo funcional.

Konrad disse:

Como conseqüência, um programa puramente funcional sempre produz o mesmo valor para uma entrada, e a ordem de avaliação não é bem definida;O que significa que valores incertos como entrada do usuário ou valores aleatórios são difíceis de modelar em linguagens puramente funcionais.

A ordem de avaliação em um programa puramente funcional pode ser difícil de raciocinar (especialmente com preguiça) ou até mesmo sem importância, mas acho que dizer que não está bem definido faz parecer que você não consegue dizer se o seu programa está indo para trabalhar!

Talvez uma explicação melhor seria que o fluxo de controle em programas funcionais se baseia em quando o valor dos argumentos de uma função é necessário.O bom disso é que em programas bem escritos, o estado se torna explícito:cada função lista suas entradas como parâmetros em vez de arbitrariamente munging estado mundial.Então, em algum nível, é mais fácil raciocinar sobre a ordem de avaliação em relação a uma função de cada vez.Cada função pode ignorar o resto do universo e focar no que precisa fazer.Quando combinadas, as funções têm a garantia de funcionar da mesma forma[1] que funcionariam isoladamente.

...Valores incertos como entrada do usuário ou valores aleatórios são difíceis de modelar em linguagens puramente funcionais.

A solução para o problema de entrada em programas puramente funcionais é incorporar uma linguagem imperativa como um DSL usando uma abstração suficientemente poderosa.Em linguagens imperativas (ou funcionais não puras), isso não é necessário porque você pode "trapacear" e passar o estado implicitamente e a ordem de avaliação é explícita (quer você goste ou não).Por causa dessa "trapaça" e avaliação forçada de todos os parâmetros para cada função, em linguagens imperativas 1) você perde a capacidade de criar seus próprios mecanismos de fluxo de controle (sem macros), 2) o código não é inerentemente thread-safe e/ou paralelizável por padrão, 3) e implementar algo como desfazer (viagem no tempo) exige um trabalho cuidadoso (o programador imperativo deve armazenar uma receita para recuperar o(s) valor(es) antigo(s)!), enquanto a programação funcional pura compra todas essas coisas para você - e mais algumas, posso esqueci - "de graça".

Espero que isso não pareça fanatismo, só queria acrescentar alguma perspectiva.A programação imperativa e especialmente a programação de paradigma misto em linguagens poderosas como C# 3.0 ainda são maneiras totalmente eficazes de fazer as coisas e Não há bala de prata.

[1] ...exceto possivelmente com relação ao uso de memória (cf.foldl e foldl' em Haskell).

Para expandir o comentário de Konrad:

e a ordem de avaliação não é bem definida

Algumas linguagens funcionais possuem o que é chamado de Avaliação Preguiçosa.O que significa que uma função não é executada até que o valor seja necessário.Até esse momento, a função em si é o que é repassado.

Linguagens processuais são passo 1 passo 2 passo 3...se na etapa 2 você disser adicionar 2 + 2, isso será feito imediatamente.Na avaliação preguiçosa você diria somar 2 + 2, mas se o resultado nunca for usado, ele nunca fará a adição.

Se você tiver uma chance, eu recomendo obter uma cópia do Lisp/Scheme e fazer alguns projetos nele.A maioria das ideias que ultimamente se tornaram populares foram expressas em Lisp décadas atrás:programação funcional, continuações (como encerramentos), coleta de lixo e até XML.

Então, essa seria uma boa maneira de começar com todas essas ideias atuais e mais algumas, como a computação simbólica.

Você deve saber para que serve a programação funcional e para que não serve.Não é bom para tudo.Alguns problemas são melhor expressos em termos de efeitos colaterais, onde a mesma pergunta dá respostas diferentes dependendo de quando é feita.

@Creighton:

Em Haskell existe uma função de biblioteca chamada produtos:

prouduct list = foldr 1 (*) list

ou simplesmente:

product = foldr 1 (*)

então o fatorial "idiomático"

fac n = foldr 1 (*)  [1..n]

seria simplesmente

fac n = product [1..n]

Programação Funcional

num = 1 
def function_to_add_one(num):
    num += 1
    return num


function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)

#Final Output: 2

Programação Processual

num = 1 
def procedure_to_add_one():
    global num
    num += 1
    return num


procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()

#Final Output: 6

function_to_add_one é uma função

procedure_to_add_one é um procedimento

Mesmo se você executar o função cinco vezes, toda vez que ele retornará 2

Se você executar o procedimento cinco vezes, no final da quinta corrida você terá 6.

A programação processual divide sequências de instruções e construções condicionais em blocos separados chamados procedimentos que são parametrizados sobre argumentos que são valores (não funcionais).

A programação funcional é a mesma, exceto que as funções são valores de primeira classe, portanto podem ser passadas como argumentos para outras funções e retornadas como resultados de chamadas de função.

Observe que a programação funcional é uma generalização da programação processual nesta interpretação.No entanto, uma minoria interpreta "programação funcional" como significando livre de efeitos colaterais, o que é bem diferente, mas irrelevante para todas as principais linguagens funcionais, exceto Haskell.

Para entender a diferença, é preciso entender que o paradigma “o padrinho” da programação processual e funcional é o programação imperativa.

Basicamente, a programação processual é apenas uma maneira de estruturar programas imperativos nos quais o principal método de abstração é o "procedimento". (ou "função" em algumas linguagens de programação).Mesmo a Programação Orientada a Objetos é apenas outra forma de estruturar um programa imperativo, onde o estado é encapsulado em objetos, tornando-se um objeto com um "estado atual", além de esse objeto possuir um conjunto de funções, métodos e outras coisas que permitem a você o programador manipula ou atualiza o estado.

Agora, no que diz respeito à programação funcional, o essência em sua abordagem é identificar quais valores assumir e como esses valores devem ser transferidos.(portanto, não há estado nem dados mutáveis, pois pega funções como valores de primeira classe e os passa como parâmetros para outras funções).

PS:compreender cada paradigma de programação é usado deve esclarecer as diferenças entre todos eles.

PSS:No final das contas, os paradigmas de programação são apenas abordagens diferentes para resolver problemas.

PSS: esse A resposta do quora tem uma ótima explicação.

Nenhuma das respostas aqui mostra programação funcional idiomática.A resposta fatorial recursiva é ótima para representar a recursão em FP, mas a maior parte do código não é recursiva, então não acho que essa resposta seja totalmente representativa.

Digamos que você tenha uma matriz de strings e cada string representa um número inteiro como "5" ou "-200".Você deseja verificar esta matriz de strings de entrada em relação ao seu caso de teste interno (usando comparação de números inteiros).Ambas as soluções são mostradas abaixo

Processual

arr_equal(a : [Int], b : [Str]) -> Bool {
    if(a.len != b.len) {
        return false;
    }

    bool ret = true;
    for( int i = 0; i < a.len /* Optimized with && ret*/; i++ ) {
        int a_int = a[i];
        int b_int = parseInt(b[i]);
        ret &= a_int == b_int;  
    }
    return ret;
}

Funcional

eq = i, j => i == j # This is usually a built-in
toInt = i => parseInt(i) # Of course, parseInt === toInt here, but this is for visualization

arr_equal(a : [Int], b : [Str]) -> Bool =
    zip(a, b.map(toInt)) # Combines into [Int, Int]
   .map(eq)
   .reduce(true, (i, j) => i && j) # Start with true, and continuously && it with each value

Embora as linguagens funcionais puras sejam geralmente linguagens de pesquisa (já que o mundo real gosta de efeitos colaterais gratuitos), as linguagens procedurais do mundo real usarão a sintaxe funcional muito mais simples quando apropriado.

Isso geralmente é implementado com uma biblioteca externa como Lodash, ou disponível integrado em linguagens mais recentes, como Ferrugem.O trabalho pesado da programação funcional é feito com funções/conceitos como map, filter, reduce, currying, partial, os três últimos dos quais você pode consultar para maior compreensão.

Termo aditivo

Para ser usado livremente, o compilador normalmente terá que descobrir como converter internamente a versão funcional na versão processual, pois a sobrecarga da chamada de função é muito alta.Casos recursivos como o fatorial mostrado usarão truques como chamada de cauda para remover o uso de memória O(n).O fato de não haver efeitos colaterais permite que compiladores funcionais implementem o && ret otimização mesmo quando o .reduce é feito por último.Usar Lodash em JS obviamente não permite nenhuma otimização, por isso é um impacto no desempenho (o que geralmente não é uma preocupação no desenvolvimento web).Linguagens como Rust serão otimizadas internamente (e terão funções como try_fold ajudar && ret otimização).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top