绝对可以通过纯粹的功能方式来完成。有几种方法可以做到这一点,但最简单的方法是让time函数不仅返回时间,还返回必须调用该函数才能获取下一次时间测量值。
在C#中,您可以这样实现:
// Exposes mutable time as immutable time (poorly, to illustrate by example)
// Although the insides are mutable, the exposed surface is immutable.
public class ClockStamp {
public static readonly ClockStamp ProgramStartTime = new ClockStamp();
public readonly DateTime Time;
private ClockStamp _next;
private ClockStamp() {
this.Time = DateTime.Now;
}
public ClockStamp NextMeasurement() {
if (this._next == null) this._next = new ClockStamp();
return this._next;
}
}
(请记住,这是一个简单但不切实际的示例。特别是列表节点由于是ProgramStartTime的根基而无法进行垃圾回收。)
这个“ ClockStamp”类的作用就像一个不可变的链表,但是实际上节点是按需生成的,因此它们可以包含“当前”时间。任何想要测量时间的函数都应该有一个“ clockStamp”参数,并且还必须在其结果中返回其最后的时间测量值(这样,调用者就不会看到旧的测量值),如下所示:
// Immutable. A result accompanied by a clockstamp
public struct TimeStampedValue<T> {
public readonly ClockStamp Time;
public readonly T Value;
public TimeStampedValue(ClockStamp time, T value) {
this.Time = time;
this.Value = value;
}
}
// Times an empty loop.
public static TimeStampedValue<TimeSpan> TimeALoop(ClockStamp lastMeasurement) {
var start = lastMeasurement.NextMeasurement();
for (var i = 0; i < 10000000; i++) {
}
var end = start.NextMeasurement();
var duration = end.Time - start.Time;
return new TimeStampedValue<TimeSpan>(end, duration);
}
public static void Main(String[] args) {
var clock = ClockStamp.ProgramStartTime;
var r = TimeALoop(clock);
var duration = r.Value; //the result
clock = r.Time; //must now use returned clock, to avoid seeing old measurements
}
当然,进出,进出,进出必须通过最后一次测量有点不方便。隐藏样板有很多方法,尤其是在语言设计级别。我认为Haskell使用了这种技巧,然后通过使用monad隐藏了丑陋的部分。