Alguém sabe qual é a pior desaceleração assintótica possível que pode acontecer durante a programação puramente funcional em oposição a imperativamente (isto é, permitindo efeitos colaterais)?
Esclarecimento do comentário de itowlson : existe algum problema para o qual o algoritmo não destrutivo mais conhecido seja assintoticamente pior que o algoritmo destrutivo mais conhecido e, em caso afirmativo, quanto?
Respostas:
De acordo com Pippenger [1996] , ao comparar um sistema Lisp que é puramente funcional (e tem uma semântica estrita de avaliação, não preguiçosa) com um que pode alterar dados, um algoritmo escrito para o impuro Lisp executado em O ( n ) pode ser traduzido a um algoritmo no Lisp puro que é executado no tempo O ( n log n ) (baseado no trabalho de Ben-Amram e Galil [1992] sobre simulação de memória de acesso aleatório usando apenas ponteiros). Pippenger também estabelece que existem algoritmos para o melhor que você pode fazer; existem problemas que são O ( n ) no sistema impuro que são Ω ( n log n ) no sistema puro.
Existem algumas ressalvas a serem feitas sobre este artigo. O mais significativo é que ele não trata de linguagens funcionais preguiçosas, como Haskell. Bird, Jones e De Moor [1997] demonstram que o problema construído por Pippenger pode ser resolvido em uma linguagem funcional preguiçosa em O ( n ) tempo, mas eles não estabelecem (e até onde eu sei, ninguém o tem) se ou não nenhuma linguagem funcional preguiçosa pode resolver todos os problemas no mesmo tempo de execução assintótico que uma linguagem com mutação.
O problema criado por Pippenger requer Ω ( n log n ) é especificamente construído para alcançar esse resultado e não é necessariamente representativo de problemas práticos do mundo real. Existem algumas restrições no problema que são um pouco inesperadas, mas necessárias para que a prova funcione; em particular, o problema requer que os resultados sejam computados on-line, sem poder acessar a entrada futura, e que a entrada consiste em uma sequência de átomos a partir de um conjunto ilimitado de átomos possíveis, em vez de um conjunto de tamanho fixo. E o artigo apenas estabelece resultados (limite inferior) para um algoritmo impuro de tempo de execução linear; para problemas que exigem um tempo de execução maior, é possível que o O extra (log n), o fator observado no problema linear pode ser "absorvido" no processo de operações extras necessárias para algoritmos com maiores tempos de execução. Esses esclarecimentos e questões em aberto são explorados brevemente por Ben-Amram [1996] .
Na prática, muitos algoritmos podem ser implementados em uma linguagem funcional pura com a mesma eficiência que em uma linguagem com estruturas de dados mutáveis. Para uma boa referência sobre técnicas a serem usadas para implementar estruturas de dados puramente funcionais com eficiência, consulte "Estruturas de Dados Puramente Funcionais" de Chris Okasaki [Okasaki 1998] (que é uma versão expandida de sua tese [Okasaki 1996] ).
Qualquer pessoa que precise implementar algoritmos em estruturas de dados puramente funcionais deve ler Okasaki. Você sempre pode obter, na pior das hipóteses, uma desaceleração de O (log n ) por operação simulando memória mutável com uma árvore binária equilibrada, mas em muitos casos você pode se sair consideravelmente melhor do que isso, e Okasaki descreve muitas técnicas úteis, de técnicas amortizadas a reais. os que executam o amortizado trabalham incrementalmente. Estruturas de dados puramente funcionais podem ser um pouco difíceis de trabalhar e analisar, mas oferecem muitos benefícios, como transparência referencial, que são úteis na otimização do compilador, na computação paralela e distribuída e na implementação de recursos como controle de versão, desfazer e reversão.
Observe também que tudo isso discute apenas os tempos de execução assintóticos. Muitas técnicas para implementar estruturas de dados puramente funcionais fornecem uma certa quantidade de desaceleração constante dos fatores, devido à contabilidade extra necessária para que eles funcionem e aos detalhes de implementação do idioma em questão. Os benefícios de estruturas de dados puramente funcionais podem compensar essas lentidões constantes dos fatores; portanto, você geralmente precisará fazer trocas com base no problema em questão.
Referências
fonte
De fato, existem vários algoritmos e estruturas de dados para os quais nenhuma solução puramente funcional assintoticamente eficiente (aquela implementável no cálculo lambda puro) é conhecida, mesmo com preguiça.
Entretanto, assumimos que em linguagens "imperativas" o acesso à memória é O (1), enquanto na teoria isso não pode ser tão assintoticamente (ou seja, para tamanhos de problemas ilimitados) e o acesso à memória em um grande conjunto de dados é sempre O (log n) , que pode ser emulado em uma linguagem funcional.
Além disso, devemos lembrar que, na verdade, todas as linguagens funcionais modernas fornecem dados mutáveis, e Haskell ainda os fornece sem sacrificar a pureza (a mônada ST).
fonte
Este artigo alega que as implementações puramente funcionais conhecidas do algoritmo de localização e união têm pior complexidade assintótica do que a que eles publicam, que possui uma interface puramente funcional, mas usa dados mutáveis internamente.
O fato de outras respostas afirmarem que nunca pode haver diferença e que, por exemplo, a única "desvantagem" do código puramente funcional é que ele pode ser paralelizado fornece uma idéia da informação / objetividade da comunidade de programação funcional sobre esses assuntos. .
EDITAR:
Os comentários abaixo apontam que uma discussão tendenciosa dos prós e contras da programação funcional pura pode não vir da "comunidade de programação funcional". Bom ponto. Talvez os advogados que vejo sejam apenas, para citar um comentário, "analfabetos".
Por exemplo, acho que este post do blog foi escrito por alguém que pode ser considerado representante da comunidade de programação funcional e, como é uma lista de "pontos para avaliação preguiçosa", seria um bom lugar para mencionar qualquer desvantagem que programação preguiçosa e puramente funcional pode ter. Um bom lugar teria sido o seguinte (tecnicamente verdadeiro, mas tendencioso a ponto de não ser engraçado):
fonte
Com um limite superior fixo no uso da memória, não deve haver diferença.
Esboço de prova: Dado um limite superior fixo no uso da memória, deve-se conseguir escrever uma máquina virtual que execute um conjunto de instruções imperativas com a mesma complexidade assintótica como se você estivesse realmente executando nessa máquina. Isso ocorre porque você pode gerenciar a memória mutável como uma estrutura de dados persistente, fornecendo leitura e gravação de O (log (n)), mas com um limite superior fixo no uso da memória, você pode ter uma quantidade fixa de memória, fazendo com que decaimento para O (1). Portanto, a implementação funcional pode ser a versão imperativa em execução na implementação funcional da VM e, portanto, ambas devem ter a mesma complexidade assintótica.
fonte
Sugiro ler sobre o desempenho de Haskell e, em seguida, analisar os desempenhos dos jogos de benchmark para linguagens funcionais versus processuais / OO.
fonte