Derivação clara e intuitiva do combinador de ponto fixo (combinador Y)?

28

O combinador de ponto fixo FIX (também conhecido como combinador Y) no cálculo lambda (sem tipo) ( ) é definido como:λ

FIXλf.(λx.f (λy.x x y)) (λx.f (λy.x x y))

Entendo sua finalidade e posso rastrear perfeitamente a execução de seu aplicativo; Gostaria de entender como derivar o FIX dos primeiros princípios .

Aqui está o que eu vejo quando tento derivar isso sozinho:

  1. CORRECÇÃO é uma função: CORRECÇÃOλ
  2. O FIX utiliza outra função, , para torná-la recursiva: FIXX f . ...fλf.
  3. O primeiro argumento da função é o "nome" da função, usado onde um aplicativo recursivo se destina. Portanto, todas as aparências do primeiro argumento para devem ser substituídas por uma função, e essa função deve esperar o restante dos argumentos de (vamos supor que use um argumento): FIXf f f λ f . ... f ( λ y . ... y )ffffλf.f (λy.y)

É aqui que eu não sei "dar um passo" no meu raciocínio. As pequenas elipses indicam onde meu FIX está faltando alguma coisa (embora eu só possa saber disso comparando-o com o FIX "real").

Eu já li Tipos e Linguagens de Programação , que não tentam derivá-lo diretamente e, em vez disso, encaminha o leitor para The Little Schemer para uma derivação. Também li isso e sua "derivação" não foi tão útil. Além disso, é menos uma derivação direta e mais um uso de um exemplo muito específico e uma tentativa ad-hoc de escrever uma função recursiva adequada em .λ

BlueBomber
fonte
11
Esta postagem pode ser útil. Em geral, acho que apenas analisar e computar várias iterações do combinador é útil para descobrir por que ele funciona.
Xodarap
2
Existem vários combinadores de pontos fixos diferentes. Talvez as pessoas apenas brincassem com combinadores até que tropeçassem neles.
Yuval Filmus
@YuvalFilmus, é isso que minha pesquisa e a resposta a esta pergunta estão começando a me fazer pensar. Mas ainda acho que seria instrutivo "ver" como os combinadores são formados logicamente, uma habilidade que seria especialmente útil quando, por exemplo, tentando construir um novo combinador.
BlueBomber 09/02
Leia o capítulo 9 em "The Little Lisper", de Daniel P. Friedman (ou "The Little Schemer").
user18199
2
O PO parece indicar que eles já leram isso.
Raphael

Respostas:

29

Eu não li isso em nenhum lugar, mas é assim que acredito que poderia ter sido derivado:Y

Vamos ter uma função recursiva , talvez um fatorial ou qualquer outra coisa assim. Informalmente, definimos como termo pseudo-lambda onde ocorre em sua própria definição:f ffff

f=ff

Primeiro, percebemos que a chamada recursiva pode ser fatorada como um parâmetro:

f=(λr.(rr))Mf

Agora poderíamos definir se tivéssemos uma maneira de passar isso como argumento para si mesmo. Isso não é possível, é claro, porque não temos em mãos. O que temos em mãos é . Como contém tudo o que precisamos para definir , podemos tentar passar como argumento em vez de e tentar reconstruir mais tarde. Nossa primeira tentativa é assim:f M M f M f fffMMfMff

f=(λr.(rr))M(λr.(rr))M

No entanto, isso não está completamente correto. Antes, foi substituído por no interior . Mas agora passamos vez. Temos de corrigir alguma forma todos os lugares onde usamos para que eles reconstruir de . Na verdade, isso não é nada difícil: agora que sabemos que , em todos os lugares que usamos , simplesmente o substituimos por .r M M r f M f = M M r ( r r )frMMrfMf=MMr(rr)

f=(λr.((rr)(rr)))M(λr.((rr)(rr)))M

Esta solução é boa, mas tivemos que alterar dentro. Isso não é muito conveniente. Podemos fazer isso de forma mais elegante sem precisar modificar introduzindo outro que envia a seu argumento aplicado a si mesmo: expressando como obtemosM λ M M λ x . M ( x x )MMλMMλx.M(xx)

f=(λx.(λr.(rr))M(xx))(λx.(λr.(rr))M(xx))

Dessa forma, quando é substituído por , é substituído por , que é pela definição igual a . Isso nos dá uma definição não recursiva de , expressa como um termo lambda válido!x M M r f fMxMMrff

A transição para agora é fácil. Podemos usar um termo lambda arbitrário em vez de e executar esse procedimento nele. Para que possamos fatorar e definirM MYMM

Y=λm.(λx.m(xx))(λx.m(xx))

De fato, o reduz a como o definimos.fYMf


Nota: Derivei conforme definido na literatura. O combinator você descreveu é uma variante do para chamada por valor línguas, às vezes também chamado de . Veja este artigo da Wikipedia .Y ZYYZ

Petr Pudlák
fonte
11
A-mas-aparentemente óbvia falta intuição de que a sua excelente resposta me deu é que uma função recursiva precisa-se como um argumento, por isso, começar com um pressuposto de que a função terá a forma por algum . Então, quando construímos , usamos a afirmação de que é definido como a aplicação de algo a si próprio internamente em , por exemplo, aplicando a em sua resposta, que por definição é igual a . Fascinante! X X f X x x ff=X(X)XXfXxxf
BlueBomber
11

