0%
Pular para o conteúdo principal
0%

3.5.4 Streams e Avaliação Atrasada

A função integral no final da seção anterior mostra como podemos usar streams para modelar sistemas de processamento de sinais que contêm loops de realimentação. O loop de realimentação para o somador mostrado na figura é modelado pelo fato de que o stream interno integ é definido em termos de si mesmo:

const integ = pair(initial_value,
() => add_streams(scale_stream(integrand, dt),
integ));

A capacidade do interpretador de lidar com tal definição implícita depende do atraso resultante de embrulhar a chamada para add_streams em uma expressão lambda. Sem esse atraso, o interpretador não poderia construir integ antes de avaliar a chamada para add_streams, o que exigiria que integ já estivesse definido. Em geral, tal atraso é crucial para usar streams para modelar sistemas de processamento de sinais que contêm loops. Sem um atraso, nossos modelos teriam que ser formulados de modo que as entradas para qualquer componente de processamento de sinal fossem totalmente avaliadas antes que a saída pudesse ser produzida. Isso proibiria loops.

Infelizmente, modelos de stream de sistemas com loops podem exigir usos de atraso além do padrão de programação de stream visto até agora. Por exemplo, a figura mostra um sistema de processamento de sinais para resolver a equação diferencial dy/dt=f(y)dy/dt = f(y) onde ff é uma função dada. A figura mostra um componente de mapeamento, que aplica ff ao seu sinal de entrada, ligado em um loop de realimentação a um integrador de maneira muito similar à dos circuitos de computador analógico que são realmente usados para resolver tais equações.

dy/dty+dtfdy/dty

Figura 3.54: Sistema de processamento de sinais com loop de realimentação para resolver dy/dt=f(y)dy/dt = f(y). O sinal de entrada dy/dt é integrado para produzir y, que passa por uma função de ganho f (representando a função diferencial), e retorna ao somador através de um loop de realimentação tracejado.

Assumindo que nos é dado um valor inicial y0y_0 para yy, poderíamos tentar modelar este sistema usando a função

function solve(f, y0, dt) {
const y = integral(dy, y0, dt);
const dy = stream_map(f, y);
return y;
}

Esta função não funciona, porque na primeira linha de solve a chamada para integral requer que o argumento de entrada dy seja definido, o que não acontece até a segunda linha de solve.

Por outro lado, a intenção de nossa definição faz sentido, porque podemos, em princípio, começar a gerar o stream y sem conhecer dy. De fato, integral e muitas outras operações de stream podem gerar parte da resposta dada apenas informação parcial sobre os argumentos. Para integral, o primeiro elemento do stream de saída é o initial_value especificado. Assim, podemos gerar o primeiro elemento do stream de saída sem avaliar o integrando dy. Uma vez que conhecemos o primeiro elemento de y, o stream_map na segunda linha de solve pode começar a trabalhar para gerar o primeiro elemento de dy, que produzirá o próximo elemento de y, e assim por diante.

Para tirar vantagem dessa ideia, redefiniremos integral para esperar que o stream do integrando seja um argumento atrasado. A função integral forçará o integrando a ser avaliado apenas quando for necessário para gerar mais do que o primeiro elemento do stream de saída:

function integral(delayed_integrand, initial_value, dt) {
const integ =
pair(initial_value,
() => {
const integrand = delayed_integrand();
return add_streams(scale_stream(integrand, dt),
integ);
});
return integ;
}

Agora podemos implementar nossa função solve atrasando a avaliação de dy na declaração de y:

function solve(f, y0, dt) {
const y = integral(() => dy, y0, dt);
const dy = stream_map(f, y);
return y;
}

Em geral, cada chamador de integral deve agora atrasar o argumento do integrando. Podemos demonstrar que a função solve funciona aproximando e2.718e \approx 2.718 computando o valor em y=1y = 1 da solução para a equação diferencial dy/dt=ydy/dt = y com condição inicial y(0)=1y(0) = 1:[^1]

stream_ref(solve(y => y, 1, 0.001), 1000);
2.716923932235896

Exercício 3.77

A função integral usada acima foi análoga à definição "implícita" do stream infinito de inteiros na seção 3.5.2. Alternativamente, podemos dar uma definição de integral que é mais como integers_starting_from (também na seção 3.5.2):

function integral(integrand, initial_value, dt) {
return pair(initial_value,
is_null(integrand)
? null
: integral(stream_tail(integrand),
dt * head(integrand) + initial_value,
dt));
}

Quando usada em sistemas com loops, esta função tem o mesmo problema que nossa versão original de integral. Modifique a função para que ela espere o integrand como um argumento atrasado e, portanto, possa ser usada na função solve mostrada acima.

Exercício 3.78

Considere o problema de projetar um sistema de processamento de sinais para estudar a equação diferencial linear homogênea de segunda ordem

d2ydt2adydtby=0\frac{d^2y}{dt^2} - a\frac{dy}{dt} - by = 0

O stream de saída, modelando yy, é gerado por uma rede que contém um loop. Isso ocorre porque o valor de d2y/dt2d^2y/dt^2 depende dos valores de yy e dy/dtdy/dt e ambos são determinados integrando d2y/dt2d^2y/dt^2. O diagrama que gostaríamos de codificar é mostrado na figura.

dy₁y₁dy₂y₂+dt×a+dt×bdy₁dy₂y₁y₂

Figura 3.78: Sistema de processamento de sinais para resolver sistemas de equações diferenciais acopladas. Dois integradores processam dy₁ e dy₂ para produzir y₁ e y₂, com multiplicadores de ganho a e b. Os loops de realimentação cruzados (tracejados) mostram como cada equação depende da solução da outra.

Escreva uma função solve_2nd que recebe como argumentos as constantes aa, bb e dtdt e os valores iniciais y0y_0 e dy0dy_0 para yy e dy/dtdy/dt e gera o stream de valores sucessivos de yy.

Exercício 3.79

Generalize a função solve_2nd do exercício 3.78 para que ela possa ser usada para resolver equações diferenciais de segunda ordem gerais d2y/dt2=f(dy/dt,y)d^2y/dt^2 = f(dy/dt, y).

Exercício 3.80

Um circuito RLC em série consiste de um resistor, um capacitor e um indutor conectados em série, como mostrado na figura. Se RR, LL e CC são a resistência, indutância e capacitância, então as relações entre voltagem (vv) e corrente (ii) para os três componentes são descritas pelas equações

undefined