Este é um bom exercício para se tornar mais fluente na linguagem de programação que você pretende aprender, mas que apenas mexeu levemente. Isso envolve trabalhar com objetos, usar ou simular fechamentos e esticar o sistema de tipos.
Sua tarefa é escrever código para gerenciar listas preguiçosas e usá-lo para implementar esse algoritmo para gerar números de Fibonacci:
As amostras de código estão em Haskell
let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
in take 40 fibs
Resultado:
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,39088169,63245986]
Sua implementação de lista lenta deve atender a estas diretrizes:
- Um nó da lista é uma das três coisas:
- Nil - lista vazia.
[]
- Contras - Um único item, emparelhado com uma Lista dos itens restantes:
1 : [2,3,4,5]
(:
é o operador contras em Haskell) - Thunk - Uma computação adiada que produz um nó de Lista quando necessário.
- Nil - lista vazia.
- Ele suporta as seguintes operações:
- nil - Construa uma lista vazia.
- contras - Construa uma célula contras.
- thunk - Construa um Thunk, dada uma função que não aceita argumentos e retorna Nil ou Contras.
- force - Dado um nó de lista:
- Se for Nil ou Contras, basta devolvê-lo.
- Se for um Thunk, chame sua função para obter Nil ou Cons. Substitua o thunk por Nil ou Cons e devolva-o.
Nota: A substituição da conversão por seu valor forçado é uma parte importante da definição de "preguiçoso" . Se esta etapa for ignorada, o algoritmo Fibonacci acima será muito lento.
- vazio - Veja se um nó da Lista é Nil (depois de forçá-lo).
- head (aka "car") - Pegue o primeiro item de uma lista (ou ajuste se for Nil).
- tail (aka "cdr") - Obtenha os elementos após o cabeçalho de uma lista (ou ajuste se for Nil).
- zipWith - Dada uma função binária (por exemplo
(+)
) e duas listas (possivelmente infinitas), aplique a função aos itens correspondentes das listas. Exemplo:
zipWith (+) [1,2,3] [1,1,10] == [2,3,13]
- take - Dado um número N e uma lista (possivelmente infinita), pegue os primeiros N itens da lista.
- imprimir - Imprima todos os itens de uma lista. Isso deve funcionar de maneira incremental quando é fornecida uma lista longa ou infinita.
fibs
usa-se em sua própria definição. Configurar recursão lenta é um pouco complicado; você precisará fazer algo assim:- Aloque um thunk para
fibs
. Deixe-o em um estado fictício por enquanto. - Defina a função thunk, que depende de uma referência a
fibs
. - Atualize o thunk com sua função.
Você pode ocultar esse encanamento definindo uma função
fix
que chama uma função de retorno de lista com seu próprio valor de retorno. Considere tirar um cochilo curto para que essa ideia possa surgir.- Aloque um thunk para
O polimorfismo (a capacidade de trabalhar com listas de qualquer tipo de item) não é necessário, mas veja se você consegue encontrar uma maneira de fazer isso que seja idiomática no seu idioma.
- Não se preocupe com o gerenciamento de memória. Mesmo os idiomas com coleta de lixo tendem a carregar objetos que você nunca mais usará (por exemplo, na pilha de chamadas), portanto, não se surpreenda se o seu programa perder memória ao percorrer uma lista infinita.
Sinta-se à vontade para se afastar um pouco dessas diretrizes para acomodar os detalhes do seu idioma ou para explorar abordagens alternativas às listas preguiçosas.
Regras:
- Escolha um idioma que você não conhece bem. Não posso "exigir" isso, daí a tag "sistema de honra". No entanto, os eleitores podem verificar seu histórico para ver em quais idiomas você está postando.
Não use o suporte à lista lenta do seu idioma para fazer tudo. Poste algo substancial ou pelo menos interessante.
Haskell está praticamente fora. Ou seja, a menos que você faça algo assim:
data List a = IORef (ListNode a) data ListNode a = Nil | Cons !a !(List a) | Thunk !(IO (ListNode a))
Nota: A avaliação não estrita de Haskell não está fora dos limites, mas sua implementação de lista lenta não deve derivar sua capacidade diretamente a partir daí. De fato, seria interessante ver uma solução eficiente e puramente funcional que não exija preguiça.
Python:
- Não use ferramentas.
- Os geradores são bons, mas, se você os usar, precisará encontrar uma maneira de memorizar valores forçados.
zipWith (+) [1,2,3,4,5] [0,0,0] == [1,2,3]
,. No entanto, isso não importa para o algoritmo Fibonacci acima, pois os dois argumentos para zipWith são listas infinitas.
fibs
corretamente, pois depende de si mesmo. Atualizei a questão para elaborar uma recursão lenta. FUZxxl descobriu por si mesmo.
zipWith
duas listas de diferentes comprimentos?