Como Yuval apontou, não há apenas um operador de ponto fixo. Existem muitos deles. Em outras palavras, a equação para o teorema do ponto fixo não possui uma única resposta. Portanto, você não pode derivar o operador deles.

É como perguntar como as pessoas derivam como uma solução para . Eles não! A equação não tem uma solução única.x = y(x,y)=(0,0)x=y


Para o caso de você querer saber como foi descoberto o primeiro teorema de ponto fixo. Deixe-me dizer que também me perguntei como eles surgiram com os teoremas de ponto fixo / recursão quando os vi pela primeira vez. Parece tão engenhoso. Particularmente na forma da teoria da computabilidade. Ao contrário do que Yuval diz, não é o caso de as pessoas brincarem até encontrar algo. Aqui está o que eu encontrei:

Tanto quanto me lembro, o teorema é originalmente devido a SC Kleene. Kleene criou o teorema original do ponto fixo ao recuperar a prova de inconsistência do cálculo lambda original da Igreja. O cálculo lambda original da Igreja sofria de um paradoxo do tipo Russel. O cálculo lambda modificado evitou o problema. Kleene estudou a prova de inconsistência provavelmente para ver como se o cálculo lambda modificado sofria de um problema semelhante e transformou a prova de inconsistência em um teorema útil do cálculo lambda modificado. Através de seu trabalho sobre equivalência do cálculo lambada com outros modelos de computação (máquinas de Turing, funções recursivas etc.), ele o transferiu para outros modelos de computação.


Como derivar o operador que você pode perguntar? Aqui está como eu mantenho isso em mente. O teorema do ponto fixo trata da remoção da auto-referência.

Todo mundo conhece o paradoxo do mentiroso:

Eu sou um covil.

Ou na forma mais lingüística:

Esta frase é falsa.

Agora, a maioria das pessoas pensa que o problema com esta frase está na auto-referência. Não é! A auto-referência pode ser eliminada (o problema é com a verdade, uma linguagem não pode falar sobre a verdade de suas próprias sentenças em geral, veja o teorema da indefinibilidade do verbo de Tarski ). O formulário em que a auto-referência é removida é o seguinte:

Se você escrever a seguinte citação duas vezes, na segunda vez entre aspas, a sentença resultante será falsa: "Se você escrever a seguinte citação duas vezes, na segunda vez entre aspas, a sentença resultante será falsa:"

Sem auto-referência, temos instruções sobre como construir uma frase e depois fazer algo com ela. E a frase que é construída é igual às instruções. Observe que em -calculus não precisamos de aspas porque não há distinção entre dados e instruções.λ

Agora, se analisarmos isso, temos onde é as instruções para construir e fazer alguma coisa.MMMxxx

Mx=f(xx)

Então é e temosMλx.f(xx)

MM=(λx.f(xx))(λx.f(xx))

Isto é para um fixo . Se você deseja torná-lo um operador, basta adicionar e obteremos :fλfY

Y=λf.(MM)=λf.((λx.f(xx))(λx.f(xx)))

Por isso, lembro o paradoxo sem auto-referência e isso me ajuda a entender o que é oY

Kaveh
fonte
3

Então você precisa definir um combinador de ponto fixo

fix f = f (fix f)
      = f (f (fix f))
      = f (f (f ... ))

mas sem recursão explícita. Vamos começar com o combinador irredutível mais simples

omega = (\x. x x) (\x. x x)
      = (\x. x x) (\x. x x)
      = ...

O xprimeiro lambda é repetidamente substituído pelo segundo lambda. A simples conversão alfa torna esse processo mais claro:

omega =  (\x. x x) (\x. x x)
      =α (\x. x x) (\y. y y)
      =β (\y. y y) (\y. y y)
      =α (\y. y y) (\z. z z)
      =β (\z. z z) (\z. z z)

Ou seja, a variável no primeiro lambda sempre desaparece. Então, se adicionarmos um fao primeiro lambda

(\x. f (x x)) (\y. y y)

o fvai subir

f ((\y. y y) (\y. y y))

Nós nos omegarecuperamos. Agora deve ficar claro que, se adicionarmos um fao segundo lambda, ele faparecerá no primeiro lambda e depois será exibido:

Y f = (\x. x x)     (\x. f (x x))
      (\x. f (x x)) (\x. f (x x)) -- the classical definition of Y

Desde a

(\x. s t) z = s ((\x. t) z), if `x' doesn't occur free in `s'

podemos reescrever a expressão como

f ((\x. x x) (\x. f (x x))

que é apenas

f (Y f)

e nós temos nossa equação Y f = f (Y f). Portanto, o Ycombinador é essencialmente

  1. dobrar o f
  2. fazer o primeiro fbalançar
  3. repetir
user3237465
fonte
2

Você pode ter visto o exemplo clássico de uma equação sem uma forma normal:

(λx.xx)(λx.xx)(λx.xx)(λx.xx)

Uma equação semelhante é sugerida por aquela para recursão geral:

(A)(λx.R(xx))(λx.R(xx)) R( (λx.R(xx))(λx.R(xx)) )R(R( (λx.R(xx))(λx.R(xx)) ))

(A) é uma maneira de escrever equações recursivas gerais no cálculo lambda (além da recursiva primitiva). Então, como você resolve a equação ? Conecte para na equação acima para obter:Yf=f(Yf)fR

Y = λ f . ( λ x . f ( x x ) ) ( λ x . f ( x x ) )

Yf=(λx.f(xx))(λx.f(xx))
Y=λf.(λx.f(xx))(λx.f(xx))
DanielV
fonte