Pretty Format Report Nested Objects Insanity

  • Thread starter Xiong Changnian
  • Start date
X

Xiong Changnian

I am handling a number of complex objects -- the best way to build them
seems to be to have some of the HoH values be other objects. On the
other hand, some values just belong to the given object itself -- I call
these "whole-object data". (There is a graph theory slant to this but
it's not all about that. There's a game theory side, too.)

For example, a Map includes both Node and Edge objects. It also has data
that pertains directly to the Map -- for instance, it's NAME. I put all
the whole-object data in a hash with the key SELF. So, the name itself
would be accessed with:

$the_name = $somemap->{SELF}{NAME};

or, by method:

$the_name = $somemap->name;

Of course, the "name" sub uses the first syntax. I actually have only
one such sub; it's defined in the superclass. Every one of my objects
has a second-level hash key {SELF}{NAME}, so one accessor method serves
all.

Going down to an Edge object, it contains, as always, a SELF hash with a
NAME key, a COLOR key, and so forth. All this data pertains to the whole
object. The Edge also contains a primary key (on the same level as SELF)
called MATRIX; this points to an AoA (for reasons I'd hope are obvious).

So far, so fine. I have some accessor methods defined in the superclass
to which both Map and Edge (as well as others) belong, such as methods
to get at NAME and COLOR -- properties common to most of my objects. I
have other accessors defined in their respective
classes/packages/modules/files, such as "between":

$boolean = $someedge->between($i, $j);

which hits:

return $self-->{MATRIX}[$i][$j];

So far, so fine. To save these objects to disk, I use Data::Dumper. It's
not entirely compact but with indent and quotes turned off it's okay.
Best of all, I load back into the object simply by eval-ing the
datafile. Neither my Save nor my Load routine need to know anything at
all about the actual structure, so I can fiddle around with it freely.
It's even possible (though risky) to load a datafile created by an
earlier version of my script.

Data::Dumper works pretty well for this purpose but its output is not
especially readable; I can trade disk space for more readability by
setting Indent = 1 or 2 but matrices span hundreds of file lines that
way. So, I need to build a routine to display my Map object (hopefully
reusably, any of my objects) by printing to a report file in a pretty
format.

Ideally, I don't want to have to rewrite the report routine every time I
upgrade the object! The stupid way would be to have a rigid procedure in
which I took one thing at a time from the object and printed it. This
would depend on the structure of the object never changing -- bad.

My current approach is better. I do this (at several levels):

sub report_sets {
my @keys = sort keys %{ $ref->{SETS} };
foreach (@keys) {
&{"report_" . lc($_)} ($ref->{SETS}{$_})
or die ("No report subroutine for $_.");
};
};

So, I have a bunch of little subs, each of which is able to pretty
format a particular kind of data, each of which is named after the hash
key that points to that kind of data. These dig their way down until
they hit a sub that prints out one small chunk of data.

*THREE* problems:

(1) The sort keys operation puts, for example, MATRIX before SELF. For
readability in the report, I'd prefer a definite, predefined order for
the various subobjects and substructures -- I'd rather see the
whole-object data first before the big matrix. I suppose I can do this
with a custom sort routine.

(2) That there is a call to a subroutine by symbolic reference. To make
it work, I have to permit

no strict "refs";

for the duration of these shenanigans. I feel that's Bad in general; a
risky operation. Twenty years ago, I recall implementing a jump table
(in 8085 assembler) by constructing the destination address and shoving
it directly into the program counter via PCHL. Needless to say, it was
the first thing that broke.

(3) This seems to lead to a great number of very similar subs. This
suggests that I'm doing something stupidly (that is, not lazy enough).

My *thought* is to declare every level in the data structure as a
well-defined object, so Map will consist of a Self object, a Sets object
that contains ( Node objects which each contain a Self object ) and (
Edge objects which each contain a Self object and a Matrix object) and
so on. They all inherit from a superclass and Self, say, has its own
little method for pretty-reporting its own contents.

I don't know if I want to do this. Right now, each object has some
meaning beyond code implementation -- Maps, Nodes, Edges. This *thought*
of mine looks like 8 or more objects that all get rolled up into Map.
Each internal object will have to have its own package, its own file.

Will this be inefficient? Unmaintainable? Is there a better, lazier
approach?

Xiong
 
G

Guest

I am handling a number of complex objects -- the best way to build them
seems to be to have some of the HoH values be other objects. On the
other hand, some values just belong to the given object itself [...]

Ideally, I don't want to have to rewrite the report routine every time I
upgrade the object! The stupid way would be to have a rigid procedure in
which I took one thing at a time from the object and printed it. This
would depend on the structure of the object never changing -- bad.
[...]

The conventional wisdom of object-oriented programming is that each
object should know how to take care of itself to a certain extent. In
this case, that means that each object should have a "to_string" method
that returns a string representation of the object's data.

My current approach is better. I do this (at several levels):

sub report_sets {
my @keys = sort keys %{ $ref->{SETS} };
foreach (@keys) {
&{"report_" . lc($_)} ($ref->{SETS}{$_})
or die ("No report subroutine for $_.");
};
};

So, I have a bunch of little subs, each of which is able to pretty
format a particular kind of data,

And that suggests that you're not doing it the right OO-way.
each of which is named after the hash
key that points to that kind of data. These dig their way down until
they hit a sub that prints out one small chunk of data.

*THREE* problems:

[ problems snipped ]
My *thought* is to declare every level in the data structure as a
well-defined object, [...]

That sounds like the best option for you in the long run.
 
X

Xiong Changnian

Maybe (probably) I said too much in last post. Sorry.

Let's just reduce this to: I plan to implement a deeply nested object
such that each level of the "real" object hash is its own little
internal object which contains its own method to report (stringify)
itself.

My concern is that this will require me to write a very large number of
classes, each with its own package and file. I can see how the final
product might comprise 30 or 50 classes, since I plan to have a dozen
different "real" objects, all deeply nested. I can have all the internal
objects inherit, of course, so these need not be large files.

Concerns (sorry if they're basic):

1) Too many files to maintain. The least little change that needs to be
made to all of them and it's not lazy anymore. Too easy to overlook a
file. Unwieldy use block.

2) Don't know exactly how to write the use blocks and base class
statements. Worry about circular weirdness at compile time. Can't decide
to inherit along the same tree as the objects or have all the little
guys inherit from one hub.

