[comp.lang.perl] Recursive types in Perl.

flee@dictionopolis.cs.psu.edu (Felix Lee) (11/28/90)

Below, recurse.pl, a simulation of recursive types in Perl.

recurse.pl introduces Vectors and Tables, which are mostly just
encapsulations of Perl's lists and associative arrays.  Vectors and
Tables are represented by strings prefixed by a magic cookie.  Any
random string that just happens to have that magic cookie may be
mistaken for a Vector or a Table with strange results.  This is an
unavoidable flaw.  Hopefully, this will happen rarely.

I don't have any sample code that uses Vectors and Tables, because I
don't seem to have a use for them right now.  I'm sure I used to have
one, but it seems to have dropped by the wayside.  Well, there's
Scheme-in-Perl? (sp?), but that's not quite a sample piece of code.

sp? was useful experience in building recurse.pl.  recurse.pl fell
into place quite quickly.  It's very different from what I might have
done a month ago.

Given Vectors, you can build true multidimensional arrays, instead of
using the $; hack.  Unfortunately, it's likely to be much slower (Perl
subroutine call overhead is quite large).  But you can easily slice
the multidimensional array, which the $; hack doesn't let you do.
--
Felix Lee	flee@cs.psu.edu

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  recurse.pl
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'recurse.pl' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'recurse.pl'\"
else
echo shar: Extracting \"'recurse.pl'\" \(4666 characters\)
sed "s/^X//" >'recurse.pl' <<'END_OF_FILE'
X#!/usr/bin/perl
X
X# Recursive types in Perl.  $Revision: 1.1 $
X
X# This is an implementation of two recursive types in Perl:
X# Vectors and Tables.
X
X# Vectors and Tables can be carried around like any scalar
X# value in Perl.  They're distinguished from other scalar
X# values by a special prefix,
X#	"\0*v-" for Vectors, and
X#	"\0*t-" for Tables.
X# If you happen to have a string that starts with one of
X# those values, then it may be misinterpreted.  This is an
X# unavoidable flaw.
X
X# XXX assumes you haven't mucked with $[.
X
Xpackage recurse;
X
X# $recurse'error
X#   Subroutine to call when a typecheck error occurs.
X
X$error = "recurse'die_with_prejudice";
X
Xsub recurse'die_with_prejudice {
X	local($where, $what) = @_;
X	die "** recurse: invalid arg to $where: $what\n";
X}
X
X
X# &isV($x)
X#   Return 1 if $x is a Vector, 0 otherwise.
X
Xsub main'isV {
X	@_[0] =~ /^\0\*v-/ || 0;
X}
X
X# &V(@value)
X#   Create a new Vector with the given value.
X
Xsub main'V {
X	local(*v) = local($z) = @ZV ? pop @ZV : "ZV" . ++$ZV;
X	@v = @_;
X	"\0*v-" . $z;
X}
X
X# &Vref("name")
X#   Create a Vector that's a reference to @name.
X
Xsub main'Vref {
X	if (@_[0] =~ /'/) {
X		"\0*v-" . @_[0];
X	} else {
X		"\0*v-" . (caller)[0] . "'" . @_[0];
X	}
X}
X
X# &Vfree($v)
X#   Reclaim the storage occupied by Vector v.
X
Xsub main'Vfree {
X	return do $error('Vfree', @_[0]) if @_[0] !~ /^\0\*v-/;
X	local($z) = $';
X	push(@ZV, $z) if $z =~ /^ZV\d/;
X	local(*v) = $z;
X	@v = ();
X}
X
X# &Vval($v)
X#   Return Vector v as a list.
X#   (In a scalar context, return the length.)
X# &Vval($v, $index, ...)
X#   Return a slice of Vector v.
X
Xsub main'Vval {
X	return do $error('Vval', @_[0]) if @_[0] !~ /^\0\*v-/;
X	local(*v) = $';
X	shift @_;
X	@_ ? @v[@_] : @v;
X}
X
X# &Vset($v, ($index, $value), ...)
X#   Set some values in Vector v.
X
Xsub main'Vset {
X	return do $error('Vset', @_[0]) if @_[0] !~ /^\0\*v-/;
X	local(*v) = $';
X	shift @_;
X	@v[shift @_] = shift @_ while @_;
X}
X
X# &Vsplice($v, $start, $length, $value, ...)
X#   Replace a segment of Vector v with the given values
X#   and return the old value.
X
Xsub main'Vsplice {
X	return do $error('Vsplice', @_[0]) if @_[0] !~ /^\0\*v-/;
X	local(*v) = $';
X	shift @_;
X	splice(@v, @_);
X}
X
X# &Vpush($v, $value, ...)
X#   Append the given values to Vector v.
X
Xsub main'Vpush {
X	return do $error('Vpush', @_[0]) if @_[0] !~ /^\0\*v-/;
X	local(*v) = $';
X	shift @_;
X	push(@v, @_);
X}
X
X# &Vpop($v)
X#   Remove and return the last element of Vector v.
X
Xsub main'Vpop {
X	return do $error('Vpop', @_[0]) if @_[0] !~ /^\0\*v-/;
X	local(*v) = $';
X	pop @v;
X}
X
X# &Vshift($v)
X#   Remove and return the first element of Vector v.
X
Xsub main'Vshift {
X	return do $error('Vshift', @_[0]) if @_[0] !~ /^\0\*v-/;
X	local(*v) = $';
X	shift @v;
X}
X
X# &Vunshift($v, $value, ...)
X#   Prepend the given values to Vector v.
X
Xsub main'Vunshift {
X	return do $error('Vunshift', @_[0]) if @_[0] !~ /^\0\*v-/;
X	local(*v) = $';
X	shift @_;
X	unshift(@v, @_);
X}
X
X
X
X# &isT($x)
X#   Return 1 if $x is a Table, 0 otherwise.
X
Xsub main'isT {
X	@_[0] =~ /\0\*t-/ || 0;
X}
X
X# &T(%value)
X#   Return a new Table with the given value.
X
Xsub main'T {
X	local(*t) = local($z) = @ZT ? pop @ZT : "\ZT" . ++$ZT;
X	%t = @_;
X	"\0*t-" . $z;
X}
X
X# &Tref("name")
X#   Return a Table that's a reference to %name.
X
Xsub main'Tref {
X	if (@_[0] =~ /'/) {
X		"\0*t-" . @_[0];
X	} else {
X		"\0*t-" . (caller)[0] . "'" . @_[0];
X	}
X}
X
X# &Tfree($t)
X#   Reclaim the storage occupied by Table t.
X
Xsub main'Tfree {
X	return do $error('Tfree', @_[0]) if @_[0] !~ /^\0\*t-/;
X	local($z) = $';
X	push(@ZT, $z) if $z =~ /^ZT\d/;
X	local(*t) = $z;
X	%t = ();
X}
X
X# &Tset($t, ($key, $value), ...)
X#   Set some (key, value) pairs in Table t.
X
Xsub main'Tset {
X	return do $error('Tset', @_[0]) if @_[0] !~ /^\0\*t-/;
X	local(*t) = $';
X	shift @_;
X	$t{shift @_} = shift @_ while @_;
X}
X
X# &Tdelete($t, $key, ...)
X#   Delete some pairs from Table t.
X
Xsub main'Tdelete {
X	return do $error('Tdelete', @_[0]) if @_[0] !~ /^\0\*t-/;
X	local(*t) = $';
X	shift @_;
X	delete $t{shift @_} while @_;
X}
X
X# &Tval($t)
X#   Return Table t as a list.
X# &Tval($t, $key, ...)
X#   Return a slice of Table t.
X
Xsub main'Tval {
X	return do $error('Tval', @_[0]) if @_[0] !~ /^\0\*t-/;
X	local(*t) = $';
X	shift @_;
X	@_ ? @t{@_} : %t;
X}
X
X# &Tkeys($t)
X#   Return a list of the keys of Table t.
X
Xsub main'Tkeys {
X	return do $error('Tkeys', @_[0]) if @_[0] !~ /^\0\*t-/;
X	local(*t) = $';
X	keys %t;
X}
X
X# &Tvalues($t)
X#   Return a list of the values of Table t.
X
Xsub main'Tvalues {
X	return do $error('Tvalues', @_[0]) if @_[0] !~ /^\0\*t-/;
X	local(*t) = $';
X	values %t;
X}
X
X# &Teach($t)
X#   Return the next (key, value) pair from Table t.
X
Xsub main'Teach {
X	return do $error('Teach', @_[0]) if @_[0] !~ /^\0\*t-/;
X	local(*t) = $';
X	each %t;
X}
X
X1;END_OF_FILE
if test 4666 -ne `wc -c <'recurse.pl'`; then
    echo shar: \"'recurse.pl'\" unpacked with wrong size!
fi
# end of 'recurse.pl'
fi
echo shar: End of shell archive.
exit 0