Exercise 3.80: A
series RLC circuit
consists of a resistor, a capacitor, and an inductor connected in series, as
shown in Figure 3.36. If R, L, and C are the resistance,
inductance, and capacitance, then the relations between voltage (
v
) and
current (
i
) for the three components are described by the equations
v
R
=
i
R
R
,
v
L
=
L
d
i
L
d
t
,
i
C
=
C
d
v
C
d
t
,
and the circuit connections dictate the relations
i
R
=
i
L
=
−
i
C
,
v
C
=
v
L
+
v
R
.
Combining these equations shows that the state of the circuit (summarized by
v
C, the voltage across the capacitor, and i
L, the current in
the inductor) is described by the pair of differential equations
d
v
C
d
t
=
−
i
L
C
,
d
i
L
d
t
=
1
L
v
C
−
R
L
i
L
.
The signal-flow diagram representing this system of differential equations is
shown in Figure 3.37.
SVG
Figure 3.37: A signal-flow diagram for the solution to a series RLC circuit.
Write a procedure RLC that takes as arguments the parameters R,
L, and C of the circuit and the time increment d
t. In a manner
similar to that of the RC procedure of Exercise 3.73, RLC
should produce a procedure that takes the initial values of the state
variables, v
C
0 and i
L
0, and produces a pair (using
cons) of the streams of states v
C and i
L. Using
RLC, generate the pair of streams that models the behavior of a series
RLC circuit with R = 1 ohm, C = 0.2 farad, L = 1 henry, d
t
= 0.1 second, and initial values i
L
0 = 0 amps and v
C
0 =
10 volts.
Normal-order evaluation
The examples in this section illustrate how the explicit use of delay
and force provides great programming flexibility, but the same examples
also show how this can make our programs more complex. Our new integral
procedure, for instance, gives us the power to model systems with loops, but we
must now remember that integral should be called with a delayed
integrand, and every procedure that uses integral must be aware of this.
In effect, we have created two classes of procedures: ordinary procedures and
procedures that take delayed arguments. In general, creating separate classes
of procedures forces us to create separate classes of higher-order procedures
as well.
One way to avoid the need for two different classes of procedures is to make
all procedures take delayed arguments. We could adopt a model of evaluation in
which all arguments to procedures are automatically delayed and arguments are
forced only when they are actually needed (for example, when they are required
by a primitive operation). This would transform our language to use
normal-order evaluation, which we first described when we introduced the
substitution model for evaluation in 1.1.5. Converting to
normal-order evaluation provides a uniform and elegant way to simplify the use
of delayed evaluation, and this would be a natural strategy to adopt if we were
concerned only with stream processing. In 4.2, after we have
studied the evaluator, we will see how to transform our language in just this
way. Unfortunately, including delays in procedure calls wreaks havoc with our
ability to design programs that depend on the order of events, such as programs
that use assignment, mutate data, or perform input or output. Even the single
delay in cons-stream can cause great confusion, as illustrated by
Exercise 3.51 and Exercise 3.52. As far as anyone knows,
mutability and delayed evaluation do not mix well in programming languages, and
devising ways to deal with both of these at once is an active area of research.
练习 3.80:串联 RLC 电路 (series RLC circuit) 由电阻器、电容器和电感器串联组成,如图 3.36 所示。若 R、L、C 分别为电阻、电感和电容,则三个元件的电压 (v) 与电流 (i) 之间的关系由以下方程描述:vR = iR·R,vL = L·(diL/dt),iC = C·(dvC/dt),电路连接关系则规定:iR = iL = −iC,vC = vL + vR。综合这些方程可知,电路的状态(以电容器两端电压 vC 和电感器中电流 iL 来概括)由以下一对微分方程描述:dvC/dt = −iL/C,diL/dt = (1/L)vC − (R/L)iL。表示该微分方程组的信号流图如图 3.37 所示。
SVG
图 3.37:串联 RLC 电路解的信号流图。
请写一个过程 RLC,以电路参数 R、L、C 和时间增量 dt 为参数。与练习 3.73 中 RC 过程的方式类似,RLC 应产生一个过程,该过程以状态变量的初始值 vC₀ 和 iL₀ 为输入,产生流 vC 和 iL 组成的序对(使用 cons)。用 RLC 生成一对流,对一个 R = 1 欧姆、C = 0.2 法拉、L = 1 亨利、dt = 0.1 秒、初始值 iL₀ = 0 安培、vC₀ = 10 伏特的串联 RLC 电路的行为建模。
正则序求值
本节的例子说明了显式使用 delay 和 force 如何带来极大的程序设计灵活性,但同样的例子也表明这会使程序变得更加复杂。例如,我们新的 integral 过程赋予了我们对含有回路的系统建模的能力,但我们现在必须记住,integral 应当以延迟的被积分量调用,而且每个使用 integral 的过程都必须意识到这一点。实际上,我们已经创建了两类过程:普通过程和接受延迟参数的过程。一般而言,创建分离的过程类别也迫使我们相应地创建分离的高阶过程类别。
避免需要两类不同过程的一种方式,是让所有过程都接受延迟参数。我们可以采用一种求值模型,其中所有过程的参数都被自动延迟,只有在实际需要时(例如,当某个基本操作需要用到它们时)才强迫求值。这将把我们的语言转换为使用正则序求值 (normal-order evaluation),这是我们在 1.1.5 中引入代换模型时首次描述的。转换为正则序求值提供了一种统一而优雅的方式来简化延迟求值的使用,如果我们只关心流处理,这将是一种很自然的策略。在 4.2 中,当我们研究了求值器之后,将会看到如何以这种方式改造我们的语言。遗憾的是,在过程调用中加入延迟会严重破坏我们设计依赖事件顺序的程序的能力,例如使用赋值、修改数据或执行输入输出的程序。即使是 cons-stream 中的单个延迟也会造成极大混乱,如练习 3.51 和练习 3.52 所示。据目前所知,可变性与延迟求值在程序设计语言中难以相容,如何同时处理二者仍是一个活跃的研究领域。