Can I write some sort of object manager that at least hides the worst of
this inside one thing? I'd prefer to stay away from Class::Struct and so
forth; they seem too rigid. But if there's a CPAN module I *must* have,
please let me know.

One possibility (?) is to declare multiple classes in one file. Don't
know if this is wise or even doable. Would take pressure off (1) but
might make (2) even worse.

Feel free to direct me to online OO resources. As I browse around, I see
a lot of highly specific, advanced articles, many elementary tutorials,
but very little general discussion of OO Perl beyond the basics.
 
A

anno4000

Xiong Changnian said:
Maybe (probably) I said too much in last post. Sorry.

Let's just reduce this to: I plan to implement a deeply nested object
such that each level of the "real" object hash is its own little
internal object which contains its own method to report (stringify)
itself.

My concern is that this will require me to write a very large number of
classes, each with its own package and file. I can see how the final
product might comprise 30 or 50 classes, since I plan to have a dozen
different "real" objects, all deeply nested. I can have all the internal
objects inherit, of course, so these need not be large files.

Concerns (sorry if they're basic):

1) Too many files to maintain. The least little change that needs to be
made to all of them and it's not lazy anymore. Too easy to overlook a
file. Unwieldy use block.

Put them all in one (or a few) files.
2) Don't know exactly how to write the use blocks and base class
statements. Worry about circular weirdness at compile time. Can't decide
to inherit along the same tree as the objects or have all the little
guys inherit from one hub.

Inside the multi-class file you'll have to put "use base 'SomeClass';"
after the definition of "SomeClass". You can also set @ISA directly,
then you don't have that limitation.

Circularities in the @ISA chain are detected by Perl.

How you organize the inheritance tree I must leave to you.
Can I write some sort of object manager that at least hides the worst of
this inside one thing? I'd prefer to stay away from Class::Struct and so
forth; they seem too rigid. But if there's a CPAN module I *must* have,
please let me know.

One possibility (?) is to declare multiple classes in one file. Don't
know if this is wise or even doable. Would take pressure off (1) but
might make (2) even worse.

I think it's the way to go in your case. Multi-class files are standard
and pose no extra problems in a pure OO approach (as opposed to
exporting modules).

Put a bare block around each class definition, so you isolate lexicals
and can re-use variable names. So the structure would be:

{
package Class1;

# definition of Class1
}

# ...

{
package Class9;
use base qw( Class1 Class6);

# definition of Class9
}

# etc.

__END__

Anno
 
X

Xiong Changnian

Put a bare block around each class definition, so you isolate lexicals
and can re-use variable names.

Ah, that's the kind of sweetener that I really appreciate. I avoid
globals like the plague but the block makes the whole multiple-class
approach more palatable. Thank you.


you need to understand how these names and calls relate to each
other and not guess and thrash about.

Well, guessing and thrashing is preferred approach for some but I agree
with you that it's good to know what's *really* happening. I will go
over your explanation several times and construct a number of dummy
setups to exercise the limits of these paradigms.

I think key to all of this is that packages that contain subs intended
to be called as functions need to handled a bit differently from
packages that implement classes, whose subs are methods. When I go in, I
go in with both feet, so my current solution is pure OO. I will be
exporting no symbols with EXPORTER.


Uri Guttman said:
you used a for loop with the implied use of $_ as the loop variable. it
is better to use a named lexical variable...

Well, I won't argue the point. But for now, I'm learning, and it's
better for me to exercise these default variables heavily, so I can
comprehend code that uses them implicitly. Okay?
you don't have to tell us your background regarding declarations. most
of the regulars here know multiple langs and know all about this area.

Better than I, no doubt. That was a "where I'm coming from" snippet.
Maybe it says something about "where I'm going" -- maybe not. Sorry if
you found it digressive. Let's just say I've turned my back firmly on
toggling in commands via front-panel switches. I'm going for maximum
abstraction and extensibility.

They say a caller should not know anything about what's inside an
object; I'm going one better and trying to write methods that don't
assume much about the innards, either. The rubber has to meet the road
sometime, of course.
it is BAD coding to use symrefs if there is a better
way to do it. that is why use strict disallows them.

I couldn't agree more. I don't know how to reject this approach more
explicitly. I did it, I'm glad I did it, I'm glad it worked. I came here
to figure out a better way to do it and, with your help, I did. Thank
you.

Xiong
 
A

anno4000

Michele Dondi said:
Well, the two approaches are not mutually exclusive in the strictest
sense. Mixing them would just be plain unusual. There may well be some
OO module also exporting some symbol, but none that springs to mind
now. Indeed one must have a very good reason to do so!

There are examples.

It can make a lot of sense to export a constructor (->new, or a somewhat
curried version thereof) under the class name itself:

package MyClass;
our @EXPORT_OK = __PACKAGE__;

sub new {
my $class = shift;
# ....
}

sub MyClass { __PACKAGE__->new( @_) }

This is briefly discussed in perltooc (look for "export"), where
an example in _The Perl Cookbook_ is mentioned.

Anno
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,996
Messages
2,570,238
Members
46,826
Latest member
robinsontor

Latest Threads

Top