History variables

From Rosetta Code
Revision as of 12:21, 28 July 2011 by rosettacode>Stevek (adding python)
Task
History variables
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.

"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.

J

J does not natively support "history variables", but the functionality is easy to add:

<lang j>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</lang>

Example use:

<lang j> '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│ └─┴──┴──┘</lang>

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.

PARI/GP

<lang parigp>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</lang>

Perl

Implemented via tie (and what's the usefulness of this?) <lang Perl>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";</lang>Output<lang>History: a b c d undo 1, current value: c undo 2, current value: b undo 3, current value: a $x is: a</lang>

Python

<lang 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()</lang>Output<lang>c: 4 -> undo x3 -> 1 HIST: {'a': [10, 20], 'i': [0, 1, 2, 3, 4], 'c': [0, 1], 'name': ['c']}</lang>

PicoLisp

<lang 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))) )</lang>

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

Protium

<lang protium>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</@></lang>

Same code, Simplified Chinese dialect <lang protium>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</#> </lang>

Sample 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  

Tcl

Though Tcl's variables don't have history by default, it can be added easily through the use of traces: <lang tcl># 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]

}</lang> Demonstrating how to use it: <lang tcl># 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</lang> Output:

quick brown fox
foo-history=a b c d, 123, quick brown fox
quick brown fox
123
a b c d