Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

To help with debugging I wrote showvar() to display a variable's name and type. This routine then calls show() or showHASH() to display the element count and content. A poor man's DUMPER. It works fine for my needs.

To cut down on typing, I would like to change:

showvar('$x',\$x) to showvar($x) showvar('@x',\@x) to showvar(@x) showvar('%x',\%x) to showvar(%x)
and still get the same results.

If we pass just a single argument named sigilVarname, say showvar(sigilVarname), what has got me stumped is how to extract the "Varname" part.

ie. showvar(@hello) sigil==@, Varname==hello

Any ideas on how to rewrite showvar() so that it works with one argument?

$x = qq(odd number of items kaboom); showvar('$x',\$x); # prints # $x SCALAR, 1 ITEM: # 'odd number of items kaboom' @x = qw(odd number of items kaboom); showvar('@x',\@x); # prints # @x ARRAY, 5 ITEMS: # 'odd' # 'number' # 'of' # 'items' # 'kaboom' %x = qw(odd number of items kaboom); showvar('%x',\%x); # prints # %x HASH, 3 KEY/VALUE PAIRS: # kaboom => ***EMPTY*** # odd => number # of => items sub showvar { # variables $, @, % local ($varname, $_) = @_; return print "\n*** One or two arguments are undefined ***\n" if ! +$_[0] or !$_[1]; my $type = ref; return print "\nVariable '$varname' is inconsistent with its refer +ence\n" if !$type; if (/SCALAR/) {print "$varname $type, "; show ($$_)} elsif (/ARRAY/) {print "$varname $type, "; show (@$_)} elsif (/HASH/) {print "$varname $type, "; showHASH (%$_)} else {print "$varname $type, is not of type SCALAR/ARRA +Y/HASH\n"} } sub show { my $len = scalar @_; my $plural = $len>1 ? 'ITEMS' : 'ITEM'; $_[0]="***EMPTY***" unless $_[0]; #avoid undef in mapping below print join "\n", "$len $plural:", map(" '$_'", @_), "------------ +\n\n"; } sub showHASH { . . # Avoid length error if hash value undef $hash{$_} = '***EMPTY***' if !defined $hash{$_}; . . # Pretty print print scalar keys %hash, " KEY/VALUE PAIRS: \n"; . . }

Replies are listed 'Best First'.
Source filter or PadWalker. Both are ugly
by imp (Priest) on Dec 08, 2006 at 09:36 UTC
    The easiest way to provide the variable name is with Data::Dumper::Simple, which uses a source filter. Since the variable name in question is one level higher up the call chain there aren't many options other than PadWalker, which is usually an indication that you are being a bad person.

    Here is an implementation using PadWalker:

    use PadWalker qw( peek_my peek_our ); use strict; use warnings; sub name(\[$@%&*]) { for my $pad ( peek_my(1), peek_our(1)) { while (my ($k,$v) = each %$pad) { if ($v == $_[0]) { return $k; } } } return; } our $x = [0]; my @a = (1,2); my %foo = (1 => 2); my $subref = sub {}; printf "Name: %s\n", name($x); printf "Name: %s\n", name(@a); printf "Name: %s\n", name(%foo); printf "Name: %s\n", name($subref);
    This will work for variables declared with my or our, but not for things like $::x.

    Note that this will not work on older perls that do not support the (\[$@^&*]) style prototype.

      It didn't seem fair (or fun) to provide a PadWalker example, and not a Filter::Simple one. I don't recommend using either of these approaches, but here is a basic(?) filter implementation: ShownameFilter.pm
      package ShownameFilter; use Filter::Simple; FILTER_ONLY code => sub { s{ showname\( ( # Capture 2 (closes second) \s* \\? # References [\$@%*]+ # Scalar, array, hash, glob ( # Capture 1 (closes first) \w* # alphanumeric. Doesn't catch a lot of perl spe +cial vars ) # End capture 1 \s* ) # End capture 2 \) } {showname('$2',$1)}xg; }; 1;
      showname.pl
      use strict; use warnings; use ShownameFilter; sub showname { my ($name,$value) = @_; print "name: $name\n"; } my ($x, $x12, @foo, %bar); showname($x); showname( $x12 ); showname( \$x12 ); showname( @foo );
      Output:
      name: x name: x12 name: x12 name: foo
      Again - I don't recommend doing this.
      Thank you imp

      Your insight is truly appreciated. I tried Data::Dumper::Simple and except for pretty printing a hash (name => value where the fat comma "=>" lines up in a vertical column), it does what I was trying to achieve with my 3 subs showvar(),show(),showHASH

      Incidently, the author Curtis "Ovid" Poe explains in more detail the reasons why people like myself would want to use his module as a simple debugging aid. ie. typos and "succintness"

      Best regards

      Bernie in Montreal.

Re: How to extract the name of a variable?
by BrowserUk (Patriarch) on Dec 08, 2006 at 11:10 UTC

    If this is for tracing variables/values for debugging purposes, take a look at Smart::Comments. It's a source filter, but when you turn debugging off for production use, you do it by commenting out the use line, so there is no source filter used in the production code.

    To trace a variable by name, you simply place the name of the var in a comment and when you use S::C, it will trace the name and value of the variable to STDERR.

    c:>perl -MSmart::Comments $var = 123; ### $var $x = 'fred'; ### $x ^Z ### $var: 123 ### $x: 'fred'

    It does a lot more besides.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: How to extract the name of a variable?
by PreferredUserName (Pilgrim) on Dec 08, 2006 at 15:21 UTC
    I take a different approach to this issue of making "printf debugging" easier: I have a binding in my text editor so that when I press Ctrl-K, it will turn the variables on the current line into a printout statement, like so:
    # In Perl, I type: %some_hash, $some_scalar, @some_list, some_func() # and then hit Ctrl-K to turn it into: print STDERR "DBG 950($$): ([\%some_hash, \$some_scalar, \@some_list +, some_func()]):\n\t", Dumper([\%some_hash, $some_scalar, \@some_list +, some_func()]);
    The random number after DBG makes it easy to search for that debug line. I have similar bindings for shell, java, C, and other languages, some of which need some hints with the types (e.g., my_name:s becomes my_name(%s)). Here's the binding in my Vim perl.vim syntax file:
    nmap <C-K> <ESC>!!print_perl_debug STDERR %<RETURN>:w<CR>
    And here's print_perl_debug:
    #!/usr/bin/perl -w $handle = shift || 'STDERR'; $file = shift || '/dev/null'; $is_php = system(q(grep -q '^<\?PHP$' ) . $file) == 0; if ($is_php) { while (<STDIN>) { chomp; s/(^\s*)//; $space = $1; $rand = int(rand 900) + 100; print qq(${space}print 'DBG $rand: $_: ' . "[$_]\\n";\n); } } else { $do_use = system "grep -q '^[^#]*use Data::Dumper' $file"; while (<STDIN>) { chomp; s/(^\s*)//; $space = $1; $use = $do_use ? " use Data::Dumper;" : ''; s/([@%])/\\$1/g; s/\$\\\@/\$\@/g; # fix up $@ $_ = "[$_]" if /,/; $rand = int(rand 900) + 100; $printable = $_; $printable =~ s/(\$|")/\\$1/g; my $last = /\S/ ? qq(($printable):\\n\\t", Dumper($_);$use\n) +: qq(\\n";\n); print $space, 'print ', ($handle eq 'STDOUT' ? '' : "$handle " +), qq{"DBG $rand(\$\$): $last}; } }