それは純粋に機能的な方法で絶対に行うことができます。これを行う方法はいくつかありますが、最も簡単なのは、時間関数が時間だけでなく、次の時間測定を取得するために呼び出す必要がある関数も返すようにすることです。
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はこの種のトリックを使用し、モナドを使用して醜い部分を隠していると思います。