Otimizações de combinador Y e chamada de cauda


20

A definição de um combinador Y em F # é

let rec y f x = f (y f) x

f espera ter como primeiro argumento alguma continuação para os subproblemas recursivos. Usando o yf como uma continuação, vemos que f será aplicado a chamadas sucessivas à medida que pudermos desenvolver

let y f x = f (y f) x = f (f (y f)) x = f (f (f (y f))) x etc...

O problema é que, a priori, esse esquema impede o uso de qualquer otimização de chamada de cauda: de fato, pode haver alguma operação pendente nos f's, nesse caso, não podemos simplesmente alterar o quadro de pilha local associado a f.

Tão :

  • por um lado, o uso do combinador Y requer uma continuação explícita diferente da própria função.
  • por outro lado, para aplicar o TCO, gostaríamos de não ter nenhuma operação pendente em f e apenas chamar f em si.

Você sabe de que maneira esses dois poderiam ser reconciliados? Como um Y com truque de acumulador ou um Y com truque de CPS? Ou um argumento provando que não há como isso ser feito?


Você adicionou o trabalho de tecla rec à sua implementação? Eu diria que ele precisa da minha leitura ..
Jimmy Hoffa

Você tem provas de que não otimiza a chamada final? Eu acho que você pode querer ler o IL para essa função e veja, eu não ficaria surpreso se o compilador é inteligente o suficiente para chegar a algo ..
Jimmy Hoffa

no caso de uma recursão desamarrada direta, isso não ocorre: no entanto, você pode reescrevê-lo para permitir tal coisa, desde que o quadro da pilha seja reutilizado através da chamada y. Sim, talvez seja necessário ver a IL, nenhuma experiência nisso.
nicolas

5
Eu fiz uma conta e consegui 50 pontos apenas para comentar aqui. Esta questão é realmente interessante. Eu acho que depende inteiramente disso f. Podemos ver que ypoderia chamar fcom um thunk (y f), mas, como você diz, fpode ter alguma operação pendente. Eu acho que seria interessante saber se existe um combinador separado que seja mais amigável ao tailcall. Gostaria de saber se esta pergunta seria melhor atenção no site CS Stackexchange?
John Cartwright

Respostas:


4

Você sabe de que maneira esses dois poderiam ser reconciliados?

Não, e por boas razões, IMHO.

O combinador Y é uma construção teórica e é necessária apenas para completar o cálculo do lambda (lembre-se, não há loops no cálculo do lambda, nem os lambdas têm nomes que poderíamos usar para recursão).

Como tal, o combinador Y é verdadeiramente fascinante.

Mas : na verdade, ninguém usa o combinador Y para recursão real! (Exceto talvez por diversão, para mostrar que realmente funciona.)

A otimização de chamada de cauda, ​​OTOH, é, como o nome diz, uma otimização. Isso não acrescenta nada à expressividade de uma linguagem, é apenas por considerações práticas, como espaço na pilha e desempenho do código recursivo, que nos preocupamos com isso.

Portanto, sua pergunta é: Existe suporte de hardware para redução beta? (Redução beta é como as expressões lambda são reduzidas, você sabe.) Mas nenhuma linguagem funcional (até onde eu saiba) compila seu código-fonte para uma representação de expressões lambda que serão reduzidas beta em tempo de execução.


2
O combinador Y é como repetir um nó que fica desamarrado após cada uso. A maioria dos sistemas ataca isso e amarra o nó no nível meta, para que ele nunca precise ser repetido.
Dan D.

1
Quanto ao último parágrafo, considere Haskell, que no fundo utiliza redução gráfica para fazer uma avaliação lenta. Mas o meu favorito é a redução ideal, que sempre segue o caminho da estrutura de Church-Rosser com as menores reduções para a forma normal completa. Tal como aparece em A implementação ideal de linguagens de programação funcional de Asperti e Guerrini . Veja também BOHM 1.1 .
Dan D.

@DanD. Obrigado pelos links, tentarei mais tarde em um navegador compatível com postscript. Claro que há algo a aprender para mim. Mas, você tem certeza de que o haskell compilado reduz os gráficos? Eu duvido disso.
Ingo

1
Na verdade, ele usa a redução gráfica: "O GHC é compilado na máquina G sem etiqueta (STG). Esta é uma máquina de redução gráfica de gráficos (ou seja, uma máquina virtual que executa reduções gráficas conforme descrito acima)". Para ... Para saber mais sobre a máquina STG, consulte Implementando linguagens funcionais preguiçosas de Simon Peyton Jones em hardware de estoque: a máquina G Spineless Tagless .
Dan D.

@DanD. no mesmo artigo que você vinculou, ele lê mais adiante que o GHC "faz várias otimizações nessa representação, antes de finalmente compilá-la em código de máquina real (possivelmente via C usando o GCC)".
Ingo

0

Não tenho certeza absoluta sobre essa resposta, mas é a melhor que pude apresentar.

O combinador y é inerentemente preguiçoso, em idiomas estritos a preguiça deve ser adicionada manualmente através de lambdas extras.

let rec y f x = f (y f) x

Sua definição parece exigir preguiça para terminar, ou o (y f)argumento nunca terminaria de avaliar e teria que avaliar se a fusava ou não . O TOC em um contexto lento é mais complicado e, além disso, o resultado (y f)é uma composição repetida de funções sem aplicação com o x. Não tenho certeza se isso precisa levar O (n) memória onde n é a profundidade da recursão, mas duvido que você possa obter o mesmo tipo de sumário possível com algo como (alternar para Haskell porque eu realmente não sei F #)

length acc []    = acc
length acc (a:b) = length (acc+1) b 

Se você ainda não está ciente disso, a diferença entre foldle foldl'em Haskell pode lançar alguma luz sobre a situação. foldlé escrito como seria feito em um idioma ansioso. Mas, em vez de ser um TOC, é realmente pior do que foldrporque o acusador armazena um thunk potencialmente enorme que não pode ser avaliado parcialmente. (Isso está relacionado ao motivo pelo qual foldl e foldl 'não funcionam em listas infinitas.) Assim, nas versões mais recentes do Haskell, foldl'foi adicionado o que força a avaliação do acumulador cada vez que a função se repete para garantir que não seja criado nenhum thunk enorme. Tenho certeza que http://www.haskell.org/haskellwiki/Foldr_Foldl_Foldl%27 pode explicar isso melhor do que eu.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.