Digamos que fn(x)
é uma função pura que faz algo caro, como retornar uma lista dos principais fatores de x
.
E digamos que criamos uma versão memorizada da mesma função chamada memoizedFn(x)
. Ele sempre retorna o mesmo resultado para uma determinada entrada, mas mantém um cache privado dos resultados anteriores para melhorar o desempenho.
Formalmente falando, é memoizedFn(x)
considerado puro?
Ou existe algum outro nome ou termo qualificado usado para se referir a essa função nas discussões sobre PF? (ou seja, uma função com efeitos colaterais que podem afetar a complexidade computacional das chamadas subseqüentes, mas que não afetam os valores de retorno.)
terminology
pure-function
callum
fonte
fonte
funcx(){sleep(cached_time--); return 0;}
retorna o mesmo val todas as vezes, mas terá um desempenho diferenteRespostas:
Sim. A versão memorizada de uma função pura também é uma função pura.
Tudo o que se preocupa com a pureza da função é o efeito que os parâmetros de entrada no valor de retorno da função (passar a mesma entrada sempre deve produzir a mesma saída) e quaisquer efeitos colaterais relevantes para os estados globais (por exemplo, texto para o terminal, interface do usuário ou rede) . O tempo de computação e o uso extra de memória são irrelevantes para a pureza da função.
Os caches de uma função pura são praticamente invisíveis para o programa; é permitido que uma linguagem de programação funcional otimize automaticamente uma função pura para uma versão memorizada da função, se puder determinar que será benéfico fazê-lo. Na prática, determinar automaticamente quando a memorização é benéfica é realmente um problema bastante difícil, mas essa otimização seria válida.
fonte
A Wikipedia define uma "Função pura" como uma função que possui as seguintes propriedades:
Seu valor de retorno é o mesmo para os mesmos argumentos (nenhuma variação com variáveis estáticas locais, variáveis não locais, argumentos de referência mutáveis ou fluxos de entrada de dispositivos de E / S).
Sua avaliação não tem efeitos colaterais (nenhuma mutação de variáveis estáticas locais, variáveis não locais, argumentos de referência mutáveis ou fluxos de E / S).
Com efeito, uma função pura retorna a mesma saída, dada a mesma entrada, e não afeta mais nada fora da função. Para fins de pureza, não importa como a função calcula seu valor de retorno, desde que retorne a mesma saída, com a mesma entrada.
Linguagens funcionalmente puras, como Haskell, usam rotineiramente a memorização para acelerar uma função, armazenando em cache seus resultados calculados anteriormente.
fonte
static const
variável local em C ++ (mas não C) ou uma estrutura de dados avaliada preguiçosamente em Haskell. Você precisa de mais uma condição: a inicialização deve ser segura para threads.Sim, funções puras memorizadas são comumente referidas como puras. Isso é especialmente comum em idiomas como Haskell, nos quais resultados imutáveis, memorizados, com preguiça de avaliar preguiçosamente são um recurso interno.
Há uma ressalva importante: a função de memorização deve ser segura para threads, ou você pode ter uma condição de corrida quando dois threads tentam chamá-lo.
Um exemplo de cientista da computação que usa o termo "puramente funcional" dessa maneira é este post de Conal Elliott sobre memoização automática:
Existem muitos exemplos na literatura revisada por pares e existem há décadas. Por exemplo, este artigo de 1995, “Usando a memorização automática como ferramenta de engenharia de software em sistemas de IA do mundo real”, usa linguagem muito semelhante na seção 5.2 para descrever o que hoje chamaríamos de função pura:
Algumas linguagens imperativas têm um idioma semelhante. Por exemplo, uma
static const
variável em C ++ é inicializada apenas uma vez, antes que seu valor seja usado e nunca sofre mutação.fonte
Depende de como você faz.
Geralmente, as pessoas querem memorizar modificando algum tipo de dicionário de cache. Isso tem todos os problemas associados à mutação impura, como ter que se preocupar com simultaneidade, se preocupar com o cache ficar muito grande etc.
No entanto, você pode memorizar sem mutação impura na memória. Um exemplo é nesta resposta , onde eu rastreio os valores memorizados externamente por meio de um
lengths
argumento.No link fornecido por Robert Harvey , a avaliação preguiçosa é usada para evitar efeitos colaterais.
Outra técnica às vezes vista é marcar explicitamente a memoização como um efeito colateral impuro no contexto de um
IO
tipo, como na função memoize do efeito de gato .Este último traz o argumento de que, às vezes, o objetivo é apenas encapsular a mutação em vez de eliminá-la. A maioria dos programadores funcionais considera "puro o suficiente" para tornar a impureza explícita e encapsulada.
Se você quer que um termo o diferencie de uma função verdadeiramente pura, acho que basta dizer "memorizado com um dicionário mutável". Isso permite que as pessoas saibam usá-lo com segurança.
fonte
collatz(100)
ecollatz(200)
para cooperar. E IIUIC, o problema com o cache cresce muito permanece (embora Haskell possa ter alguns truques interessantes para isso?).IO
é puro. Todos os métodos impurosIO
e Gatos são nomeadosunsafe
.Async.memoize
também é puro, por isso não precisamos nos contentar com "puro o suficiente" :)Geralmente, uma função que retorna uma lista não é pura, porque requer alocação de armazenamento e pode falhar (por exemplo, lançando uma exceção que não é pura). Um idioma que possui tipos de valor e pode representar uma lista como um tipo de valor de tamanho limitado pode não ter esse problema. Por esse motivo, seu exemplo provavelmente não é puro.
Em geral, se a memorização puder ser feita de maneira isenta de falhas (por exemplo, tendo armazenamento estaticamente alocado para resultados memorizados e sincronização interna para controlar o acesso a eles se o idioma admitir encadeamentos), é razoável considerar essa função puro.
fonte
Você pode implementar a memorização sem efeitos colaterais usando a mônada do estado .
No seu caso, o estado seria o valor memorizado ou nada (ou seja, Haskell
Maybe
ou ScalaOption[A]
). Se o valor memorizado estiver presente, ele será retornado comoA
, caso contrário,A
será calculado e retornado como o estado de transição e o resultado.fonte