History variables: Difference between revisions
imported>Arakov |
|||
(24 intermediate revisions by 14 users not shown) | |||
Line 23: | Line 23: | ||
demonstrate how this might be implemented. |
demonstrate how this might be implemented. |
||
<br><br> |
<br><br> |
||
=={{header|68000 Assembly}}== |
|||
68000 Assembly doesn't support history variables natively. They can be implemented using a stack system. |
|||
The 68000 can use push/pop commands on any address register, not just the hardware stack pointer. |
|||
<syntaxhighlight lang="68000devpac">HistoryVar equ $100200 ;assume this is work ram. |
|||
LEA HistoryVar+4,A0 |
|||
MOVE.L #$11223344,D0 |
|||
MOVE.L D0,-(A0) ;store the 32-bit value #$11223344 starting at $100200 |
|||
MOVE.L #$55667788,D0 |
|||
MOVE.L D0,-(A0) ;store the 32-bit value #$5566788 at $1001FC |
|||
MOVE.L #$AABBCCDD,D0 |
|||
MOVE.L D0,-(A0) ;store the 32-bit value #$AABBCCDD at $1001F8 |
|||
;display the history |
|||
LEA HistoryVar,A0 |
|||
MOVE.L (A0),D0 ;load #$11223344 into D0 |
|||
MOVE.L (-4,A0),D1 ;load #$55667788 into D1 |
|||
MOVE.L (-8,A0),D2 ;load #$AABBCCDD into D2 |
|||
jsr PrintHex32 ;unimplemented routine that prints D0 as a 32-bit hex number |
|||
mov D1,D0 |
|||
jsr PrintHex32 |
|||
mov D2,D0 |
|||
jsr PrintHex32</syntaxhighlight> |
|||
Whether this constitutes a single variable at this point is debatable, because at a hardware level anything you write to a memory address permanently erases what was there, with no way to get it back unless you saved it somewhere else. So storing three values at the same memory location won't maintain the history. |
|||
=={{header|Ada}}== |
=={{header|Ada}}== |
||
Line 37: | Line 65: | ||
Specification: |
Specification: |
||
< |
<syntaxhighlight lang="ada">private with Ada.Containers.Indefinite_Vectors; |
||
generic |
generic |
||
type Item_Type (<>) is private; |
type Item_Type (<>) is private; |
||
Line 53: | Line 81: | ||
-- non-destructively search for old values |
-- non-destructively search for old values |
||
function Peek(V: Variable; Generation: Natural := 1) return Item_Type; |
function Peek(V: Variable; Generation: Natural := 1) return Item_Type; |
||
-- V.Peek(0) returns current value; V.Peek(1) the previous value, |
-- V.Peek(0) returns current value; V.Peek(1) the previous value, etc. |
||
-- when calling V.Peek(i), i must be in 0 .. V.Defined-1, else Constraint_Error is raised |
-- when calling V.Peek(i), i must be in 0 .. V.Defined-1, else Constraint_Error is raised |
||
-- destructively restore previous value |
-- destructively restore previous value |
||
procedure Undo(V: in out Variable); |
procedure Undo(V: in out Variable); |
||
-- old V.Peek(0) is forgotten, old V.Peek(i) is new V.Peek(i-1), |
-- old V.Peek(0) is forgotten, old V.Peek(i) is new V.Peek(i-1), etc. |
||
-- accordingly, V.Defined decrements by 1 |
-- accordingly, V.Defined decrements by 1 |
||
-- special case: if V.Defined=0 then V.Undo does not change V |
-- special case: if V.Defined=0 then V.Undo does not change V |
||
Line 70: | Line 98: | ||
History: Vectors.Vector; |
History: Vectors.Vector; |
||
end record; |
end record; |
||
end History_Variables;</ |
end History_Variables;</syntaxhighlight> |
||
The implementation of "History_Variables": |
The implementation of "History_Variables": |
||
< |
<syntaxhighlight lang="ada">package body History_Variables is |
||
-- set and get |
-- set and get |
||
Line 108: | Line 136: | ||
end Undo; |
end Undo; |
||
end History_Variables;</ |
end History_Variables;</syntaxhighlight> |
||
===Sample 1: The History of an Integer Variable=== |
===Sample 1: The History of an Integer Variable=== |
||
< |
<syntaxhighlight lang="ada">with Ada.Text_IO, History_Variables; |
||
procedure Test_History is |
procedure Test_History is |
||
Line 144: | Line 172: | ||
Ada.Text_IO.Put_Line(Integer'Image(Sum)); |
Ada.Text_IO.Put_Line(Integer'Image(Sum)); |
||
end Test_History;</ |
end Test_History;</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 153: | Line 181: | ||
===Sample 2: The History of a String=== |
===Sample 2: The History of a String=== |
||
< |
<syntaxhighlight lang="ada">with Ada.Text_IO, History_Variables; |
||
procedure Test_History is |
procedure Test_History is |
||
Line 184: | Line 212: | ||
Ada.Text_IO.Put_Line(Integer'Image(Sum)); |
Ada.Text_IO.Put_Line(Integer'Image(Sum)); |
||
end Test_History;</ |
end Test_History;</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 193: | Line 221: | ||
Algol W does not have history variables as standard, as with other languages here, we can add them using a simple linked list. |
Algol W does not have history variables as standard, as with other languages here, we can add them using a simple linked list. |
||
<br>As Algol W does not have generic types, a separate history variable type would be required for each type of variable. |
<br>As Algol W does not have generic types, a separate history variable type would be required for each type of variable. |
||
< |
<syntaxhighlight lang="algolw">begin |
||
% implements integer history variables % |
% implements integer history variables % |
||
% similar history types could be defined for other types of variable % |
% similar history types could be defined for other types of variable % |
||
Line 227: | Line 255: | ||
write( "Sum of the historic values: ", s ) |
write( "Sum of the historic values: ", s ) |
||
end |
end |
||
end.</ |
end.</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 233: | Line 261: | ||
Sum of the historic values: 6 |
Sum of the historic values: 6 |
||
</pre> |
</pre> |
||
=={{header|Arturo}}== |
|||
<syntaxhighlight lang="arturo">define :history [][ |
|||
init: [ |
|||
this\record: @[0] |
|||
] |
|||
] |
|||
assign: function [historyVar,newValue][ |
|||
historyVar\record: historyVar\record ++ newValue |
|||
] |
|||
alias.infix {'-->} 'assign |
|||
records: function [historyVar][ |
|||
historyVar\record |
|||
] |
|||
retrieve: function [historyVar][ |
|||
result: last historyVar\record |
|||
historyVar\record: chop historyVar\record |
|||
return result |
|||
] |
|||
current: function [historyVar][ |
|||
return last historyVar\record |
|||
] |
|||
do [ |
|||
h: to :history [] |
|||
print "Assigning three values: 1, 2, 3..." |
|||
h --> 1 |
|||
h --> 2 |
|||
h --> 3 |
|||
print "\nHistory (oldest values first):" |
|||
print [">" records h] |
|||
print ["\nCurrent value is:" current h] |
|||
print "\nRecalling the three values..." |
|||
loop 1..3 'x -> |
|||
print ["- Recalled:" retrieve h] |
|||
print "\nHistory:" |
|||
print [">" records h] |
|||
]</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Assigning three values: 1, 2, 3... |
|||
History (oldest values first): |
|||
> [0 1 2 3] |
|||
Current value is: 3 |
|||
Recalling the three values... |
|||
- Recalled: 3 |
|||
- Recalled: 2 |
|||
- Recalled: 1 |
|||
History: |
|||
> [0] </pre> |
|||
=={{header|AspectJ}}== |
=={{header|AspectJ}}== |
||
AspectJ implementation for Java 7. |
AspectJ implementation for Java 7. |
||
Type of the history variable (Java class): |
Type of the history variable (Java class): |
||
< |
<syntaxhighlight lang="java">public class HistoryVariable |
||
{ |
{ |
||
private Object value; |
private Object value; |
||
Line 265: | Line 357: | ||
{ |
{ |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
Aspect (HistoryHandling.aj): |
Aspect (HistoryHandling.aj): |
||
< |
<syntaxhighlight lang="java">import java.util.Deque; |
||
import java.util.HashMap; |
import java.util.HashMap; |
||
import java.util.LinkedList; |
import java.util.LinkedList; |
||
Line 309: | Line 401: | ||
private Map<HistoryVariable, Deque<Object>> history = new HashMap<>(); |
private Map<HistoryVariable, Deque<Object>> history = new HashMap<>(); |
||
}</ |
}</syntaxhighlight> |
||
Usage: |
Usage: |
||
< |
<syntaxhighlight lang="java">public final class Main |
||
{ |
{ |
||
public static void main(final String[] args) |
public static void main(final String[] args) |
||
Line 327: | Line 419: | ||
System.out.println(hv.toString()); |
System.out.println(hv.toString()); |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 341: | Line 433: | ||
AutoHotkey records a history of your keypresses, but not your variables. The closest you can come is with a class: |
AutoHotkey records a history of your keypresses, but not your variables. The closest you can come is with a class: |
||
{{works with|AutoHotkey 1.1}} |
{{works with|AutoHotkey 1.1}} |
||
< |
<syntaxhighlight lang="ahk">HV := new HistoryVariable |
||
HV.var := 1 |
HV.var := 1 |
||
HV.var := 2 |
HV.var := 2 |
||
Line 355: | Line 447: | ||
Return this[1] |
Return this[1] |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
=={{header|C sharp}}== |
=={{header|C sharp}}== |
||
< |
<syntaxhighlight lang="c sharp">using System; |
||
using System.Collections; |
using System.Collections; |
||
using System.Collections.Generic; |
using System.Collections.Generic; |
||
Line 426: | Line 518: | ||
} |
} |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>foobar <- foo <- 5 |
<pre>foobar <- foo <- 5 |
||
</pre> |
|||
=={{header|C++}}== |
|||
C++ does not have history variables, but they can easily by implemented with a generic class. |
|||
<syntaxhighlight lang="c++"> |
|||
#include <deque> |
|||
#include <iostream> |
|||
#include <string> |
|||
template <typename T> |
|||
class with_history { |
|||
public: |
|||
with_history(const T& element) { |
|||
history.push_front(element); |
|||
} |
|||
T get() { |
|||
return history.front(); |
|||
} |
|||
void set(const T& element) { |
|||
history.push_front(element); |
|||
} |
|||
std::deque<T> get_history() { |
|||
return std::deque<T>(history); |
|||
} |
|||
T rollback() { |
|||
if ( history.size() > 1 ) { |
|||
history.pop_front(); |
|||
} |
|||
return history.front(); |
|||
} |
|||
private: |
|||
std::deque<T> history; |
|||
}; |
|||
int main() { |
|||
with_history<double> number(1.2345); |
|||
std::cout << "Current value of number: " << number.get() << std::endl; |
|||
number.set(3.4567); |
|||
number.set(5.6789); |
|||
std::cout << "Historical values of number: "; |
|||
for ( const double& value : number.get_history() ) { |
|||
std::cout << value << " "; |
|||
} |
|||
std::cout << std::endl << std::endl; |
|||
with_history<std::string> word("Goodbye"); |
|||
word.set("Farewell"); |
|||
word.set("Hello"); |
|||
std::cout << word.get() << std::endl; |
|||
word.rollback(); |
|||
std::cout << word.get() << std::endl; |
|||
word.rollback(); |
|||
std::cout << word.get() << std::endl; |
|||
word.rollback(); |
|||
std::cout << word.get() << std::endl; |
|||
word.rollback(); |
|||
} |
|||
</syntaxhighlight> |
|||
{{ out }} |
|||
<pre> |
|||
Current value of number: 1.2345 |
|||
Historical values of number: 5.6789 3.4567 1.2345 |
|||
Hello |
|||
Farewell |
|||
Goodbye |
|||
Goodbye |
|||
</pre> |
</pre> |
||
Line 436: | Line 603: | ||
via a watcher function that can track changes on a variable. |
via a watcher function that can track changes on a variable. |
||
< |
<syntaxhighlight lang="clojure"> |
||
(def a (ref 0)) |
(def a (ref 0)) |
||
(def a-history (atom [@a])) ; define a history vector to act as a stack for changes on variable a |
(def a-history (atom [@a])) ; define a history vector to act as a stack for changes on variable a |
||
(add-watch a :hist (fn [key ref old new] (swap! a-history conj new))) |
(add-watch a :hist (fn [key ref old new] (swap! a-history conj new))) |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out|Sample Output}} |
{{out|Sample Output}} |
||
Line 463: | Line 630: | ||
In Lisp the list is a natural and simple data structure for representing variables with history. By adding some simple macros we can make a small DSL that hides the list and makes it look like a history variable is it's own special thing. |
In Lisp the list is a natural and simple data structure for representing variables with history. By adding some simple macros we can make a small DSL that hides the list and makes it look like a history variable is it's own special thing. |
||
< |
<syntaxhighlight lang="lisp">(defmacro make-hvar (value) |
||
`(list ,value)) |
`(list ,value)) |
||
Line 486: | Line 653: | ||
(undo-hvar v) |
(undo-hvar v) |
||
(format t "Restored value = ~a~%" (get-hvar v))) |
(format t "Restored value = ~a~%" (get-hvar v))) |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre>Initial value = 1 |
<pre>Initial value = 1 |
||
Line 496: | Line 663: | ||
D does not have history variables. The following implementation provides a generic HistoryVariable that protects the historical values by defining them as 'const'. |
D does not have history variables. The following implementation provides a generic HistoryVariable that protects the historical values by defining them as 'const'. |
||
< |
<syntaxhighlight lang="d">import std.stdio, std.array, std.string, std.datetime, std.traits; |
||
/// A history variable. |
/// A history variable. |
||
Line 558: | Line 725: | ||
s = "goodby"; |
s = "goodby"; |
||
writefln("%(%s\n%)", s.history); |
writefln("%(%s\n%)", s.history); |
||
}</ |
}</syntaxhighlight> |
||
{{out|Sample Output}} |
{{out|Sample Output}} |
||
<pre>2013-Jan-19 23:04:55.1660302; 1 |
<pre>2013-Jan-19 23:04:55.1660302; 1 |
||
Line 568: | Line 735: | ||
2013-Jan-19 23:04:55.1662596; goodby |
2013-Jan-19 23:04:55.1662596; goodby |
||
</pre> |
</pre> |
||
=={{header|Delphi}}== |
|||
{{libheader| System.SysUtils}} |
|||
<syntaxhighlight lang="delphi"> |
|||
program History_variables; |
|||
{$APPTYPE CONSOLE} |
|||
uses |
|||
System.SysUtils; |
|||
type |
|||
THistoryVarType = record |
|||
value: variant; |
|||
timestamp: TDateTime; |
|||
function ToString: string; |
|||
end; |
|||
THistoryVar = record |
|||
Fvalue: variant; |
|||
FHistory: TArray<THistoryVarType>; |
|||
private |
|||
procedure SetValue(const Value: Variant); |
|||
function GetTimestamp: TDateTime; |
|||
public |
|||
property History: TArray<THistoryVarType> read FHistory; |
|||
constructor Init(val: variant); |
|||
function Dump: string; |
|||
function Recall(steps: Integer): variant; overload; |
|||
function Recall(timestamp: TDateTime): variant; overload; |
|||
property Timestamp: TDateTime read GetTimestamp; |
|||
property Value: Variant read Fvalue write SetValue; |
|||
end; |
|||
{ THistoryVar } |
|||
function THistoryVar.Dump: string; |
|||
begin |
|||
Result := ''; |
|||
for var h in FHistory do |
|||
Result := Result + h.ToString + #10; |
|||
end; |
|||
function THistoryVar.GetTimestamp: TDateTime; |
|||
begin |
|||
if Length(FHistory) = 0 then |
|||
exit(0); |
|||
Result := FHistory[high(FHistory)].timestamp; |
|||
end; |
|||
constructor THistoryVar.Init(val: variant); |
|||
begin |
|||
SetLength(Fhistory, 0); |
|||
SetValue(val); |
|||
end; |
|||
function THistoryVar.Recall(timestamp: TDateTime): variant; |
|||
begin |
|||
for var h in FHistory do |
|||
begin |
|||
if h.timestamp = timestamp then |
|||
exit(h.value); |
|||
end; |
|||
result := Fvalue; |
|||
end; |
|||
function THistoryVar.Recall(steps: Integer): variant; |
|||
begin |
|||
if (steps <= 1) or (steps >= Length(FHistory)) then |
|||
exit(value); |
|||
result := FHistory[Length(FHistory) - steps - 1].value; |
|||
end; |
|||
procedure THistoryVar.SetValue(const Value: Variant); |
|||
begin |
|||
SetLength(FHistory, Length(FHistory) + 1); |
|||
FHistory[High(FHistory)].Value := Value; |
|||
FHistory[High(FHistory)].timestamp := now; |
|||
Fvalue := Value; |
|||
end; |
|||
{ THistoryVarType } |
|||
function THistoryVarType.ToString: string; |
|||
var |
|||
dt: string; |
|||
begin |
|||
DateTimeToString(dt, 'mm/dd/yyyy hh:nn:ss.zzz', timestamp); |
|||
Result := format('%s - %s', [dt, Value]); |
|||
end; |
|||
begin |
|||
var v := THistoryVar.Init(0); |
|||
v.Value := True; |
|||
Sleep(200); |
|||
if v.Value then |
|||
v.Value := 'OK'; |
|||
Sleep(100); |
|||
if v.Value = 'OK' then |
|||
v.Value := PI; |
|||
writeln('History of variable values:'#10); |
|||
Writeln(v.Dump); |
|||
Writeln('Recall 2 steps'); |
|||
Writeln(v.Recall(2)); |
|||
var t := v.History[3].timestamp; |
|||
Writeln(#10'Recall to timestamp ', datetimetostr(t)); |
|||
Writeln(v.Recall(t)); |
|||
readln; |
|||
end.</syntaxhighlight> |
|||
{{out}} |
|||
<pre>History of variable values: |
|||
02/28/2021 19:50:32.512 - 0 |
|||
02/28/2021 19:50:32.512 - True |
|||
02/28/2021 19:50:32.712 - OK |
|||
02/28/2021 19:50:32.813 - 3,14159265358979 |
|||
Recall 2 steps |
|||
True |
|||
Recall to timestamp 28/02/2021 19:50:32 |
|||
3,14159265358979</pre> |
|||
=={{header|EchoLisp}}== |
=={{header|EchoLisp}}== |
||
No native support. We implement an anonymous stack associated with the variable, and a few syntax rules to define the needed operations. |
No native support. We implement an anonymous stack associated with the variable, and a few syntax rules to define the needed operations. |
||
< |
<syntaxhighlight lang="lisp"> |
||
(define-syntax-rule (make-h-var name) (define name (stack (gensym)))) |
(define-syntax-rule (make-h-var name) (define name (stack (gensym)))) |
||
(define-syntax-rule (h-get name) (stack-top name)) |
(define-syntax-rule (h-get name) (stack-top name)) |
||
Line 593: | Line 889: | ||
(h-undo x) → 42 |
(h-undo x) → 42 |
||
(h-undo x) → ❌ error: no more values x |
(h-undo x) → ❌ error: no more values x |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Elena}}== |
=={{header|Elena}}== |
||
ELENA |
ELENA 6.x : |
||
< |
<syntaxhighlight lang="elena">import extensions; |
||
import system'collections; |
import system'collections; |
||
import system'routines; |
import system'routines; |
||
Line 606: | Line 903: | ||
object value; |
object value; |
||
Value |
|||
{ |
{ |
||
get() = value; |
get() = value; |
||
set(value) |
set(value) |
||
{ |
{ |
||
Line 616: | Line 913: | ||
previous.push(this value) |
previous.push(this value) |
||
}; |
}; |
||
this value := value |
this value := value |
||
} |
} |
||
Line 635: | Line 932: | ||
enumerator() => previous; |
enumerator() => previous; |
||
string toPrintable() => this value; |
|||
} |
} |
||
Line 647: | Line 944: | ||
console.printLine(o); |
console.printLine(o); |
||
o.forEach |
o.forEach(printingLn); |
||
o.undo().undo().undo(); |
o.undo().undo().undo(); |
||
console.printLine(o.Value ?? "nil") |
console.printLine(o.Value ?? "nil") |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 683: | Line 980: | ||
=={{header|Factor}}== |
=={{header|Factor}}== |
||
< |
<syntaxhighlight lang="factor">USING: accessors combinators formatting kernel models.history ; |
||
1 <history> { |
1 <history> { |
||
Line 694: | Line 991: | ||
[ go-back ] |
[ go-back ] |
||
[ value>> "Restored value: %u\n" printf ] |
[ value>> "Restored value: %u\n" printf ] |
||
} cleave</ |
} cleave</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 704: | Line 1,001: | ||
=={{header|Forth}}== |
=={{header|Forth}}== |
||
Forth does not have history variables, but it is trivial to define them. This one uses a linked list. The history variable is initialized to zero to avoid expensive sanity checks in ''H@''. ''H--'' undoes the assignment of a value and effectively returns a history variable to its former state. |
Forth does not have history variables, but it is trivial to define them. This one uses a linked list. The history variable is initialized to zero to avoid expensive sanity checks in ''H@''. ''H--'' undoes the assignment of a value and effectively returns a history variable to its former state. |
||
< |
<syntaxhighlight lang="forth">: history create here cell+ , 0 , -1 , ; |
||
: h@ @ @ ; |
: h@ @ @ ; |
||
: h! swap here >r , dup @ , r> swap ! ; |
: h! swap here >r , dup @ , r> swap ! ; |
||
: .history @ begin dup cell+ @ -1 <> while dup ? cell+ @ repeat drop ; |
: .history @ begin dup cell+ @ -1 <> while dup ? cell+ @ repeat drop ; |
||
: h-- dup @ cell+ @ dup -1 = if abort" End of history" then swap ! ;</ |
: h-- dup @ cell+ @ dup -1 = if abort" End of history" then swap ! ;</syntaxhighlight> |
||
A sample session: |
A sample session: |
||
Line 729: | Line 1,026: | ||
=={{header|Go}}== |
=={{header|Go}}== |
||
We're all in this for the extra points. Mallon and Takota seem happy with sequences, but time and timestamps were mentioned on LtU. Beyond a separate sequence for each history variable, timestamps enable multiple variables to be seen in a common temporal sequence. In Go, with it's attention to concurrency, this might be done with flexibility for proper handling of shared variables, and efficient handling of variables limited to a single thread. |
We're all in this for the extra points. Mallon and Takota seem happy with sequences, but time and timestamps were mentioned on LtU. Beyond a separate sequence for each history variable, timestamps enable multiple variables to be seen in a common temporal sequence. In Go, with it's attention to concurrency, this might be done with flexibility for proper handling of shared variables, and efficient handling of variables limited to a single thread. |
||
< |
<syntaxhighlight lang="go">package main |
||
import ( |
import ( |
||
Line 841: | Line 1,138: | ||
fmt.Println(rv) |
fmt.Println(rv) |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 859: | Line 1,156: | ||
There are no native Haskell history variables, but they are simple to implement. |
There are no native Haskell history variables, but they are simple to implement. |
||
< |
<syntaxhighlight lang="haskell">import Data.IORef |
||
newtype HVar a = HVar (IORef [a]) |
newtype HVar a = HVar (IORef [a]) |
||
Line 890: | Line 1,187: | ||
undoHVar var |
undoHVar var |
||
undoHVar var |
undoHVar var |
||
undoHVar var</ |
undoHVar var</syntaxhighlight> |
||
=={{header|J}}== |
=={{header|J}}== |
||
Line 896: | Line 1,193: | ||
J does not natively support "history variables", but the functionality is easy to add: |
J does not natively support "history variables", but the functionality is easy to add: |
||
< |
<syntaxhighlight lang="j">varref_hist_=:'VAR','_hist_',~] |
||
set_hist_=:4 :0 |
set_hist_=:4 :0 |
||
V=.varref x |
V=.varref x |
||
Line 907: | Line 1,204: | ||
) |
) |
||
length_hist_=: #@getall |
length_hist_=: #@getall |
||
get_hist_=: _1 {:: getall</ |
get_hist_=: _1 {:: getall</syntaxhighlight> |
||
Example use: |
Example use: |
||
< |
<syntaxhighlight lang="j"> 'x' set_hist_ 9 |
||
9 |
9 |
||
'x' set_hist_ 10 |
'x' set_hist_ 10 |
||
Line 924: | Line 1,221: | ||
┌─┬──┬──┐ |
┌─┬──┬──┐ |
||
│9│10│11│ |
│9│10│11│ |
||
└─┴──┴──┘</ |
└─┴──┴──┘</syntaxhighlight> |
||
Note that each value is contained in a box, so different values do not need to be type compatible with each other. If this is considered a defect then assertions could be added to enforce type compatibility across assignments. |
Note that each value is contained in a box, so different values do not need to be type compatible with each other. If this is considered a defect then assertions could be added to enforce type compatibility across assignments. |
||
Line 935: | Line 1,232: | ||
Java does not support history variables, but they are easy to implement using the lists that come with Java's Collections framework. |
Java does not support history variables, but they are easy to implement using the lists that come with Java's Collections framework. |
||
=== |
===Java Integer with History=== |
||
This implementation does not allow the History Variable to be "empty". It can be assigned one or multiple "nulls", but it can never have an undefined value (such as being created and not initialized). |
This implementation does not allow the History Variable to be "empty". It can be assigned one or multiple "nulls", but it can never have an undefined value (such as being created and not initialized). |
||
< |
<syntaxhighlight lang="java">import java.util.Collections; |
||
import java.util.LinkedList; |
import java.util.LinkedList; |
||
import java.util.List; |
import java.util.List; |
||
Line 1,010: | Line 1,307: | ||
} |
} |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
Test Program: |
Test Program: |
||
< |
<syntaxhighlight lang="java">public class TestIntegerWithHistory { |
||
public static void main(String[] args) { |
public static void main(String[] args) { |
||
Line 1,036: | Line 1,333: | ||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 1,052: | Line 1,349: | ||
</pre> |
</pre> |
||
=== |
===Java History for "any class" using Generics=== |
||
This implementation does not allow the History Variable to be "empty". It can be assigned one or multiple "nulls", but it can never have an undefined value (such as being created and not initialized). |
This implementation does not allow the History Variable to be "empty". It can be assigned one or multiple "nulls", but it can never have an undefined value (such as being created and not initialized). |
||
<lang java></lang> |
|||
Test Program using a "String with History": |
Test Program using a "String with History": |
||
< |
<syntaxhighlight lang="java">import java.util.Collections; |
||
import java.util.LinkedList; |
import java.util.LinkedList; |
||
import java.util.List; |
import java.util.List; |
||
Line 1,132: | Line 1,428: | ||
} |
} |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 1,147: | Line 1,443: | ||
</pre> |
</pre> |
||
=={{header|Julia}}== |
=={{header|Julia}}== |
||
Julia currently does not support overloading the assignment "=" operator. |
Julia currently does not support overloading the assignment "=" operator. |
||
< |
<syntaxhighlight lang="julia">mutable struct Historied |
||
num::Number |
num::Number |
||
history::Vector{Number} |
history::Vector{Number} |
||
Line 1,164: | Line 1,461: | ||
println("Past history of variable x: $(x.history). Current value is $(x.num)") |
println("Past history of variable x: $(x.history). Current value is $(x.num)") |
||
</ |
</syntaxhighlight>{{output}}<pre>Past history of variable x: Number[1, 3, 5]. Current value is 4</pre> |
||
=={{header|Kotlin}}== |
=={{header|Kotlin}}== |
||
< |
<syntaxhighlight lang="scala">// version 1.1.4 |
||
class HistoryVariable<T>(initialValue: T) { |
class HistoryVariable<T>(initialValue: T) { |
||
Line 1,194: | Line 1,491: | ||
v.showHistory() |
v.showHistory() |
||
println("\nCurrentvalue is ${v.currentValue}") |
println("\nCurrentvalue is ${v.currentValue}") |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 1,205: | Line 1,502: | ||
Currentvalue is 3 |
Currentvalue is 3 |
||
</pre> |
</pre> |
||
=={{header|Lua}}== |
|||
Lua does not natively support history variables. However, as with other languages here, something roughly equivalent could be implemented with a "stack wrapper". The only complicated issue might be how to treat nil's (null values). The implementation below allows initial nil's, and allows undo()'s back to nil, but does <i>not</i> consider nil as part of the "history". |
|||
===Via metatable=== |
|||
<syntaxhighlight lang="lua">-- History variables in Lua 6/12/2020 db |
|||
local HistoryVariable = { |
|||
new = function(self) |
|||
return setmetatable({history={}},self) |
|||
end, |
|||
get = function(self) |
|||
return self.history[#self.history] |
|||
end, |
|||
set = function(self, value) |
|||
self.history[#self.history+1] = value |
|||
end, |
|||
undo = function(self) |
|||
self.history[#self.history] = nil |
|||
end, |
|||
} |
|||
HistoryVariable.__index = HistoryVariable |
|||
local hv = HistoryVariable:new() |
|||
print("defined:", hv) |
|||
print("value is:", hv:get()) |
|||
-- |
|||
hv:set(1); print("set() to:", hv:get()) |
|||
hv:set(2); print("set() to:", hv:get()) |
|||
hv:set(3); print("set() to:", hv:get()) |
|||
-- |
|||
print("history:", table.concat(hv.history,",")) |
|||
-- |
|||
hv:undo(); print("undo() to:", hv:get()) |
|||
hv:undo(); print("undo() to:", hv:get()) |
|||
hv:undo(); print("undo() to:", hv:get())</syntaxhighlight> |
|||
{{out}} |
|||
<pre>defined: table: 0000000000939240 |
|||
value is: nil |
|||
set() to: 1 |
|||
set() to: 2 |
|||
set() to: 3 |
|||
history: 1,2,3 |
|||
undo() to: 2 |
|||
undo() to: 1 |
|||
undo() to: nil</pre> |
|||
===Via closure=== |
|||
If a desire is to keep history private and immutable, then.. |
|||
<syntaxhighlight lang="lua">-- History variables in Lua 6/12/2020 db |
|||
local function HistoryVariable() |
|||
local history = {} |
|||
return { |
|||
get = function() |
|||
return history[#history] |
|||
end, |
|||
set = function(value) |
|||
history[#history+1] = value |
|||
end, |
|||
undo = function() |
|||
history[#history] = nil |
|||
end, |
|||
getHistory = function() |
|||
local clone = {} |
|||
for k,v in pairs(history) do clone[k]=v end |
|||
return clone |
|||
end |
|||
} |
|||
end |
|||
local hv = HistoryVariable() |
|||
print("defined:", hv) |
|||
print("value is:", hv.get()) |
|||
-- |
|||
hv.set(1); print("set() to:", hv.get()) |
|||
hv.set(2); print("set() to:", hv.get()) |
|||
hv.set(3); print("set() to:", hv.get()) |
|||
-- |
|||
print("history:", table.concat(hv.getHistory(),",")) |
|||
-- |
|||
hv.undo(); print("undo() to:", hv.get()) |
|||
hv.undo(); print("undo() to:", hv.get()) |
|||
hv.undo(); print("undo() to:", hv.get())</syntaxhighlight> |
|||
{{out}} |
|||
<pre>defined: table: 0000000000a59500 |
|||
value is: nil |
|||
set() to: 1 |
|||
set() to: 2 |
|||
set() to: 3 |
|||
history: 1,2,3 |
|||
undo() to: 2 |
|||
undo() to: 1 |
|||
undo() to: nil</pre> |
|||
=={{header|M2000 Interpreter}}== |
=={{header|M2000 Interpreter}}== |
||
Line 1,210: | Line 1,597: | ||
First we see how we work with Stack (each module/function see a "current stack", we can make pointers to new stacks, but we can't get pointer of current stack). There are some statement no demonstrate here, such as Shift and ShiftBack (move top to a place, and move back to top), and Over (make new items by copying items). |
First we see how we work with Stack (each module/function see a "current stack", we can make pointers to new stacks, but we can't get pointer of current stack). There are some statement no demonstrate here, such as Shift and ShiftBack (move top to a place, and move back to top), and Over (make new items by copying items). |
||
<syntaxhighlight lang="m2000 interpreter"> |
|||
<lang M2000 Interpreter> |
|||
Flush ' empty curtrent stack |
Flush ' empty curtrent stack |
||
\\ a is a pointer to a new stack object |
\\ a is a pointer to a new stack object |
||
Line 1,249: | Line 1,636: | ||
b=stackitem(z, 2) |
b=stackitem(z, 2) |
||
Print stackitem(b, 4)=1000 |
Print stackitem(b, 4)=1000 |
||
</syntaxhighlight> |
|||
</lang> |
|||
So now lets see the code: |
So now lets see the code: |
||
<syntaxhighlight lang="m2000 interpreter"> |
|||
<lang M2000 Interpreter> |
|||
Module CheckHistoryVariables { |
Module CheckHistoryVariables { |
||
Class History { |
Class History { |
||
Line 1,433: | Line 1,820: | ||
} |
} |
||
CheckStringHistoryVariables |
CheckStringHistoryVariables |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Nim}}== |
|||
Nim doesn’t provide history variables, but it is easy to implement them. |
|||
As Nim is statically typed, we define history variable type in a generic way. |
|||
<syntaxhighlight lang="nim">type HistoryVar[T] = object |
|||
hist: seq[T] |
|||
func initHistoryVar[T](value: T = T.default): HistoryVar[T] = |
|||
## Initialize a history variable with given value. |
|||
result.hist.add value |
|||
func set(h: var HistoryVar; value: h.T) = |
|||
## Set the history variable to given value. |
|||
h.hist.add value |
|||
func get(h: HistoryVar): h.T = |
|||
## Return the current value of history variable. |
|||
h.hist[^1] |
|||
proc showHistory(h: HistoryVar) = |
|||
## Show the history starting from oldest values. |
|||
for i, value in h.hist: |
|||
echo i, ": ", value |
|||
func pop(h: var HistoryVar): h.T = |
|||
## Pop the current value and return it. |
|||
if h.hist.len > 1: h.hist.pop() else: h.hist[0] |
|||
when isMainModule: |
|||
var h = initHistoryVar[int]() # Initialized to 0. |
|||
echo "Assigning three values: 1, 2, 3" |
|||
h.set(1) # 0, 1 |
|||
h.set(2) # 0, 1, 2 |
|||
h.set(3) # 0, 1, 2, 3 |
|||
echo "History (oldest values first):" |
|||
h.showHistory() |
|||
echo "Current value is ", h.get() |
|||
echo "Recall the three values:" |
|||
echo h.pop() # -> 3, last value removed. |
|||
echo h.pop() # -> 2, last value removed. |
|||
echo h.pop() # -> 1, last value removed. |
|||
echo "History (note that initial value can never be removed):" |
|||
h.showHistory()</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Assigning three values: 1, 2, 3 |
|||
History (oldest values first): |
|||
0: 0 |
|||
1: 1 |
|||
2: 2 |
|||
3: 3 |
|||
Current value is 3 |
|||
Recall the three values: |
|||
3 |
|||
2 |
|||
1 |
|||
History (note that initial value can never be removed): |
|||
0: 0</pre> |
|||
=={{header|Oberon-2}}== |
=={{header|Oberon-2}}== |
||
< |
<syntaxhighlight lang="oberon2"> |
||
MODULE HVar; |
MODULE HVar; |
||
IMPORT Out, Conv; |
IMPORT Out, Conv; |
||
Line 1,616: | Line 2,069: | ||
ShowVal(history.Undo()); |
ShowVal(history.Undo()); |
||
END HVar. |
END HVar. |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|OCaml}}== |
=={{header|OCaml}}== |
||
The easiest solution is to use the Stack module coming with OCaml's standard library: |
The easiest solution is to use the Stack module coming with OCaml's standard library: |
||
< |
<syntaxhighlight lang="ocaml"> |
||
open Stack |
open Stack |
||
(* The following line is only for convenience when typing code *) |
(* The following line is only for convenience when typing code *) |
||
Line 1,637: | Line 2,090: | ||
hs |> H.pop |> Printf.printf "%d\n"; |
hs |> H.pop |> Printf.printf "%d\n"; |
||
hs |> H.pop |> Printf.printf "%d\n" |
hs |> H.pop |> Printf.printf "%d\n" |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
Line 1,651: | Line 2,104: | ||
=={{header|OxygenBasic}}== |
=={{header|OxygenBasic}}== |
||
Simple history class for fixed length types that do not contain volatile pointer members. |
Simple history class for fixed length types that do not contain volatile pointer members. |
||
< |
<syntaxhighlight lang="oxygenbasic"> |
||
'============ |
'============ |
||
class History |
class History |
||
Line 1,710: | Line 2,163: | ||
del hv |
del hv |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|PARI/GP}}== |
=={{header|PARI/GP}}== |
||
< |
<syntaxhighlight lang="parigp">default(histsize, 1000) \\ or some other positive number to suit |
||
1+7 |
1+7 |
||
sin(Pi) |
sin(Pi) |
||
Line 1,722: | Line 2,175: | ||
\a2 |
\a2 |
||
\a3 |
\a3 |
||
[%1, %2, %3] \\ or any other command using these values</ |
[%1, %2, %3] \\ or any other command using these values</syntaxhighlight> |
||
=={{header|Peloton}}== |
=={{header|Peloton}}== |
||
< |
<syntaxhighlight lang="sgml">Turn history on <@ DEFHST>__on</@> |
||
Notify Protium we are interested in the variable mv |
Notify Protium we are interested in the variable mv |
||
<@ DEFHST>mv</@> |
<@ DEFHST>mv</@> |
||
Line 1,735: | Line 2,188: | ||
Undo once: <@ ACTUNDVAR>mv</@><@ SAYVAR>mv</@> |
Undo once: <@ ACTUNDVAR>mv</@><@ SAYVAR>mv</@> |
||
Undo twice: <@ ACTUNDVAR>mv</@><@ SAYVAR>mv</@> |
Undo twice: <@ ACTUNDVAR>mv</@><@ SAYVAR>mv</@> |
||
Turn history off <@ DEFHST>__off</@></ |
Turn history off <@ DEFHST>__off</@></syntaxhighlight> |
||
Same code, Simplified Chinese dialect |
Same code, Simplified Chinese dialect |
||
< |
<syntaxhighlight lang="sgml">Turn history on <# 定义变量史>__on</#> |
||
Notify Protium we are interested in the variable mv |
Notify Protium we are interested in the variable mv |
||
<# 定义变量史>mv</#> |
<# 定义变量史>mv</#> |
||
Line 1,748: | Line 2,201: | ||
Undo once: <# 运行撤消变量>mv</#><# 显示变量>mv</#> |
Undo once: <# 运行撤消变量>mv</#><# 显示变量>mv</#> |
||
Undo twice: <# 运行撤消变量>mv</#><# 显示变量>mv</#> |
Undo twice: <# 运行撤消变量>mv</#><# 显示变量>mv</#> |
||
Turn history off <# 定义变量史>__off</#> </ |
Turn history off <# 定义变量史>__off</#> </syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 1,766: | Line 2,219: | ||
=={{header|Perl}}== |
=={{header|Perl}}== |
||
Implemented via tie (and what's the usefulness of this?) |
Implemented via tie (and what's the usefulness of this?) |
||
< |
<syntaxhighlight lang="perl">package History; |
||
sub TIESCALAR { |
sub TIESCALAR { |
||
Line 1,805: | Line 2,258: | ||
History::off($x); |
History::off($x); |
||
print "\$x is: $x\n";</ |
print "\$x is: $x\n";</syntaxhighlight> |
||
{{out}}<lang>History: a b c d |
{{out}}<syntaxhighlight lang="text">History: a b c d |
||
undo 1, current value: c |
undo 1, current value: c |
||
undo 2, current value: b |
undo 2, current value: b |
||
undo 3, current value: a |
undo 3, current value: a |
||
$x is: a</ |
$x is: a</syntaxhighlight> |
||
=={{header|Perl 6}}== |
|||
{{works with|Rakudo|2018.03}} |
|||
<lang perl6>class HistoryVar { |
|||
has @.history; |
|||
has $!var handles <Str gist FETCH Numeric>; |
|||
method STORE($val) is rw { |
|||
push @.history, [now, $!var]; |
|||
$!var = $val; |
|||
} |
|||
} |
|||
my \foo = HistoryVar.new; |
|||
foo = 1; |
|||
foo = 2; |
|||
foo += 3; |
|||
foo = 42; |
|||
.say for foo.history; |
|||
say "Current value: {foo}";</lang> |
|||
{{out}} |
|||
<pre>[Instant:1523396079.685629 (Any)] |
|||
[Instant:1523396079.686844 1] |
|||
[Instant:1523396079.687130 2] |
|||
[Instant:1523396079.687302 5] |
|||
Current value: 42 |
|||
</pre> |
|||
=={{header|Phix}}== |
=={{header|Phix}}== |
||
No native support, but trivial to implement.<br> |
No native support, but trivial to implement.<br> |
||
If you only need the history for a single variable, you can just do this: |
If you only need the history for a single variable, you can just do this, though it does not work under pwa/p2js: |
||
<!--<syntaxhighlight lang="phix">--> |
|||
<lang Phix>sequence history = {} |
|||
<span style="color: #008080;">without</span> <span style="color: #008080;">js</span> <span style="color: #000080;font-style:italic;">-- (desktop/Phix only)</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">history</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span> |
|||
type hvt(object o) |
|||
history = append(history,o) |
|||
<span style="color: #008080;">type</span> <span style="color: #000000;">hvt</span><span style="color: #0000FF;">(</span><span style="color: #004080;">object</span> <span style="color: #000000;">o</span><span style="color: #0000FF;">)</span> |
|||
return true |
|||
<span style="color: #000000;">history</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">history</span><span style="color: #0000FF;">,</span><span style="color: #000000;">o</span><span style="color: #0000FF;">)</span> |
|||
end type |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">true</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">type</span> |
|||
hvt test = 1 |
|||
<span style="color: #000000;">hvt</span> <span style="color: #000000;">test</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span> |
|||
test = 3 |
|||
<span style="color: #000000;">test</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">2</span> |
|||
?{"current",test} |
|||
<span style="color: #000000;">test</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">3</span> |
|||
?{"history",history}</lang> |
|||
<span style="color: #0000FF;">?{</span><span style="color: #008000;">"current"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">test</span><span style="color: #0000FF;">}</span> |
|||
<span style="color: #0000FF;">?{</span><span style="color: #008000;">"history"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">history</span><span style="color: #0000FF;">}</span> |
|||
<!--</syntaxhighlight>--> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 1,859: | Line 2,288: | ||
{"history",{1,2,3}} |
{"history",{1,2,3}} |
||
</pre> |
</pre> |
||
Multiple history variables would require that routines must be invoked to create, update, and inspect them.<br> |
Multiple history variables would require that routines must be invoked to create, update, and inspect them, and would be pwa/p2js compatible.<br> |
||
Writing this as a separate reusable component (but omitting destroy/freelist handling for simplicity): |
Writing this as a separate reusable component (but omitting destroy/freelist handling for simplicity): |
||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
<lang Phix>-- history.e |
|||
<span style="color: #000080;font-style:italic;">-- history.e</span> |
|||
sequence histories = {} |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">histories</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span> |
|||
global function new_history_id(object v) |
|||
<span style="color: #008080;">global</span> <span style="color: #008080;">function</span> <span style="color: #000000;">new_history_id</span><span style="color: #0000FF;">(</span><span style="color: #004080;">object</span> <span style="color: #000000;">v</span><span style="color: #0000FF;">)</span> |
|||
histories = append(histories,{v}) |
|||
<span style="color: #000000;">histories</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">histories</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">v</span><span style="color: #0000FF;">})</span> |
|||
return length(histories) |
|||
<span style="color: #008080;">return</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">histories</span><span style="color: #0000FF;">)</span> |
|||
end function |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
global procedure set_hv(integer hv, object v) |
|||
<span style="color: #008080;">global</span> <span style="color: #008080;">procedure</span> <span style="color: #000000;">set_hv</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">hv</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">object</span> <span style="color: #000000;">v</span><span style="color: #0000FF;">)</span> |
|||
histories[hv] = append(histories[hv],v) |
|||
<span style="color: #000000;">histories</span><span style="color: #0000FF;">[</span><span style="color: #000000;">hv</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">histories</span><span style="color: #0000FF;">[</span><span style="color: #000000;">hv</span><span style="color: #0000FF;">],</span><span style="color: #000000;">v</span><span style="color: #0000FF;">)</span> |
|||
end procedure |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
global function get_hv(integer hv) |
|||
<span style="color: #008080;">global</span> <span style="color: #008080;">function</span> <span style="color: #000000;">get_hv</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">hv</span><span style="color: #0000FF;">)</span> |
|||
return histories[hv][$] |
|||
<span style="color: #008080;">return</span> <span style="color: #000000;">histories</span><span style="color: #0000FF;">[</span><span style="color: #000000;">hv</span><span style="color: #0000FF;">][$]</span> |
|||
end function |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
global function get_hv_full_history(integer hv) |
|||
<span style="color: #008080;">global</span> <span style="color: #008080;">function</span> <span style="color: #000000;">get_hv_full_history</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">hv</span><span style="color: #0000FF;">)</span> |
|||
return histories[hv] |
|||
<span style="color: #008080;">return</span> <span style="color: #000000;">histories</span><span style="color: #0000FF;">[</span><span style="color: #000000;">hv</span><span style="color: #0000FF;">]</span> |
|||
end function</lang> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<!--</syntaxhighlight>--> |
|||
And use it like this |
And use it like this |
||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
<lang Phix>include history.e |
|||
<span style="color: #008080;">include</span> <span style="color: #000000;">history</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span> |
|||
constant test2 = new_history_id(1) |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">test2</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">new_history_id</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
set_hv(test2, 2) |
|||
<span style="color: #000000;">set_hv</span><span style="color: #0000FF;">(</span><span style="color: #000000;">test2</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">2</span><span style="color: #0000FF;">)</span> |
|||
set_hv(test2, 3) |
|||
<span style="color: #000000;">set_hv</span><span style="color: #0000FF;">(</span><span style="color: #000000;">test2</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">3</span><span style="color: #0000FF;">)</span> |
|||
?{"current",get_hv(test2)} |
|||
<span style="color: #0000FF;">?{</span><span style="color: #008000;">"current"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">get_hv</span><span style="color: #0000FF;">(</span><span style="color: #000000;">test2</span><span style="color: #0000FF;">)}</span> |
|||
?{"history",get_hv_full_history(test2)}</lang> |
|||
<span style="color: #0000FF;">?{</span><span style="color: #008000;">"history"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">get_hv_full_history</span><span style="color: #0000FF;">(</span><span style="color: #000000;">test2</span><span style="color: #0000FF;">)}</span> |
|||
<!--</syntaxhighlight>--> |
|||
Same output. Of course test2 does not ''have'' to be a constant, but it may help. |
Same output. Of course test2 does not ''have'' to be a constant, but it may help. |
||
=={{header|PicoLisp}}== |
=={{header|PicoLisp}}== |
||
< |
<syntaxhighlight lang="picolisp">(de setH ("Var" Val) |
||
(when (val "Var") |
(when (val "Var") |
||
(with "Var" |
(with "Var" |
||
Line 1,898: | Line 2,331: | ||
(de restoreH ("Var") |
(de restoreH ("Var") |
||
(set "Var" (pop (prop "Var" 'history))) )</ |
(set "Var" (pop (prop "Var" 'history))) )</syntaxhighlight> |
||
Test: |
Test: |
||
<pre>: (setH 'A "Hello world") |
<pre>: (setH 'A "Hello world") |
||
Line 1,928: | Line 2,361: | ||
=={{header|PL/I}}== |
=={{header|PL/I}}== |
||
<syntaxhighlight lang="pl/i"> |
|||
<lang PL/I> |
|||
declare t float controlled; |
declare t float controlled; |
||
Line 1,938: | Line 2,371: | ||
put (t); free t; |
put (t); free t; |
||
end; |
end; |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|PureBasic}}== |
=={{header|PureBasic}}== |
||
< |
<syntaxhighlight lang="purebasic">; integer history variable |
||
Structure historyint |
Structure historyint |
||
Line 1,990: | Line 2,423: | ||
Debug "undo, x = "+Str(x\value()) |
Debug "undo, x = "+Str(x\value()) |
||
Next |
Next |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
Line 2,009: | Line 2,442: | ||
=={{header|Python}}== |
=={{header|Python}}== |
||
< |
<syntaxhighlight lang="python">import sys |
||
HIST = {} |
HIST = {} |
||
Line 2,042: | Line 2,475: | ||
sys.settrace(trace) |
sys.settrace(trace) |
||
main()</ |
main()</syntaxhighlight> |
||
{{out}}<lang>c: 4 -> undo x3 -> 1 |
{{out}}<syntaxhighlight lang="text">c: 4 -> undo x3 -> 1 |
||
HIST: {'a': [10, 20], 'i': [0, 1, 2, 3, 4], 'c': [0, 1], 'name': ['c']}</ |
HIST: {'a': [10, 20], 'i': [0, 1, 2, 3, 4], 'c': [0, 1], 'name': ['c']}</syntaxhighlight> |
||
=={{header|Quackery}}== |
|||
Quackery does not have variables, it has ancillary stacks that can serve, amongst their uses, as variables and as history variables. Given an ancillary stack <code>x</code>, the following Quackery words are relevant. |
|||
*<code>x put</code> moves an item from the data stack to the ancillary stack <code>x</code>. |
|||
*<code>x take</code> moves an item from the ancillary stack <code>x</code> to the data stack. |
|||
*<code>x share</code> copies the top of the ancillary stack <code>x</code> to the data stack. |
|||
*<code>x replace</code> replaces the top item on the ancillary stack <code>x</code> with the item on top of the data stack. |
|||
*<code>x behead drop</code> leaves a copy of the ancillary stack <code>x</code> on the data stack as a nest (dynamic array). |
|||
The requirements of this task demonstrated as a dialogue in the Quackery shell. |
|||
<pre>/O> [ stack ] is x |
|||
... 3 x put |
|||
... 4 x put |
|||
... 5 x put |
|||
... x behead drop echo cr |
|||
... x take echo cr |
|||
... x take echo cr |
|||
... x take echo cr |
|||
... |
|||
[ 3 4 5 ] |
|||
5 |
|||
4 |
|||
3 |
|||
</pre> |
|||
=={{header|Racket}}== |
=={{header|Racket}}== |
||
Line 2,050: | Line 2,513: | ||
Racket does not come with history variables, but they can be provided as a library as follows: |
Racket does not come with history variables, but they can be provided as a library as follows: |
||
< |
<syntaxhighlight lang="racket"> |
||
#lang racket |
#lang racket |
||
Line 2,080: | Line 2,543: | ||
(hvar-undo! hv) |
(hvar-undo! hv) |
||
(check-equal? (hvar-current hv) 0) |
(check-equal? (hvar-current hv) 0) |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Raku}}== |
|||
(formerly Perl 6) |
|||
{{works with|Rakudo|2018.03}} |
|||
<syntaxhighlight lang="raku" line>class HistoryVar { |
|||
has @.history; |
|||
has $!var handles <Str gist FETCH Numeric>; |
|||
method STORE($val) is rw { |
|||
push @.history, [now, $!var]; |
|||
$!var = $val; |
|||
} |
|||
} |
|||
my \foo = HistoryVar.new; |
|||
foo = 1; |
|||
foo = 2; |
|||
foo += 3; |
|||
foo = 42; |
|||
.say for foo.history; |
|||
say "Current value: {foo}";</syntaxhighlight> |
|||
{{out}} |
|||
<pre>[Instant:1523396079.685629 (Any)] |
|||
[Instant:1523396079.686844 1] |
|||
[Instant:1523396079.687130 2] |
|||
[Instant:1523396079.687302 5] |
|||
Current value: 42 |
|||
</pre> |
|||
=={{header|REXX}}== |
=={{header|REXX}}== |
||
Line 2,087: | Line 2,579: | ||
<br><br>The history list part of the  ' '''varSet''' subroutine could be separated into its |
<br><br>The history list part of the  ' '''varSet''' subroutine could be separated into its |
||
<br>own if you wanted to keep the subroutine's function pure. |
<br>own if you wanted to keep the subroutine's function pure. |
||
< |
<syntaxhighlight lang="rexx">/*REXX program demonstrates a method to track history of assignments to a REXX variable.*/ |
||
varSet!.=0 /*initialize the all of the VARSET!'s. */ |
varSet!.=0 /*initialize the all of the VARSET!'s. */ |
||
call varSet 'fluid',min(0,-5/2,-1) ; say 'fluid=' fluid |
call varSet 'fluid',min(0,-5/2,-1) ; say 'fluid=' fluid |
||
Line 2,108: | Line 2,600: | ||
say 'history entry' ?j "for var" ?z":" varSet!.?J.?x |
say 'history entry' ?j "for var" ?z":" varSet!.?J.?x |
||
end /*?j*/ |
end /*?j*/ |
||
return ?j-1 /*return the number of assignments. */</ |
return ?j-1 /*return the number of assignments. */</syntaxhighlight> |
||
'''output''' |
'''output''' |
||
<pre> |
<pre> |
||
Line 2,122: | Line 2,614: | ||
===Version 2=== |
===Version 2=== |
||
< |
<syntaxhighlight lang="rexx"> |
||
/* REXX *************************************************************** |
/* REXX *************************************************************** |
||
* Demonstrate how the history of assignments can be kept and shown |
* Demonstrate how the history of assignments can be kept and shown |
||
Line 2,163: | Line 2,655: | ||
end |
end |
||
Return varset.0.varu /*return the number of assignments. */ |
Return varset.0.varu /*return the number of assignments. */ |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Ruby}}== |
=={{header|Ruby}}== |
||
This uses trace_var, which only works for global variables: |
This uses trace_var, which only works for global variables: |
||
< |
<syntaxhighlight lang="ruby">foo_hist = [] |
||
trace_var(:$foo){|v| foo_hist.unshift(v)} |
trace_var(:$foo){|v| foo_hist.unshift(v)} |
||
Line 2,175: | Line 2,667: | ||
p foo_hist # => ["banana", "pear", "apple"] |
p foo_hist # => ["banana", "pear", "apple"] |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Rust}}== |
=={{header|Rust}}== |
||
< |
<syntaxhighlight lang="rust">#[derive(Clone, Debug)] |
||
struct HVar<T> { |
struct HVar<T> { |
||
history: Vec<T>, |
history: Vec<T>, |
||
Line 2,220: | Line 2,712: | ||
println!("{:?}", var.revert()); |
println!("{:?}", var.revert()); |
||
println!("{:?}", var.get()); |
println!("{:?}", var.get()); |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>([0, 1], 2) |
<pre>([0, 1], 2) |
||
Line 2,230: | Line 2,722: | ||
=={{header|Scala}}== |
=={{header|Scala}}== |
||
Scala doesn't have a native support for history variables, but it's quite easy to implement them. The following class uses same conventions as ML's mutable reference cells. (i.e. <code>!</code> as accessor, and <code>:=</code> as mutator.) |
Scala doesn't have a native support for history variables, but it's quite easy to implement them. The following class uses same conventions as ML's mutable reference cells. (i.e. <code>!</code> as accessor, and <code>:=</code> as mutator.) |
||
< |
<syntaxhighlight lang="scala">class HVar[A](initialValue: A) extends Proxy { |
||
override def self = !this |
override def self = !this |
||
override def toString = "HVar(" + !this + ")" |
override def toString = "HVar(" + !this + ")" |
||
Line 2,249: | Line 2,741: | ||
v |
v |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
Usage: |
Usage: |
||
< |
<syntaxhighlight lang="scala">scala> val h = new HVar(3) |
||
h: HVar[Int] = HVar(3) |
h: HVar[Int] = HVar(3) |
||
Line 2,271: | Line 2,763: | ||
scala> h.undo |
scala> h.undo |
||
res36: Int = 3</ |
res36: Int = 3</syntaxhighlight> |
||
=={{header|SenseTalk}}== |
|||
SenseTalk does not have native support for history variables, but here is a simple object that can be instantiated to provide a history: |
|||
<syntaxhighlight lang="sensetalk">// HistoryVariable.script |
|||
properties |
|||
history: [], -- a list of all historical values |
|||
asTextFormat:"[[the last item of my history]]" -- always display the last value |
|||
end properties |
|||
to set newValue |
|||
push newValue nested into my history |
|||
end set |
|||
to rollback |
|||
pop my history |
|||
return it |
|||
end rollback |
|||
</syntaxhighlight> |
|||
Here is an example how this could be used (note that variables in SenseTalk may hold any type of value): |
|||
<syntaxhighlight lang="sensetalk">set x to be a new HistoryVariable |
|||
tell x to set "Hello" |
|||
put "x is now" && x -- display the current value |
|||
tell x to set 88 |
|||
put "x is now" && x |
|||
tell x to set "World" |
|||
put "x is now" && x |
|||
put "History of x:" && x's history -- non-destructively display the history |
|||
tell x to set [9,6,3] -- set it to a list |
|||
put "x is now" && x |
|||
put "History of x:" && the history of x |
|||
put "Rolling back:" && x.rollback -- remove the last value |
|||
put "After rollback, x is now" && x |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
x is now Hello |
|||
x is now 88 |
|||
x is now World |
|||
History of x: ["Hello",88,"World"] |
|||
x is now [9,6,3] |
|||
History of x: ["Hello",88,"World",[9,6,3]] |
|||
Rolling back: [9,6,3] |
|||
After rollback, x is now World |
|||
</pre> |
|||
=={{header|Sidef}}== |
=={{header|Sidef}}== |
||
Implemented as a class: |
Implemented as a class: |
||
< |
<syntaxhighlight lang="ruby">class HistoryVar(v) { |
||
has history = [] |
has history = [] |
||
Line 2,302: | Line 2,845: | ||
say "History: #{foo.history}" |
say "History: #{foo.history}" |
||
say "Current value: #{foo}"</ |
say "Current value: #{foo}"</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 2,312: | Line 2,855: | ||
Smalltalk doesn't have native support for history variables. It could be implemented using (implementation dependent) tracing facilities, or by changing the code generator (which is part of the library and open for change). |
Smalltalk doesn't have native support for history variables. It could be implemented using (implementation dependent) tracing facilities, or by changing the code generator (which is part of the library and open for change). |
||
The following implementation is portable and in the spirit of the Lisp implementation above, and defines a new "assignment operator": |
The following implementation is portable and in the spirit of the Lisp implementation above, and defines a new "assignment operator": |
||
< |
<syntaxhighlight lang="smalltalk">Object subclass:'HVar' |
||
instanceVariableNames:'values' |
instanceVariableNames:'values' |
||
classVariableNames:'' |
classVariableNames:'' |
||
Line 2,347: | Line 2,890: | ||
x value. |
x value. |
||
x history. |
x history. |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Swift}}== |
=={{header|Swift}}== |
||
Swift does not support history variables. However, you can add a watcher that can track when the variable will change. |
Swift does not support history variables. However, you can add a watcher that can track when the variable will change. |
||
< |
<syntaxhighlight lang="swift">var historyOfHistory = [Int]() |
||
var history:Int = 0 { |
var history:Int = 0 { |
||
willSet { |
willSet { |
||
Line 2,361: | Line 2,905: | ||
history = 4 |
history = 4 |
||
println(historyOfHistory) |
println(historyOfHistory) |
||
</syntaxhighlight> |
|||
</lang> |
|||
another approach, using generics: |
another approach, using generics: |
||
plug this code into a Playground to see the output |
plug this code into a Playground to see the output |
||
{{works with|Swift|2.x+}} |
{{works with|Swift|2.x+}} |
||
< |
<syntaxhighlight lang="swift"> |
||
struct History <T> { |
struct History <T> { |
||
Line 2,407: | Line 2,951: | ||
h.undo() // outputs "First" |
h.undo() // outputs "First" |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Tcl}}== |
=={{header|Tcl}}== |
||
Though Tcl's variables don't have history by default, it can be added easily through the use of traces: |
Though Tcl's variables don't have history by default, it can be added easily through the use of traces: |
||
< |
<syntaxhighlight lang="tcl"># Define the history machinery |
||
proc histvar {varName operation} { |
proc histvar {varName operation} { |
||
upvar 1 $varName v ___history($varName) history |
upvar 1 $varName v ___history($varName) history |
||
Line 2,443: | Line 2,987: | ||
upvar 1 $varName v ___history($key) history |
upvar 1 $varName v ___history($key) history |
||
set v [lindex $history end] |
set v [lindex $history end] |
||
}</ |
}</syntaxhighlight> |
||
Demonstrating how to use it: |
Demonstrating how to use it: |
||
< |
<syntaxhighlight lang="tcl"># Enable history for foo |
||
histvar foo start |
histvar foo start |
||
set foo {a b c d} |
set foo {a b c d} |
||
Line 2,457: | Line 3,001: | ||
histvar foo undo |
histvar foo undo |
||
puts $foo |
puts $foo |
||
histvar foo stop</ |
histvar foo stop</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 2,465: | Line 3,009: | ||
123 |
123 |
||
a b c d |
a b c d |
||
</pre> |
|||
=={{header|Wren}}== |
|||
{{trans|Kotlin}} |
|||
Not built in but we can soon make a suitable class. |
|||
<syntaxhighlight lang="wren">class HistoryVariable { |
|||
construct new(initValue) { |
|||
_history = [initValue] |
|||
} |
|||
currentValue { _history[-1] } |
|||
currentValue=(newValue) { _history.add(newValue) } |
|||
showHistory() { |
|||
System.print("The variable's history, oldest values first, is:") |
|||
for (item in _history) System.print(item) |
|||
} |
|||
} |
|||
var v = HistoryVariable.new(1) |
|||
v.currentValue = 2 |
|||
v.currentValue = 3 |
|||
v.showHistory() |
|||
System.print("\nCurrent value is %(v.currentValue)")</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
The variable's history, oldest values first, is: |
|||
1 |
|||
2 |
|||
3 |
|||
Current value is 3 |
|||
</pre> |
</pre> |
||
=={{header|zkl}}== |
=={{header|zkl}}== |
||
No native support, here is a something that can be done with a class: |
No native support, here is a something that can be done with a class: |
||
< |
<syntaxhighlight lang="zkl">class HistoryVar{ |
||
var [private] _v, _history=List(), maxSz; |
var [private] _v, _history=List(), maxSz; |
||
fcn init(v,maxEntries=3){ maxSz=maxEntries; set(v) } |
fcn init(v,maxEntries=3){ maxSz=maxEntries; set(v) } |
||
Line 2,486: | Line 3,063: | ||
fcn{ _history.pump(List,fcn([(t,v)]){ T(Time.Date.ctime(t),v) }) }; |
fcn{ _history.pump(List,fcn([(t,v)]){ T(Time.Date.ctime(t),v) }) }; |
||
fcn __opAdd(x){ set(_v + x); self } |
fcn __opAdd(x){ set(_v + x); self } |
||
}</ |
}</syntaxhighlight> |
||
< |
<syntaxhighlight lang="zkl">hv:=HistoryVar(123); |
||
hv+4; |
hv+4; |
||
hv.set("Shuttle prepared for liftoff"); |
hv.set("Shuttle prepared for liftoff"); |
||
hv+": orbit achived"; |
hv+": orbit achived"; |
||
hv.history.concat("\n").println(); |
hv.history.concat("\n").println(); |
||
hv.get(3).println("<-- value two changes ago");</ |
hv.get(3).println("<-- value two changes ago");</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
Latest revision as of 11:15, 25 January 2024
You are encouraged to solve this task according to the task description, using any language you may know.
Storing the history of objects in a program is a common task. Maintaining the history of an object in a program has traditionally required programmers either to write specific code for handling the historical data, or to use a library which supports history logging.
History variables are variables in a programming language which store not only their current value, but also the values they have contained in the past. Some existing languages do provide support for history variables. However these languages typically have many limits and restrictions on use of history variables.
[http://www.bod.com/index.php?id=3435&objk_id=148050 "History Variables: The Semantics, Formal Correctness, and Implementation of History Variables in an Imperative Programming Language" by Mallon and Takaoka]
Concept also discussed on LtU and Patents.com.
- Task
Demonstrate History variable support:
- enable history variable support (if needed)
- define a history variable
- assign three values
- non-destructively display the history
- recall the three values.
For extra points, if the language of choice does not support history variables,
demonstrate how this might be implemented.
68000 Assembly
68000 Assembly doesn't support history variables natively. They can be implemented using a stack system. The 68000 can use push/pop commands on any address register, not just the hardware stack pointer.
HistoryVar equ $100200 ;assume this is work ram.
LEA HistoryVar+4,A0
MOVE.L #$11223344,D0
MOVE.L D0,-(A0) ;store the 32-bit value #$11223344 starting at $100200
MOVE.L #$55667788,D0
MOVE.L D0,-(A0) ;store the 32-bit value #$5566788 at $1001FC
MOVE.L #$AABBCCDD,D0
MOVE.L D0,-(A0) ;store the 32-bit value #$AABBCCDD at $1001F8
;display the history
LEA HistoryVar,A0
MOVE.L (A0),D0 ;load #$11223344 into D0
MOVE.L (-4,A0),D1 ;load #$55667788 into D1
MOVE.L (-8,A0),D2 ;load #$AABBCCDD into D2
jsr PrintHex32 ;unimplemented routine that prints D0 as a 32-bit hex number
mov D1,D0
jsr PrintHex32
mov D2,D0
jsr PrintHex32
Whether this constitutes a single variable at this point is debatable, because at a hardware level anything you write to a memory address permanently erases what was there, with no way to get it back unless you saved it somewhere else. So storing three values at the same memory location won't maintain the history.
Ada
Ada does not natively support history variables -- we have to implement them.
Furthermore, Ada is a strongly typed language -- that means, we would need to write a history variable type for every basic item type. Instead, we write a single generic package "History_Variables" that works for any item type.
Generic Package "History_Variables"
Specification:
private with Ada.Containers.Indefinite_Vectors;
generic
type Item_Type (<>) is private;
package History_Variables is
type Variable is tagged limited private;
-- set and get current value
procedure Set(V: in out Variable; Item: Item_Type);
function Get(V: Variable) return Item_Type;
-- number of items in history (including the current one)
function Defined(V: Variable) return Natural;
-- non-destructively search for old values
function Peek(V: Variable; Generation: Natural := 1) return Item_Type;
-- V.Peek(0) returns current value; V.Peek(1) the previous value, etc.
-- when calling V.Peek(i), i must be in 0 .. V.Defined-1, else Constraint_Error is raised
-- destructively restore previous value
procedure Undo(V: in out Variable);
-- old V.Peek(0) is forgotten, old V.Peek(i) is new V.Peek(i-1), etc.
-- accordingly, V.Defined decrements by 1
-- special case: if V.Defined=0 then V.Undo does not change V
private
package Vectors is new Ada.Containers.Indefinite_Vectors
(Index_Type => Positive,
Element_Type => Item_Type);
type Variable is tagged limited record
History: Vectors.Vector;
end record;
end History_Variables;
The implementation of "History_Variables":
package body History_Variables is
-- set and get
procedure Set(V: in out Variable; Item: Item_Type) is
begin
V.History.Prepend(Item);
end Set;
function Get(V: Variable) return Item_Type is
begin
return V.History.First_Element;
end Get;
-- number of items in history (including the current one)
function Defined(V: Variable) return Natural is
begin
return (1 + V.History.Last_Index) - V.History.First_Index;
end Defined;
-- non-destructively search
function Peek(V: Variable; Generation: Natural := 1) return Item_Type is
Index: Positive := V.History.First_Index + Generation;
begin
if Index > V.History.Last_Index then
raise Constraint_Error;
end if;
return V.History.Element(Index);
end Peek;
procedure Undo(V: in out Variable) is
begin
V.History.Delete_First;
end Undo;
end History_Variables;
Sample 1: The History of an Integer Variable
with Ada.Text_IO, History_Variables;
procedure Test_History is
package Int_With_Hist is new History_Variables(Integer);
-- define a history variable
I: Int_With_Hist.Variable;
Sum: Integer := 0;
begin
-- assign three values
I.Set(3);
I.Set(I.Get + 4);
I.Set(9);
-- non-destructively display the history
for N in reverse 0 .. I.Defined-1 loop
Ada.Text_IO.Put(Integer'Image(I.Peek(N)));
end loop;
Ada.Text_IO.New_Line;
-- recall the three values
while I.Defined > 0 loop
Sum := Sum + I.Get;
I.Undo;
end loop;
Ada.Text_IO.Put_Line(Integer'Image(Sum));
end Test_History;
- Output:
3 7 9 19
Sample 2: The History of a String
with Ada.Text_IO, History_Variables;
procedure Test_History is
package Str_With_Hist is new History_Variables(String);
-- define a history variable
S: Str_With_Hist.Variable;
Sum: Integer := 0;
begin
-- assign three values
S.Set("one");
S.Set(S.Get & S.Get); --"oneone"
S.Set("three");
-- non-destructively display the history
for N in reverse 0 .. S.Defined-1 loop
Ada.Text_IO.Put(S.Peek(Generation => N) &" ");
end loop;
Ada.Text_IO.New_Line;
-- recall the three values
while S.Defined > 0 loop
Sum := Sum + S.Get'Length;
S.Undo;
end loop;
Ada.Text_IO.Put_Line(Integer'Image(Sum));
end Test_History;
- Output:
one oneone three 14
ALGOL W
Algol W does not have history variables as standard, as with other languages here, we can add them using a simple linked list.
As Algol W does not have generic types, a separate history variable type would be required for each type of variable.
begin
% implements integer history variables %
% similar history types could be defined for other types of variable %
record HInteger ( integer iValue; reference(HInteger) iPrev );
% sets the latest value of hv to n %
procedure setIhv ( reference(HInteger) value result hv; integer value n ) ; hv := HInteger( n, hv );
% declare an integer history variable %
reference(HInteger) hv;
% initialise the history to a null value %
hv := null;
% assign three values %
setIhv( hv, 1 );
setIhv( hv, 2 );
setIhv( hv, 3 );
% show the history of hv %
begin
reference(HInteger) h;
write( "hv history: " );
h := hv;
while h not = null do begin
writeon( i_w := 3, s_w := 0, iValue(h), " " );
h := iPrev(h)
end while_h_ne_null
end;
% remove the values from hv, summing them as in the Ada sample %
begin
integer s;
s := 0;
while hv not = null do begin
s := s + iValue(hv);
hv := iPrev(hv)
end while_hv_ne_null ;
write( "Sum of the historic values: ", s )
end
end.
- Output:
hv history: 3 2 1 Sum of the historic values: 6
Arturo
define :history [][
init: [
this\record: @[0]
]
]
assign: function [historyVar,newValue][
historyVar\record: historyVar\record ++ newValue
]
alias.infix {'-->} 'assign
records: function [historyVar][
historyVar\record
]
retrieve: function [historyVar][
result: last historyVar\record
historyVar\record: chop historyVar\record
return result
]
current: function [historyVar][
return last historyVar\record
]
do [
h: to :history []
print "Assigning three values: 1, 2, 3..."
h --> 1
h --> 2
h --> 3
print "\nHistory (oldest values first):"
print [">" records h]
print ["\nCurrent value is:" current h]
print "\nRecalling the three values..."
loop 1..3 'x ->
print ["- Recalled:" retrieve h]
print "\nHistory:"
print [">" records h]
]
- Output:
Assigning three values: 1, 2, 3... History (oldest values first): > [0 1 2 3] Current value is: 3 Recalling the three values... - Recalled: 3 - Recalled: 2 - Recalled: 1 History: > [0]
AspectJ
AspectJ implementation for Java 7. Type of the history variable (Java class):
public class HistoryVariable
{
private Object value;
public HistoryVariable(Object v)
{
value = v;
}
public void update(Object v)
{
value = v;
}
public Object undo()
{
return value;
}
@Override
public String toString()
{
return value.toString();
}
public void dispose()
{
}
}
Aspect (HistoryHandling.aj):
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
public privileged aspect HistoryHandling
{
before() : execution(HistoryVariable.new(..))
{
history.put((HistoryVariable) thisJoinPoint.getTarget(), new LinkedList<>());
}
after() : execution(void HistoryVariable.dispose())
{
history.remove(thisJoinPoint.getTarget());
}
before(Object v) : execution(void HistoryVariable.update(Object)) && args(v)
{
final HistoryVariable hv = (HistoryVariable) thisJoinPoint.getThis();
history.get(hv).add(hv.value);
}
after() : execution(Object HistoryVariable.undo())
{
final HistoryVariable hv = (HistoryVariable) thisJoinPoint.getThis();
final Deque<Object> q = history.get(hv);
if (!q.isEmpty())
hv.value = q.pollLast();
}
String around() : this(HistoryVariable) && execution(String toString())
{
final HistoryVariable hv = (HistoryVariable) thisJoinPoint.getThis();
final Deque<Object> q = history.get(hv);
if (q == null)
return "<disposed>";
else
return "current: "+ hv.value + ", previous: " + q.toString();
}
private Map<HistoryVariable, Deque<Object>> history = new HashMap<>();
}
Usage:
public final class Main
{
public static void main(final String[] args)
{
HistoryVariable hv = new HistoryVariable("a");
hv.update(90);
hv.update(12.1D);
System.out.println(hv.toString());
System.out.println(hv.undo());
System.out.println(hv.undo());
System.out.println(hv.undo());
System.out.println(hv.undo());
System.out.println(hv.toString());
hv.dispose();
System.out.println(hv.toString());
}
}
- Output:
current: 12.1, previous: [a, 90] 12.1 90 a a current: a, previous: [] <disposed>
AutoHotkey
AutoHotkey records a history of your keypresses, but not your variables. The closest you can come is with a class:
HV := new HistoryVariable
HV.var := 1
HV.var := 2
HV.var := "Latest value"
Msgbox % HV.var "`n" HV[2] "`n" HV[3]
class HistoryVariable {
__Set(aName, aValue) {
this.Insert(1,aValue)
Return aValue
}
__Get(aName) {
Return this[1]
}
}
C#
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace History
{
class Program
{
static void Main(string[] args)
{
var h = new HistoryObject();
h.Value = 5;
h.Value = "foo";
h.Value += "bar";
var history = h.ToArray();
for (int i = 0; i < history.Length; i++)
{
Console.Write("{0}{1}", history[i], ((i >= history.Length - 1) ? "\n" : " <- "));
}
h.Undo();
h.Undo();
h.Undo();
Console.WriteLine(h.Value);
}
private class HistoryObject : IEnumerable<object>
{
public HistoryObject()
{
_history = new Stack<object>(); // Initiates the history stack.
}
public object Value
{
get // Returns the top value from the history if there is one. Otherwise null.
{
if (_history.Count > 0)
return _history.Peek();
return null;
}
set { _history.Push(value); } // Adds the specified value to the history.
}
public void Undo()
{
if (_history.Count > 0)
_history.Pop(); // Removes the current value from the history.
}
// History stack that will hold all previous values of the object.
private readonly Stack<object> _history;
public IEnumerator<object> GetEnumerator()
{
return _history.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
}
- Output:
foobar <- foo <- 5
C++
C++ does not have history variables, but they can easily by implemented with a generic class.
#include <deque>
#include <iostream>
#include <string>
template <typename T>
class with_history {
public:
with_history(const T& element) {
history.push_front(element);
}
T get() {
return history.front();
}
void set(const T& element) {
history.push_front(element);
}
std::deque<T> get_history() {
return std::deque<T>(history);
}
T rollback() {
if ( history.size() > 1 ) {
history.pop_front();
}
return history.front();
}
private:
std::deque<T> history;
};
int main() {
with_history<double> number(1.2345);
std::cout << "Current value of number: " << number.get() << std::endl;
number.set(3.4567);
number.set(5.6789);
std::cout << "Historical values of number: ";
for ( const double& value : number.get_history() ) {
std::cout << value << " ";
}
std::cout << std::endl << std::endl;
with_history<std::string> word("Goodbye");
word.set("Farewell");
word.set("Hello");
std::cout << word.get() << std::endl;
word.rollback();
std::cout << word.get() << std::endl;
word.rollback();
std::cout << word.get() << std::endl;
word.rollback();
std::cout << word.get() << std::endl;
word.rollback();
}
- Output:
Current value of number: 1.2345 Historical values of number: 5.6789 3.4567 1.2345 Hello Farewell Goodbye Goodbye
Clojure
Clojure does not have history variables, but it can be accomplished via a watcher function that can track changes on a variable.
(def a (ref 0))
(def a-history (atom [@a])) ; define a history vector to act as a stack for changes on variable a
(add-watch a :hist (fn [key ref old new] (swap! a-history conj new)))
- Sample Output:
user=> (dosync (ref-set a 1)) 1 user=> (dosync (ref-set a 2)) 2 user=> (dosync (ref-set a 3)) 3 user=> @a-history [0 1 2 3] user=> (dotimes [n 3] (println (peek @a-history)) (swap! a-history pop)) 3 2 1 nil user=> @a-history [0]
Common Lisp
In Lisp the list is a natural and simple data structure for representing variables with history. By adding some simple macros we can make a small DSL that hides the list and makes it look like a history variable is it's own special thing.
(defmacro make-hvar (value)
`(list ,value))
(defmacro get-hvar (hvar)
`(car ,hvar))
(defmacro set-hvar (hvar value)
`(push ,value ,hvar))
;; Make sure that setf macro can be used
(defsetf get-hvar set-hvar)
(defmacro undo-hvar (hvar)
`(pop ,hvar))
(let ((v (make-hvar 1)))
(format t "Initial value = ~a~%" (get-hvar v))
(set-hvar v 2)
(setf (get-hvar v) 3) ;; Alternative using setf
(format t "Current value = ~a~%" (get-hvar v))
(undo-hvar v)
(undo-hvar v)
(format t "Restored value = ~a~%" (get-hvar v)))
- Output:
Initial value = 1 Current value = 3 Restored value = 1
D
D does not have history variables. The following implementation provides a generic HistoryVariable that protects the historical values by defining them as 'const'.
import std.stdio, std.array, std.string, std.datetime, std.traits;
/// A history variable.
struct HistoryVariable(T) {
/// A value in a point in time.
static struct HistoryValue {
SysTime time;
T value;
// Alternative to the more common toString.
//void toString(scope void delegate(string) output) const {
void toString(scope void delegate(const(char)[]) output)const {
output(format("%s; %s", time, value));
}
}
const(HistoryValue)[] values;
private void addValue(T value) {
values ~= HistoryValue(Clock.currTime(), value);
}
void opAssign(T value) {
addValue(value);
}
@property T currentValue() const pure nothrow {
return values.back.value;
}
alias currentValue this;
@property auto history() const pure nothrow {
return values;
}
/**
Demonstrating D's compile-time reflection features. The member
functions that are in this 'static if' block would be added for
types T that are arrays (including strings). */
static if (isArray!T) {
// Append-with-assign operator.
void opOpAssign(string op : "~")(T element) {
addValue(currentValue ~ element);
}
// Similar implementation for other array operators...
}
}
void main() {
HistoryVariable!int x;
x = 1;
x = 2;
x = 3;
writefln("%(%s\n%)\n", x.history);
HistoryVariable!string s;
s = "hello";
s ~= " world";
s = "goodby";
writefln("%(%s\n%)", s.history);
}
- Sample Output:
2013-Jan-19 23:04:55.1660302; 1 2013-Jan-19 23:04:55.1660407; 2 2013-Jan-19 23:04:55.1660424; 3 2013-Jan-19 23:04:55.1662551; hello 2013-Jan-19 23:04:55.1662581; hello world 2013-Jan-19 23:04:55.1662596; goodby
Delphi
program History_variables;
{$APPTYPE CONSOLE}
uses
System.SysUtils;
type
THistoryVarType = record
value: variant;
timestamp: TDateTime;
function ToString: string;
end;
THistoryVar = record
Fvalue: variant;
FHistory: TArray<THistoryVarType>;
private
procedure SetValue(const Value: Variant);
function GetTimestamp: TDateTime;
public
property History: TArray<THistoryVarType> read FHistory;
constructor Init(val: variant);
function Dump: string;
function Recall(steps: Integer): variant; overload;
function Recall(timestamp: TDateTime): variant; overload;
property Timestamp: TDateTime read GetTimestamp;
property Value: Variant read Fvalue write SetValue;
end;
{ THistoryVar }
function THistoryVar.Dump: string;
begin
Result := '';
for var h in FHistory do
Result := Result + h.ToString + #10;
end;
function THistoryVar.GetTimestamp: TDateTime;
begin
if Length(FHistory) = 0 then
exit(0);
Result := FHistory[high(FHistory)].timestamp;
end;
constructor THistoryVar.Init(val: variant);
begin
SetLength(Fhistory, 0);
SetValue(val);
end;
function THistoryVar.Recall(timestamp: TDateTime): variant;
begin
for var h in FHistory do
begin
if h.timestamp = timestamp then
exit(h.value);
end;
result := Fvalue;
end;
function THistoryVar.Recall(steps: Integer): variant;
begin
if (steps <= 1) or (steps >= Length(FHistory)) then
exit(value);
result := FHistory[Length(FHistory) - steps - 1].value;
end;
procedure THistoryVar.SetValue(const Value: Variant);
begin
SetLength(FHistory, Length(FHistory) + 1);
FHistory[High(FHistory)].Value := Value;
FHistory[High(FHistory)].timestamp := now;
Fvalue := Value;
end;
{ THistoryVarType }
function THistoryVarType.ToString: string;
var
dt: string;
begin
DateTimeToString(dt, 'mm/dd/yyyy hh:nn:ss.zzz', timestamp);
Result := format('%s - %s', [dt, Value]);
end;
begin
var v := THistoryVar.Init(0);
v.Value := True;
Sleep(200);
if v.Value then
v.Value := 'OK';
Sleep(100);
if v.Value = 'OK' then
v.Value := PI;
writeln('History of variable values:'#10);
Writeln(v.Dump);
Writeln('Recall 2 steps');
Writeln(v.Recall(2));
var t := v.History[3].timestamp;
Writeln(#10'Recall to timestamp ', datetimetostr(t));
Writeln(v.Recall(t));
readln;
end.
- Output:
History of variable values: 02/28/2021 19:50:32.512 - 0 02/28/2021 19:50:32.512 - True 02/28/2021 19:50:32.712 - OK 02/28/2021 19:50:32.813 - 3,14159265358979 Recall 2 steps True Recall to timestamp 28/02/2021 19:50:32 3,14159265358979
EchoLisp
No native support. We implement an anonymous stack associated with the variable, and a few syntax rules to define the needed operations.
(define-syntax-rule (make-h-var name) (define name (stack (gensym))))
(define-syntax-rule (h-get name) (stack-top name))
(define-syntax-rule (h-set name value) (push name value))
(define-syntax-rule (h-undo name)
(begin
(pop name)
(when ( stack-empty? name) (error "no more values" 'name))
(stack-top name)))
(define-syntax-rule (h-values name) (stack->list name))
;; usage
(make-h-var x)→ x
(h-set x 42) → 42
(h-set x 666)→ 666
(h-set x 'elvis)→ elvis
(h-values x) → (42 666 elvis) ;; historized values
(h-get x) → elvis
(h-undo x)→ 666
(h-undo x) → 42
(h-undo x) → ❌ error: no more values x
Elena
ELENA 6.x :
import extensions;
import system'collections;
import system'routines;
import extensions'routines;
class HistoryVariable
{
Stack previous := new Stack();
object value;
Value
{
get() = value;
set(value)
{
if ((this value) != nil)
{
previous.push(this value)
};
this value := value
}
}
undo()
{
ifnot (previous.isEmpty())
{
value := previous.pop()
}
else
{
value := nil
}
}
enumerator() => previous;
string toPrintable() => this value;
}
public program()
{
var o := new HistoryVariable();
o.Value := 5;
o.Value := "foo";
o.Value := o.Value + "bar";
console.printLine(o);
o.forEach(printingLn);
o.undo().undo().undo();
console.printLine(o.Value ?? "nil")
}
- Output:
foobar foo 5 nil
Erlang
If we consider the usage of history variables, like not losing the old value by mistake or having a way to get an old value, then Erlang can really help. Being single assignment Erlang will not allow you to lose an old value, and it is certain that the previous values are always there. The downside is that you must handle this manually.
- Output:
1> V1 = "123". "123" 2> V1 = V1 ++ "qwe". ** exception error: no match of right hand side value "123qwe" 3> V2 = V1 ++ "qwe". "123qwe" 4> V3 = V2 ++ "ASD". "123qweASD" 5> V1. "123" 6> V2. "123qwe" 7> V3. "123qweASD"
Factor
USING: accessors combinators formatting kernel models.history ;
1 <history> {
[ add-history ]
[ value>> "Initial value: %u\n" printf ]
[ 2 >>value add-history ]
[ 3 swap value<< ]
[ value>> "Current value: %u\n" printf ]
[ go-back ]
[ go-back ]
[ value>> "Restored value: %u\n" printf ]
} cleave
- Output:
Initial value: 1 Current value: 3 Restored value: 1
Forth
Forth does not have history variables, but it is trivial to define them. This one uses a linked list. The history variable is initialized to zero to avoid expensive sanity checks in H@. H-- undoes the assignment of a value and effectively returns a history variable to its former state.
: history create here cell+ , 0 , -1 , ;
: h@ @ @ ;
: h! swap here >r , dup @ , r> swap ! ;
: .history @ begin dup cell+ @ -1 <> while dup ? cell+ @ repeat drop ;
: h-- dup @ cell+ @ dup -1 = if abort" End of history" then swap ! ;
A sample session:
history z ok 23 z h! ok z h@ . 23 ok 34 z h! ok z h@ . 34 ok 45 z h! ok z h@ . 45 ok z .history 45 34 23 ok z dup h-- h@ . 34 ok z dup h-- h@ . 23 ok z dup h-- h@ . 0 ok z dup h-- h@ . :86: End of history
Go
We're all in this for the extra points. Mallon and Takota seem happy with sequences, but time and timestamps were mentioned on LtU. Beyond a separate sequence for each history variable, timestamps enable multiple variables to be seen in a common temporal sequence. In Go, with it's attention to concurrency, this might be done with flexibility for proper handling of shared variables, and efficient handling of variables limited to a single thread.
package main
import (
"fmt"
"sort"
"sync"
"time"
)
// data type for history variable (its an int)
type history struct {
timestamp tsFunc
hs []hset
}
// data type for timestamp generator
type tsFunc func() time.Time
// data type for a "set" event
type hset struct {
int // new value
t time.Time // timestamp
}
// newHistory creates a history variable
func newHistory(ts tsFunc) history {
return history{ts, []hset{{t: ts()}}}
}
// int returns the current value
func (h history) int() int {
return h.hs[len(h.hs)-1].int
}
// set does what you expect and returns the timestamp recorded for the event
func (h *history) set(x int) time.Time {
t := h.timestamp()
h.hs = append(h.hs, hset{x, t})
return t
}
// dump displays a complete history
func (h history) dump() {
for _, hs := range h.hs {
fmt.Println(hs.t.Format(time.StampNano), hs.int)
}
}
// recall recalls the value stored in the history variable at time t.
// if the variable had not been created yet, ok is false.
func (h history) recall(t time.Time) (int, /*ok*/ bool) {
i := sort.Search(len(h.hs), func(i int) bool {
return h.hs[i].t.After(t)
})
if i > 0 {
return h.hs[i-1].int, true
}
return 0, false
}
// newTimestamper returns a function that generates unique timestamps.
// Use a single timestamper for multiple history variables to preserve
// an unambiguous sequence of assignments across the multiple history
// variables within a single goroutine.
func newTimestamper() tsFunc {
var last time.Time
return func() time.Time {
if t := time.Now(); t.After(last) {
last = t
} else {
last.Add(1)
}
return last
}
}
// newProtectedTimestamper generates unique timestamps for concurrent
// goroutines.
func newProtectedTimestamper() tsFunc {
var last time.Time
var m sync.Mutex
return func() (t time.Time) {
t = time.Now()
m.Lock() // m protects last
if t.After(last) {
last = t
} else {
last.Add(1)
t = last
}
m.Unlock()
return
}
}
func main() {
// enable history variable support appropriate for single goroutine.
ts := newTimestamper()
// define a history variable
h := newHistory(ts)
// assign three values. (timestamps kept for future reference.)
ref := []time.Time{h.set(3), h.set(1), h.set(4)}
// non-destructively display history
fmt.Println("History of variable h:")
h.dump()
// recall the three values. (this is non-destructive as well, but
// different than the dump in that values are recalled by time.)
fmt.Println("Recalling values:")
for _, t := range ref {
rv, _ := h.recall(t)
fmt.Println(rv)
}
}
- Output:
History of variable h: Dec 3 18:51:17.292260000 0 Dec 3 18:51:17.292262000 3 Dec 3 18:51:17.292264000 1 Dec 3 18:51:17.292270000 4 Recalling values: 3 1 4
Haskell
There are no native Haskell history variables, but they are simple to implement.
import Data.IORef
newtype HVar a = HVar (IORef [a])
newHVar :: a -> IO (HVar a)
newHVar value = fmap HVar (newIORef [value])
readHVar :: HVar a -> IO a
readHVar (HVar ref) = fmap head (readIORef ref)
writeHVar :: a -> HVar a -> IO ()
writeHVar value (HVar ref) = modifyIORef ref (value:)
undoHVar :: HVar a -> IO ()
undoHVar (HVar ref) = do
(_ : history) <- readIORef ref
writeIORef ref history
getHistory :: HVar a -> IO [a]
getHistory (HVar ref) = readIORef ref
-- Testing
main :: IO ()
main = do
var <- newHVar 0
writeHVar 1 var
writeHVar 2 var
writeHVar 3 var
getHistory var >>= print
undoHVar var
undoHVar var
undoHVar var
J
J does not natively support "history variables", but the functionality is easy to add:
varref_hist_=:'VAR','_hist_',~]
set_hist_=:4 :0
V=.varref x
if.0>nc<V do.(<V)=:''end.
(<V)=.V~,<y
y
)
getall_hist_=:3 :0
(varref y)~
)
length_hist_=: #@getall
get_hist_=: _1 {:: getall
Example use:
'x' set_hist_ 9
9
'x' set_hist_ 10
10
'x' set_hist_ 11
11
get_hist_ 'x'
11
length_hist_ 'x'
3
getall_hist_ 'x'
┌─┬──┬──┐
│9│10│11│
└─┴──┴──┘
Note that each value is contained in a box, so different values do not need to be type compatible with each other. If this is considered a defect then assertions could be added to enforce type compatibility across assignments.
Note that only nouns are supported here: If you want to store verbs using this mechanism you will need to use their gerunds.
Finally, note that the distinction between relevant history and irrelevant history depend on the application, and perhaps on the user. Also, necessity suggests that a lot of the history detail will be irrelevant for many purposes.
Java
Java does not support history variables, but they are easy to implement using the lists that come with Java's Collections framework.
Java Integer with History
This implementation does not allow the History Variable to be "empty". It can be assigned one or multiple "nulls", but it can never have an undefined value (such as being created and not initialized).
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* A class for an "Integer with a history".
* <p>
* Note that it is not possible to create an empty Variable (so there is no "null") with this type. This is a design
* choice, because if "empty" variables were allowed, reading of empty variables must return a value. Null is a
* bad idea, and Java 8's Optional<T> (which is somewhat like the the official fix for the null-bad-idea) would
* make things more complicated than an example should be.
*/
public class IntegerWithHistory {
/**
* The "storage Backend" is a list of all values that have been ever assigned to this variable. The List is
* populated front to back, so a new value is inserted at the start (position 0), and older values move toward the end.
*/
private final List<Integer> history;
/**
* Creates this variable and assigns the initial value
*
* @param value initial value
*/
public IntegerWithHistory(Integer value) {
history = new LinkedList<>();
history.add(value);
}
/**
* Sets a new value, pushing the older ones back in the history
*
* @param value the new value to be assigned
*/
public void set(Integer value) {
//History is populated from the front to the back, so the freshest value is stored a position 0
history.add(0, value);
}
/**
* Gets the current value. Since history is populuated front to back, the current value is the first element
* of the history.
*
* @return the current value
*/
public Integer get() {
return history.get(0);
}
/**
* Gets the entire history all values that have been assigned to this variable.
*
* @return a List of all values, including the current one, ordered new to old
*/
public List<Integer> getHistory() {
return Collections.unmodifiableList(this.history);
}
/**
* Rolls back the history one step, so the current value is removed from the history and replaced by it's predecessor.
* This is a destructive operation! It is not possible to rollback() beyond the initial value!
*
* @return the value that had been the current value until history was rolled back.
*/
public Integer rollback() {
if (history.size() > 1) {
return history.remove(0);
} else {
return history.get(0);
}
}
}
Test Program:
public class TestIntegerWithHistory {
public static void main(String[] args) {
//creating and setting three different values
IntegerWithHistory i = new IntegerWithHistory(3);
i.set(42);
i.set(7);
//looking at current value and history
System.out.println("The current value of i is :" + i.get());
System.out.println("The history of i is :" + i.getHistory());
//demonstrating rollback
System.out.println("Rolling back:");
System.out.println("returns what was the current value: " + i.rollback());
System.out.println("after rollback: " + i.get());
System.out.println("returns what was the current value: " + i.rollback());
System.out.println("after rollback: " + i.get());
System.out.println("Rolling back only works to the original value: " + i.rollback());
System.out.println("Rolling back only works to the original value: " + i.rollback());
System.out.println("So there is no way to 'null' the variable: " + i.get());
}
}
- Output:
The current value of i is :7 The history of i is :[7, 42, 3] Rolling back: returns what was the current value: 7 after rollback: 42 returns what was the current value: 42 after rollback: 3 Rolling back only works to the original value: 3 Rolling back only works to the original value: 3 So there is no way to 'null' the variable: 3
Java History for "any class" using Generics
This implementation does not allow the History Variable to be "empty". It can be assigned one or multiple "nulls", but it can never have an undefined value (such as being created and not initialized). Test Program using a "String with History":
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* A Generic class for an "Anything with a history".
* <p>
* Note that it is not possible to create an empty Variable (so there is no "null") with this type. This is a design
* choice, because if "empty" variables were allowed, reading of empty variables must return a value. Null is a
* bad idea, and Java 8's Optional<T> (which is somewhat like the the official fix for the null-bad-idea) would
* make things more complicated than an example should be.
* <p>
* Also note that this "really works" only with constant Ts. If somebody keeps a reference to an assigned value,
* and is able to modify the state of this value through the reference , this will not be reflected in the history!
*/
public class WithHistory<T> {
/**
* The "storage Backend" is a list of all values that have been ever assigned to this variable. The List is
* populated front to back, so a new value is inserted at the start (position 0), and older values move toward the end.
*/
private final List<T> history;
/**
* Creates this variable and assigns the initial value
*
* @param value initial value
*/
public WithHistory(T value) {
history = new LinkedList<>();
history.add(value);
}
/**
* Sets a new value, pushing the older ones back in the history
*
* @param value the new value to be assigned
*/
public void set(T value) {
//History is populated from the front to the back, so the freshest value is stored a position 0
history.add(0, value);
}
/**
* Gets the current value. Since history is populuated front to back, the current value is the first element
* of the history.
*
* @return the current value
*/
public T get() {
return history.get(0);
}
/**
* Gets the entire history all values that have been assigned to this variable.
*
* @return a List of all values, including the current one, ordered new to old
*/
public List<T> getHistory() {
return Collections.unmodifiableList(this.history);
}
/**
* Rolls back the history one step, so the current value is removed from the history and replaced by it's predecessor.
* This is a destructive operation! It is not possible to rollback() beyond the initial value!
*
* @return the value that had been the cueent value until history was rolled back.
*/
public T rollback() {
if (history.size() > 1) {
return history.remove(0);
} else {
return history.get(0);
}
}
}
- Output:
The current value is :And now to something completely different. The history is:[And now to something completely different., See you later History!, Hello History!] Rolling back: returns what was the current value: And now to something completely different. after rollback: See you later History! returns what was the current value: See you later History! after rollback: Hello History! Rolling back only works to the original value: Hello History! Rolling back only works to the original value: Hello History! So there is no way to 'null' the variable: Hello History!
Julia
Julia currently does not support overloading the assignment "=" operator.
mutable struct Historied
num::Number
history::Vector{Number}
Historied(n) = new(n, Vector{Number}())
end
assign(y::Historied, z) = (push!(y.history, y.num); y.num = z; y)
x = Historied(1)
assign(x, 3)
assign(x, 5)
assign(x, 4)
println("Past history of variable x: $(x.history). Current value is $(x.num)")
- Output:
Past history of variable x: Number[1, 3, 5]. Current value is 4
Kotlin
// version 1.1.4
class HistoryVariable<T>(initialValue: T) {
private val history = mutableListOf<T>()
var currentValue: T
get() = history[history.size - 1]
set(value) {
history.add(value)
}
init {
currentValue = initialValue
}
fun showHistory() {
println("The variable's history, oldest values first, is:")
for (item in history) println(item)
}
}
fun main(args: Array<String>) {
val v = HistoryVariable(1)
v.currentValue = 2
v.currentValue = 3
v.showHistory()
println("\nCurrentvalue is ${v.currentValue}")
}
- Output:
The variable's history, oldest values first, is: 1 2 3 Currentvalue is 3
Lua
Lua does not natively support history variables. However, as with other languages here, something roughly equivalent could be implemented with a "stack wrapper". The only complicated issue might be how to treat nil's (null values). The implementation below allows initial nil's, and allows undo()'s back to nil, but does not consider nil as part of the "history".
Via metatable
-- History variables in Lua 6/12/2020 db
local HistoryVariable = {
new = function(self)
return setmetatable({history={}},self)
end,
get = function(self)
return self.history[#self.history]
end,
set = function(self, value)
self.history[#self.history+1] = value
end,
undo = function(self)
self.history[#self.history] = nil
end,
}
HistoryVariable.__index = HistoryVariable
local hv = HistoryVariable:new()
print("defined:", hv)
print("value is:", hv:get())
--
hv:set(1); print("set() to:", hv:get())
hv:set(2); print("set() to:", hv:get())
hv:set(3); print("set() to:", hv:get())
--
print("history:", table.concat(hv.history,","))
--
hv:undo(); print("undo() to:", hv:get())
hv:undo(); print("undo() to:", hv:get())
hv:undo(); print("undo() to:", hv:get())
- Output:
defined: table: 0000000000939240 value is: nil set() to: 1 set() to: 2 set() to: 3 history: 1,2,3 undo() to: 2 undo() to: 1 undo() to: nil
Via closure
If a desire is to keep history private and immutable, then..
-- History variables in Lua 6/12/2020 db
local function HistoryVariable()
local history = {}
return {
get = function()
return history[#history]
end,
set = function(value)
history[#history+1] = value
end,
undo = function()
history[#history] = nil
end,
getHistory = function()
local clone = {}
for k,v in pairs(history) do clone[k]=v end
return clone
end
}
end
local hv = HistoryVariable()
print("defined:", hv)
print("value is:", hv.get())
--
hv.set(1); print("set() to:", hv.get())
hv.set(2); print("set() to:", hv.get())
hv.set(3); print("set() to:", hv.get())
--
print("history:", table.concat(hv.getHistory(),","))
--
hv.undo(); print("undo() to:", hv.get())
hv.undo(); print("undo() to:", hv.get())
hv.undo(); print("undo() to:", hv.get())
- Output:
defined: table: 0000000000a59500 value is: nil set() to: 1 set() to: 2 set() to: 3 history: 1,2,3 undo() to: 2 undo() to: 1 undo() to: nil
M2000 Interpreter
We can make objects type of Group to act as History Variables. Each group has a pointer to a stack object (a linked list). First we see how we work with Stack (each module/function see a "current stack", we can make pointers to new stacks, but we can't get pointer of current stack). There are some statement no demonstrate here, such as Shift and ShiftBack (move top to a place, and move back to top), and Over (make new items by copying items).
Flush ' empty curtrent stack
\\ a is a pointer to a new stack object
a=stack:=1,2@,3%
Print Len(A)=3
For i=1 to Len(a) {
Print StackType$(a, i)="Number"
}
b=stack
Print len(b)=0
Push 1, 2 \\ to current stack
Stack b {
\\ make b the current stack
Data 1, 2, 3
} ' Data add to bottom
\\ now current stack get the old object
Stack b {Push 1, 2, 3 } ' Push add to top, so 3 is at top
Stack b {
While not empty {
Read k ' Read used to pop value to a variable
\\ number pop value in an expression
Print k, number, ' 3 2 1 1 2 3
}
Print
}
z=[] ' [] pop all values from current stack to a new stack, and return a pointer
Print z ' 2 1
z=Stack(a, z,a, z) ' merge stack objects, to a new one, preserve a and z
Print Len(z)
\\ empty a using a new object
a=stack
b=stack:=1,2,3
\\ z has two stack objects
z=stack:= a, b
Stack b { data 1000}
b=stack ' b point to a new stack
\\ we get pointer back
b=stackitem(z, 2)
Print stackitem(b, 4)=1000
So now lets see the code:
Module CheckHistoryVariables {
Class History {
Private:
myvalue=stack
Public:
Group Count {
Value {
link parent myvalue to m
=Len(m)
}
}
Function CopyMe {
m=This
.myvalue<=stack(.myvalue)
=Group(m)
}
Group CopyValues {
Value {
link parent myvalue to m
=Stack(m)
}
}
Function Values (x as long) {
if x>=0 and x<=len(.myvalue) then =StackItem(.myvalue, x)
Else Error "Fault index"
}
Module Back {
if len(.myvalue)<2 then exit
Stack .myvalue {Drop}
}
Operator "++" {
Stack .myvalue {
Push stackitem()+1
}
}
Operator "--" {
Stack .myvalue {
Push stackitem()-1
}
}
Operator "+=" (x) {
Stack .myvalue {
Push stackitem()+x
} }
Operator "-=" (x) {
Stack .myvalue {
Push stackitem()-x
}
}
Operator "/=" (x){
if x==0 then error "division by zero"
Stack .myvalue {
Push stackitem()/x
}
}
Operator "*=" (x){
Stack .myvalue {
Push stackitem()*x
}
}
Value {
=StackItem(.myvalue)
}
Set {
Read x : Stack .myvalue { Push x}
}
Class:
Module History {
If Match("N") then Read x : Stack .myvalue { Push x}
}
}
N=History()
N=1
N=2
N=3
Print N, N.Values(2), N.Values(3), N.Count
\\ Get a copy of history
m=N.CopyValues
Print len(m)=3
\\ just show all
Print m ' 3 2 1
\\ or iterate from last to first
k=each(m, -1, 1)
While k {
Print Stackitem(k) \\ 1 NL 2 NL 3 (NL = new line)
}
N1=N.CopyMe()
N=5
N1=4
N=6
Print N
Print N1.Count=4, N.Count=5
Print N1, N
Print N.CopyValues ' 6 5 3 2 1
Print N1.CopyValues ' 4 3 2 1
Print N1<N
N=N+1
Print N.Count=6
Print N.CopyValues ' 6 5 3 2 1
Print "Go Back"
While N.Count>1 {
N.Back
Print N
}
N+=10
N*=2
Print N.CopyValues ' 22 11 1
}
CheckHistoryVariables
\\ for strings
Module CheckStringHistoryVariables {
Class History$ {
Private:
myvalue=stack
Public:
Group Count {
Value {
link parent myvalue to m
=Len(m)
}
}
Function CopyMe {
m=This
.myvalue<=stack(.myvalue)
=Group(m)
}
Group CopyValues {
Value {
link parent myvalue to m
=Stack(m)
}
}
Function Values$ (x as long) {
if x>=0 and x<=len(.myvalue) then =StackItem$(.myvalue, x)
Else Error "Fault index"
}
Module Back {
if len(.myvalue)<2 then exit
Stack .myvalue {Drop}
}
Operator "+=" (x$) {
Stack .myvalue {
Push stackitem$()+x$
} }
Value {
=StackItem$(.myvalue)
}
Set {
Read x$ : Stack .myvalue { Push x$}
}
Class:
Module History {
If Match("S") then Read x$ : Stack .myvalue { Push x$}
}
}
N$=History$("First")
N$="Second"
N$="Third"
Print N.Count=3
M=N.CopyValues
K=Each(M, -1, 1)
While K {
Print StackItem$(K)
}
N$+=" and this"
Print N.Count=4
Print N.CopyValues
N.Back
Print N.Count=3
Print N.CopyValues
Print N.Values$(2)="Second"
Input "New value:", N$
Print N$
Print N.Count=4
Print N.CopyValues
}
CheckStringHistoryVariables
Nim
Nim doesn’t provide history variables, but it is easy to implement them. As Nim is statically typed, we define history variable type in a generic way.
type HistoryVar[T] = object
hist: seq[T]
func initHistoryVar[T](value: T = T.default): HistoryVar[T] =
## Initialize a history variable with given value.
result.hist.add value
func set(h: var HistoryVar; value: h.T) =
## Set the history variable to given value.
h.hist.add value
func get(h: HistoryVar): h.T =
## Return the current value of history variable.
h.hist[^1]
proc showHistory(h: HistoryVar) =
## Show the history starting from oldest values.
for i, value in h.hist:
echo i, ": ", value
func pop(h: var HistoryVar): h.T =
## Pop the current value and return it.
if h.hist.len > 1: h.hist.pop() else: h.hist[0]
when isMainModule:
var h = initHistoryVar[int]() # Initialized to 0.
echo "Assigning three values: 1, 2, 3"
h.set(1) # 0, 1
h.set(2) # 0, 1, 2
h.set(3) # 0, 1, 2, 3
echo "History (oldest values first):"
h.showHistory()
echo "Current value is ", h.get()
echo "Recall the three values:"
echo h.pop() # -> 3, last value removed.
echo h.pop() # -> 2, last value removed.
echo h.pop() # -> 1, last value removed.
echo "History (note that initial value can never be removed):"
h.showHistory()
- Output:
Assigning three values: 1, 2, 3 History (oldest values first): 0: 0 1: 1 2: 2 3: 3 Current value is 3 Recall the three values: 3 2 1 History (note that initial value can never be removed): 0: 0
Oberon-2
MODULE HVar;
IMPORT Out, Conv;
TYPE
(* Generic Object *)
Object* = POINTER TO ObjectDesc;
ObjectDesc = RECORD
Show: PROCEDURE(o:Object);
AsString: PROCEDURE(o: Object; VAR str: ARRAY OF CHAR);
END;
(* Integers *)
Integer = POINTER TO IntegerDesc;
IntegerDesc = RECORD (ObjectDesc)
val: INTEGER;
END;
(* Chars *)
Char = POINTER TO CharDesc;
CharDesc = RECORD (ObjectDesc)
val: CHAR;
END;
Node = POINTER TO NodeDesc;
NodeDesc = RECORD
val: Object;
next: Node;
END;
(* History Variable *)
HVar* = POINTER TO HVarDesc;
HVarDesc = RECORD
first, last: Node;
size-: INTEGER;
END;
PROCEDURE CharAsString(o:Object; VAR dst: ARRAY OF CHAR);
BEGIN
IF LEN(dst) >= 2 THEN
dst[1] := 0X;
END;
dst[0] := o(Char)^.val
END CharAsString;
PROCEDURE IntAsString(o:Object; VAR dst: ARRAY OF CHAR);
BEGIN
Conv.ConvInt(o(Integer)^.val,dst);
END IntAsString;
PROCEDURE ShowInt(o:Object);
BEGIN
Out.Int(o(Integer)^.val,10);
END ShowInt;
PROCEDURE ShowChar(o:Object);
BEGIN
Out.Char(o(Char)^.val);
END ShowChar;
PROCEDURE BoxChar(val: CHAR): Char;
VAR
c: Char;
BEGIN
NEW(c);
c^.val := val;
c^.Show := ShowChar;
c^.AsString := CharAsString;
RETURN c;
END BoxChar;
PROCEDURE BoxInt(val:INTEGER): Integer;
VAR
i: Integer;
BEGIN
NEW(i);
i^.val := val;
i^.Show := ShowInt;
i^.AsString := IntAsString;
RETURN i;
END BoxInt;
PROCEDURE InitNode(): Node;
VAR
l: Node;
BEGIN
NEW(l);
l.val := NIL;
l.next := NIL;
RETURN l;
END InitNode;
PROCEDURE InitHVar(): HVar;
VAR
hv: HVar;
BEGIN
NEW(hv);
hv.first := NIL;
hv.last := NIL;
hv.size := 0;
RETURN hv;
END InitHVar;
PROCEDURE (v: HVar) Value(): Object;
BEGIN
RETURN v^.first^.val;
END Value;
PROCEDURE (v: HVar) Set(o: Object);
VAR
l: Node;
BEGIN
l := InitNode();
l^.val := o;
IF (v^.first = v^.last) & (v^.first = NIL) THEN
v^.first := l;
v^.last := l;
INC(v^.size);
ELSIF (v^.first = v^.last) & (v^.first # NIL) THEN
v^.first^.next := l;
v^.last := v^.first.next;
INC(v^.size);
ELSIF (v^.first # v^.last) THEN
v^.last^.next := l;
v^.last := l;
INC(v^.size);
END
END Set;
PROCEDURE (v: HVar) Undo(): Object;
VAR
o: Object;
BEGIN
IF (v^.first = v^.last) & (v^.first = NIL) THEN
o := NIL;
ELSIF (v^.first = v^.last) & (v^.first # NIL) THEN
o := v^.first^.val;
v^.first := NIL;
v^.last := NIL;
DEC(v^.size);
ELSE
o := v^.first^.val;
v^.first := v^.first^.next;
DEC(v^.size);
END;
RETURN o;
END Undo;
PROCEDURE (v: HVar) Print();
VAR
iter : Node;
BEGIN
iter := v.first;
WHILE(iter # NIL) DO
iter^.val^.Show(iter^.val);
iter := iter^.next;
END;
Out.Ln;
END Print;
PROCEDURE ShowVal(val: Object);
VAR
s: ARRAY 128 OF CHAR;
BEGIN
val^.AsString(val,s);
Out.String("> ");Out.String(s);Out.Ln;
END ShowVal;
VAR
history: HVar;
BEGIN
history := InitHVar();
history.Set(BoxChar(64X));
history.Set(BoxInt(10));
history.Set(BoxInt(15));
history.Set(BoxInt(20));
history.Print();
ShowVal(history.Undo());
ShowVal(history.Undo());
ShowVal(history.Undo());
END HVar.
OCaml
The easiest solution is to use the Stack module coming with OCaml's standard library:
open Stack
(* The following line is only for convenience when typing code *)
module H = Stack
let show_entry e =
Printf.printf "History entry: %5d\n" e
let () =
let hs = H.create() in
H.push 111 hs ;
H.push 4 hs ;
H.push 42 hs ;
H.iter show_entry hs;
hs |> H.pop |> Printf.printf "%d\n";
hs |> H.pop |> Printf.printf "%d\n";
hs |> H.pop |> Printf.printf "%d\n"
- Output:
History entry: 42 History entry: 4 History entry: 111 42 4 111
OxygenBasic
Simple history class for fixed length types that do not contain volatile pointer members.
'============
class History
'============
indexbase 0
string buf
sys ii,ld,pb
method constructor(sys n=1000, l=sizeof sys) {buf=nuls n*l : pb=strptr buf : ld=l : ii=0}
method destructor () {clear}
'
method setup(sys n=1000, l=sizeof sys) {buf=nuls n*l : pb=strptr buf : ld=l : ii=0}
method clear() {buf="" : pb=0 : ld=0 : ii=0}
method max (sys i) {if i>ii{ii=i}}
method count() as sys {return ii}
method size () as sys {return ld}
'
method get (any*p,i) {copy @p, pb+i*ld,ld } 'out
method add (any*p) {copy pb+ii*ld,@p,ld : ii++} 'in
method put (any*p,sys i) {copy pb+i*ld,@p,ld : max i} 'in
'
end class
'====
'TEST
'====
'this works for fixed length types
'it will not work for types containing
'volatile pointers. eg: string members
type vector double x,y,z
vector v
new History hv(1000,sizeof v) 'give number of records and variable size
sys i
for i=0 to 9
v<=i,i*10,i*100 'assign new values to vector
hv.add v 'add to history
next
string tab=chr(9) : cr=chr(13)+chr(10)
string pr="Data History of v" cr cr
pr+="n" tab "x" tab "y" tab "z" cr
vector sv
'
for i=hv.count()-1 to 0 step -1
hv.get sv,i
pr+=i tab sv.x tab sv.y tab sv.z cr
next
print pr 'result '9,90,900 : 8,80,800 ...
del hv
PARI/GP
default(histsize, 1000) \\ or some other positive number to suit
1+7
sin(Pi)
2^100
\a1 \\ display history item #1, etc.
% \\ alternate syntax
%1 \\ alternate syntax
\a2
\a3
[%1, %2, %3] \\ or any other command using these values
Peloton
Turn history on <@ DEFHST>__on</@>
Notify Protium we are interested in the variable mv
<@ DEFHST>mv</@>
Assign a value: <@ LETVARLIT>mv|first value</@><@ SAYVAR>mv</@>
Reassign the value: <@ LETVARLIT>mv|second value</@><@ SAYVAR>mv</@>
Reassign the value: <@ LETVARLIT>mv|third value</@><@ SAYVAR>mv</@>
Dump history <@ SAYDMPHSTVAR>mv</@>
Current value: <@ SAYVAR>mv</@>
Undo once: <@ ACTUNDVAR>mv</@><@ SAYVAR>mv</@>
Undo twice: <@ ACTUNDVAR>mv</@><@ SAYVAR>mv</@>
Turn history off <@ DEFHST>__off</@>
Same code, Simplified Chinese dialect
Turn history on <# 定义变量史>__on</#>
Notify Protium we are interested in the variable mv
<# 定义变量史>mv</#>
Assign a value: <# 指定变量字串>mv|first value</#><# 显示变量>mv</#>
Reassign the value: <# 指定变量字串>mv|second value</#><# 显示变量>mv</#>
Reassign the value: <# 指定变量字串>mv|third value</#><# 显示变量>mv</#>
Dump history <# 显示全内容变量史变量>mv</#>
Current value: <# 显示变量>mv</#>
Undo once: <# 运行撤消变量>mv</#><# 显示变量>mv</#>
Undo twice: <# 运行撤消变量>mv</#><# 显示变量>mv</#>
Turn history off <# 定义变量史>__off</#>
- Output:
Turn history on Notify Protium we are interested in the variable mv Assign a value: first value Reassign the value: second value Reassign the value: third value Dump history third value^second value^first value^ Current value: third value Undo once: second value Undo twice: first value Turn history off
Perl
Implemented via tie (and what's the usefulness of this?)
package History;
sub TIESCALAR {
my $cls = shift;
my $cur_val = shift;
return bless [];
}
sub FETCH {
return shift->[-1]
}
sub STORE {
my ($var, $val) = @_;
push @$var, $val;
return $val;
}
sub get(\$) { @{tied ${+shift}} }
sub on(\$) { tie ${+shift}, __PACKAGE__ }
sub off(\$) { untie ${+shift} }
sub undo(\$) { pop @{tied ${+shift}} }
package main;
my $x = 0;
History::on($x);
for ("a" .. "d") { $x = $_ }
print "History: @{[History::get($x)]}\n";
for (1 .. 3) {
print "undo $_, ";
History::undo($x);
print "current value: $x\n";
}
History::off($x);
print "\$x is: $x\n";
- Output:
History: a b c d
undo 1, current value: c
undo 2, current value: b
undo 3, current value: a
$x is: a
Phix
No native support, but trivial to implement.
If you only need the history for a single variable, you can just do this, though it does not work under pwa/p2js:
without js -- (desktop/Phix only) sequence history = {} type hvt(object o) history = append(history,o) return true end type hvt test = 1 test = 2 test = 3 ?{"current",test} ?{"history",history}
- Output:
{"current",3} {"history",{1,2,3}}
Multiple history variables would require that routines must be invoked to create, update, and inspect them, and would be pwa/p2js compatible.
Writing this as a separate reusable component (but omitting destroy/freelist handling for simplicity):
-- history.e sequence histories = {} global function new_history_id(object v) histories = append(histories,{v}) return length(histories) end function global procedure set_hv(integer hv, object v) histories[hv] = append(histories[hv],v) end procedure global function get_hv(integer hv) return histories[hv][$] end function global function get_hv_full_history(integer hv) return histories[hv] end function
And use it like this
include history.e constant test2 = new_history_id(1) set_hv(test2, 2) set_hv(test2, 3) ?{"current",get_hv(test2)} ?{"history",get_hv_full_history(test2)}
Same output. Of course test2 does not have to be a constant, but it may help.
PicoLisp
(de setH ("Var" Val)
(when (val "Var")
(with "Var"
(=: history (cons @ (: history))) ) )
(set "Var" Val) )
(de restoreH ("Var")
(set "Var" (pop (prop "Var" 'history))) )
Test:
: (setH 'A "Hello world") -> "Hello world" : (setH 'A '(a b c d)) -> (a b c d) : (setH 'A 123) -> 123 : A -> 123 : (get 'A 'history) -> ((a b c d) "Hello world") : (restoreH 'A) -> (a b c d) : (restoreH 'A) -> "Hello world" : A -> "Hello world" : (restoreH 'A) -> NIL
PL/I
declare t float controlled;
do i = 1 to 5; /* a loop to read in and save five values. */
allocate t; get (t);
end;
do while (allocation(t) > 0); /* a loop to retrieve the values. */
put (t); free t;
end;
PureBasic
; integer history variable
Structure historyint
List value.i()
EndStructure
Procedure SetInt (*var.historyint, val.i)
AddElement(*var\value())
If (ListSize(*var\value()) = 1)
*var\value() = 0
AddElement(*var\value())
EndIf
*var\value() = val
EndProcedure
Procedure ShowHistory (*var.historyint)
ForEach *var\value()
Debug *var\value()
Next
EndProcedure
Procedure UndoInt (*var.historyint)
If (ListSize(*var\value()) = 1)
ProcedureReturn
EndIf
DeleteElement(*var\value())
EndProcedure
;----------------------------------------------
Define x.historyint
For i = 0 To 3
setint(x, Random(50)+100 )
Next
Debug "x history:"
ShowHistory(x)
Debug ""
For i = 0 To 3
UndoInt(x)
Debug "undo, x = "+Str(x\value())
Next
- Output:
x history: 0 131 109 143 108 undo, x = 143 undo, x = 109 undo, x = 131 undo, x = 0
Python
import sys
HIST = {}
def trace(frame, event, arg):
for name,val in frame.f_locals.items():
if name not in HIST:
HIST[name] = []
else:
if HIST[name][-1] is val:
continue
HIST[name].append(val)
return trace
def undo(name):
HIST[name].pop(-1)
return HIST[name][-1]
def main():
a = 10
a = 20
for i in range(5):
c = i
print "c:", c, "-> undo x3 ->",
c = undo('c')
c = undo('c')
c = undo('c')
print c
print 'HIST:', HIST
sys.settrace(trace)
main()
- Output:
c: 4 -> undo x3 -> 1
HIST: {'a': [10, 20], 'i': [0, 1, 2, 3, 4], 'c': [0, 1], 'name': ['c']}
Quackery
Quackery does not have variables, it has ancillary stacks that can serve, amongst their uses, as variables and as history variables. Given an ancillary stack x
, the following Quackery words are relevant.
x put
moves an item from the data stack to the ancillary stackx
.x take
moves an item from the ancillary stackx
to the data stack.x share
copies the top of the ancillary stackx
to the data stack.x replace
replaces the top item on the ancillary stackx
with the item on top of the data stack.x behead drop
leaves a copy of the ancillary stackx
on the data stack as a nest (dynamic array).
The requirements of this task demonstrated as a dialogue in the Quackery shell.
/O> [ stack ] is x ... 3 x put ... 4 x put ... 5 x put ... x behead drop echo cr ... x take echo cr ... x take echo cr ... x take echo cr ... [ 3 4 5 ] 5 4 3
Racket
Racket does not come with history variables, but they can be provided as a library as follows:
#lang racket
(require rackunit)
(struct hvar (current history) #:mutable)
(define (make-hvar v) (hvar v empty))
(define (hvar-set! hv new)
(match-define (hvar cur hist) hv)
(set-hvar-history! hv (cons cur hist))
(set-hvar-current! hv new))
(define (hvar-undo! hv)
(match-define (hvar cur (cons old hist)) hv)
(set-hvar-current! hv old)
(set-hvar-history! hv hist))
;; unit tests
(define hv (make-hvar 0))
(hvar-set! hv 1)
(check-equal? (hvar-current hv) 1)
(hvar-set! hv 2)
(hvar-set! hv 3)
(check-equal? (hvar-history hv) '(2 1 0))
(hvar-undo! hv)
(hvar-undo! hv)
(hvar-undo! hv)
(check-equal? (hvar-current hv) 0)
Raku
(formerly Perl 6)
class HistoryVar {
has @.history;
has $!var handles <Str gist FETCH Numeric>;
method STORE($val) is rw {
push @.history, [now, $!var];
$!var = $val;
}
}
my \foo = HistoryVar.new;
foo = 1;
foo = 2;
foo += 3;
foo = 42;
.say for foo.history;
say "Current value: {foo}";
- Output:
[Instant:1523396079.685629 (Any)] [Instant:1523396079.686844 1] [Instant:1523396079.687130 2] [Instant:1523396079.687302 5] Current value: 42
REXX
Version 1
The REXX language doesn't support histories, but can be coded with little trouble.
The history list part of the  ' varSet subroutine could be separated into its
own if you wanted to keep the subroutine's function pure.
/*REXX program demonstrates a method to track history of assignments to a REXX variable.*/
varSet!.=0 /*initialize the all of the VARSET!'s. */
call varSet 'fluid',min(0,-5/2,-1) ; say 'fluid=' fluid
call varSet 'fluid',3.14159 ; say 'fluid=' fluid
call varSet 'fluid',' Santa Claus' ; say 'fluid=' fluid
call varSet 'fluid',,999
say 'There were' result "assignments (sets) for the FLUID variable."
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
varSet: arg ?x; parse arg ?z, ?v, ?L /*obtain varName, value, optional─List.*/
if ?L=='' then do /*not la ist, so set the X variable.*/
?_=varSet!.0.?x+1 /*bump the history count (# of SETs). */
varSet!.0.?x=?_ /* ... and store it in the "database"*/
varSet!.?_.?x=?v /* ... and store the SET value. */
call value(?x),?v /*now, set the real X variable. */
return ?v /*also, return the value for function. */
end
say /*show a blank line for readability. */
do ?j=1 to ?L while ?j<=varSet!.0.?x /*display the list of "set" history. */
say 'history entry' ?j "for var" ?z":" varSet!.?J.?x
end /*?j*/
return ?j-1 /*return the number of assignments. */
output
fluid= -2.5 fluid= 3.14159 fluid= Santa Claus history entry 1 for var fluid: -2.5 history entry 2 for var fluid: 3.14159 history entry 3 for var fluid: Santa Claus There were 3 assignments (sets) for the FLUID variable.
Version 2
/* REXX ***************************************************************
* Demonstrate how the history of assignments can be kept and shown
* 13.07.2012 Walter Pachl Rewrite of Version 1 for (my) readability
* varset.i.varu contains the ith value assigned to var
* varset.0.varu contains the number of assignments so far
**********************************************************************/
varset.=0 /*initialize the assignment count */
call varset 'fluid',min(0,-5/2,-1) ; say 'fluid=' fluid
call varset 'fluid',3.14159 ; say 'fluid=' fluid
call varset 'fluid',3.14159 ; say 'fluid=' fluid
call varset 'fluid',' Santa Claus' ; say 'fluid=' fluid
call varset 'FLUID',' Easter Rabbit' ; say 'fluid=' fluid
say 'There were' varset('fluid',,'L'),
'assignments (sets) for the FLUID variable.'
exit
varset: Procedure Expose varset.
/**********************************************************************
* record values assigned to var (=varu as Rexx is case insensitive)
* Invoke with list<>'' to list the sequence of assigned values
* and return the number of assignments made (using this routine)
**********************************************************************/
Parse Upper Arg varu /* name of variable in uppercase */
Parse arg var,value,list /*varName, value, optional-List. */
if list='' then do /*not list, so set the X variable*/
z=varset.0.varu+1 /*bump the history count (SETs). */
varset.z.varu=value /* ... and store it in "database"*/
varset.0.varu=z /*the new history count */
call value var,value /*now assign the value to var */
end
else Do
Say '' /*show blank line for readability*/
do i=1 to varset.0.varu /*list the assignment history */
say 'history entry' i "for var" var":" varset.i.varu
end
end
Return varset.0.varu /*return the number of assignments. */
Ruby
This uses trace_var, which only works for global variables:
foo_hist = []
trace_var(:$foo){|v| foo_hist.unshift(v)}
$foo = "apple"
$foo = "pear"
$foo = "banana"
p foo_hist # => ["banana", "pear", "apple"]
Rust
#[derive(Clone, Debug)]
struct HVar<T> {
history: Vec<T>,
current: T,
}
impl<T> HVar<T> {
fn new(val: T) -> Self {
HVar {
history: Vec::new(),
current: val,
}
}
fn get(&self) -> &T {
&self.current
}
fn set(&mut self, val: T) {
self.history.push(std::mem::replace(&mut self.current, val));
}
fn history(&self) -> (&[T], &T) {
(&self.history, &self.current)
}
fn revert(&mut self) -> Option<T> {
self.history
.pop()
.map(|val| std::mem::replace(&mut self.current, val))
}
}
fn main() {
let mut var = HVar::new(0);
var.set(1);
var.set(2);
println!("{:?}", var.history());
println!("{:?}", var.revert());
println!("{:?}", var.revert());
println!("{:?}", var.revert());
println!("{:?}", var.get());
}
- Output:
([0, 1], 2) Some(2) Some(1) None 0
Scala
Scala doesn't have a native support for history variables, but it's quite easy to implement them. The following class uses same conventions as ML's mutable reference cells. (i.e. !
as accessor, and :=
as mutator.)
class HVar[A](initialValue: A) extends Proxy {
override def self = !this
override def toString = "HVar(" + !this + ")"
def history = _history
private var _history = List(initialValue)
def unary_! = _history.head
def :=(newValue: A): Unit = {
_history = newValue :: _history
}
def modify(f: A => A): Unit = {
_history = f(!this) :: _history
}
def undo: A = {
val v = !this
_history = _history.tail
v
}
}
Usage:
scala> val h = new HVar(3)
h: HVar[Int] = HVar(3)
scala> h := 11
scala> h := 90
scala> !h
res32: Int = 90
scala> h.history
res33: List[Int] = List(90, 11, 3)
scala> h.undo
res34: Int = 90
scala> h.undo
res35: Int = 11
scala> h.undo
res36: Int = 3
SenseTalk
SenseTalk does not have native support for history variables, but here is a simple object that can be instantiated to provide a history:
// HistoryVariable.script
properties
history: [], -- a list of all historical values
asTextFormat:"[[the last item of my history]]" -- always display the last value
end properties
to set newValue
push newValue nested into my history
end set
to rollback
pop my history
return it
end rollback
Here is an example how this could be used (note that variables in SenseTalk may hold any type of value):
set x to be a new HistoryVariable
tell x to set "Hello"
put "x is now" && x -- display the current value
tell x to set 88
put "x is now" && x
tell x to set "World"
put "x is now" && x
put "History of x:" && x's history -- non-destructively display the history
tell x to set [9,6,3] -- set it to a list
put "x is now" && x
put "History of x:" && the history of x
put "Rolling back:" && x.rollback -- remove the last value
put "After rollback, x is now" && x
- Output:
x is now Hello x is now 88 x is now World History of x: ["Hello",88,"World"] x is now [9,6,3] History of x: ["Hello",88,"World",[9,6,3]] Rolling back: [9,6,3] After rollback, x is now World
Sidef
Implemented as a class:
class HistoryVar(v) {
has history = []
has variable = v
method ≔(value) {
history << variable
variable = value
}
method to_s {
"#{variable}"
}
method AUTOLOAD(_, name, *args) {
variable.(name)(args...)
}
}
var foo = HistoryVar(0)
foo ≔ 1
foo ≔ 2
foo ≔ foo+3
foo ≔ 42
say "History: #{foo.history}"
say "Current value: #{foo}"
- Output:
History: [0, 1, 2, 5] Current value: 42
Smalltalk
Smalltalk doesn't have native support for history variables. It could be implemented using (implementation dependent) tracing facilities, or by changing the code generator (which is part of the library and open for change). The following implementation is portable and in the spirit of the Lisp implementation above, and defines a new "assignment operator":
Object subclass:'HVar'
instanceVariableNames:'values'
classVariableNames:''
poolDictionaries:''
category:'example'.
!
!HVar methodsFor:'accessing'!
<-- value
(values ifNil:[values := OrderedCollection new]) add:value.
^ value
!
value
^ values last
!
undo
values removeLast.
!
history
^ history
! !
|x|
x := HVar new.
x <-- 1.
x value.
x <-- 2.
x <-- (x value + 1).
x value.
x history.
Swift
Swift does not support history variables. However, you can add a watcher that can track when the variable will change.
var historyOfHistory = [Int]()
var history:Int = 0 {
willSet {
historyOfHistory.append(history)
}
}
history = 2
history = 3
history = 4
println(historyOfHistory)
another approach, using generics: plug this code into a Playground to see the output
struct History <T> {
private var _history = [T]()
var history : [T] {
return _history
}
var now : T {
return history.last!
}
init(_ firstValue:T) {
_history = [firstValue]
}
mutating func set(newValue:T) {
_history.append(newValue)
}
mutating func undo() -> T {
guard _history.count > 1 else { return _history.first! }
_history.removeLast()
return _history.last!
}
}
var h = History("First")
h.set("Next")
h.set("Then")
h.set("Finally")
h.history // output ["First", "Next", "Then", "Finally"]
h.now // outputs "Finally"
h.undo() // outputs "Then"
h.undo() // outputs "Next"
h.undo() // outputs "First"
h.undo() // outputs "First", since it can't roll back any further
h.undo() // outputs "First"
Tcl
Though Tcl's variables don't have history by default, it can be added easily through the use of traces:
# Define the history machinery
proc histvar {varName operation} {
upvar 1 $varName v ___history($varName) history
switch -- $operation {
start {
set history {}
if {[info exist v]} {
lappend history $v
}
trace add variable v write [list histvar.write $varName]
trace add variable v read [list histvar.read $varName]
}
list {
return $history
}
undo {
set history [lrange $history 0 end-1]
}
stop {
unset history
trace remove variable v write [list histvar.write $varName]
trace remove variable v read [list histvar.read $varName]
}
}
}
proc histvar.write {key varName args} {
upvar 1 $varName v ___history($key) history
lappend history $v
}
proc histvar.read {key varName args} {
upvar 1 $varName v ___history($key) history
set v [lindex $history end]
}
Demonstrating how to use it:
# Enable history for foo
histvar foo start
set foo {a b c d}
set foo 123
set foo "quick brown fox"
puts $foo
puts foo-history=[join [histvar foo list] ", "]
puts $foo
histvar foo undo
puts $foo
histvar foo undo
puts $foo
histvar foo stop
- Output:
quick brown fox foo-history=a b c d, 123, quick brown fox quick brown fox 123 a b c d
Wren
Not built in but we can soon make a suitable class.
class HistoryVariable {
construct new(initValue) {
_history = [initValue]
}
currentValue { _history[-1] }
currentValue=(newValue) { _history.add(newValue) }
showHistory() {
System.print("The variable's history, oldest values first, is:")
for (item in _history) System.print(item)
}
}
var v = HistoryVariable.new(1)
v.currentValue = 2
v.currentValue = 3
v.showHistory()
System.print("\nCurrent value is %(v.currentValue)")
- Output:
The variable's history, oldest values first, is: 1 2 3 Current value is 3
zkl
No native support, here is a something that can be done with a class:
class HistoryVar{
var [private] _v, _history=List(), maxSz;
fcn init(v,maxEntries=3){ maxSz=maxEntries; set(v) }
fcn set(v){
_v=v; _history.append(T(Time.Clock.time,v));
if(_history.len()>maxSz) _history.del(0);
self
}
fcn get(n=0){ // look back into history
z:=_history.len();
n=(if(n>=z) 0 else z-n-1);
_history[n][1]
}
var [proxy] v=fcn{ _v };
var [proxy] history=
fcn{ _history.pump(List,fcn([(t,v)]){ T(Time.Date.ctime(t),v) }) };
fcn __opAdd(x){ set(_v + x); self }
}
hv:=HistoryVar(123);
hv+4;
hv.set("Shuttle prepared for liftoff");
hv+": orbit achived";
hv.history.concat("\n").println();
hv.get(3).println("<-- value two changes ago");
- Output:
L("Thu Oct 13 13:48:37 2016",127) L("Thu Oct 13 13:48:37 2016","Shuttle prepared for liftoff") L("Thu Oct 13 13:48:37 2016","Shuttle prepared for liftoff: orbit achived") 127<-- value two changes ago
- Programming Tasks
- Solutions by Programming Task
- 68000 Assembly
- Ada
- ALGOL W
- Arturo
- AspectJ
- AutoHotkey
- C sharp
- C++
- Clojure
- Common Lisp
- D
- Delphi
- System.SysUtils
- EchoLisp
- Elena
- Erlang
- Factor
- Forth
- Go
- Haskell
- J
- Java
- Julia
- Kotlin
- Lua
- M2000 Interpreter
- Nim
- Oberon-2
- OCaml
- OxygenBasic
- PARI/GP
- Peloton
- Perl
- Phix
- PicoLisp
- PL/I
- PureBasic
- Python
- Quackery
- Racket
- Raku
- REXX
- Ruby
- Rust
- Scala
- SenseTalk
- Sidef
- Smalltalk
- Swift
- Tcl
- Wren
- Zkl
- 8086 Assembly/Omit
- 80386 Assembly/Omit
- AWK/Omit
- BASIC/Omit
- Batch File/Omit
- C/Omit
- C++/Omit
- GUISS/Omit
- Locomotive Basic/Omit
- Lotus 123 Macro Scripting/Omit
- Lua/Omit
- ML/I/Omit
- UNIX Shell/Omit
- Visual Basic/Omit
- ZX Spectrum Basic/Omit
- Déjà Vu/Omit