Re: My very confusing questions
by NetWallah (Canon) on Jul 08, 2014 at 05:29 UTC
|
Try this more complete, corrected version of your code:
use strict;
use warnings;
my @h = (
sub { return sprintf ("0x%x in HEX\n", shift) },
sub { return sprintf ("%b in Binary\n", shift) },
sub { return sprintf ("%d in Decimal\n", shift) },
);
print $_->(5) for @h;
Now - an Explanation:
First, you need @h, not %h for an array .
Next, the "for" loop places each element of the array (which happens to be a "sub"routine reference, into $_.
THen The $_->() actually CALLS the subroutine (an anonomyous one). Each delcared anonymous subroutine is called in sequence
the constant "5" is passed in, separately to each call.
The "shift" actually does "shift @_", which retrieves the parameter (5).
You should read the documentation in perlsub.
Update:Here is how to use a hash for this structure properly:
use strict;
use warnings;
my %h = (
HEX => sub { return sprintf ("0x%x ", shift) },
BIN => sub { return sprintf ("%b", shift) },
DEC => sub { return sprintf ("%d", shift) },
);
my $value = 5;
for my $fmt (sort keys %h){
print "$value in $fmt is: ", $h{$fmt}->($value) , "\n";
}
What is the sound of Perl? Is it not the sound of a wall that people have stopped banging their heads against?
-Larry Wall, 1992
| [reply] [d/l] [select] |
|
|
So, when we use shift, we're returning whatever # we want to call(In your case 5)
What about the original code I posted? I use <STDIN> instead of just using a number, so would shift just shift whatever number is supplied by the user?
| [reply] |
|
|
From "perldoc perlsub":
Any arguments passed in show up in the array @_ . Therefore, if you called a function with two arguments, those would be stored in $_[0] and $_1 .
From "perldoc -f shift":
Shifts the first value of the array off and returns it, shortening the array by 1 and moving everything down. If there are no elements in the array, returns the undefined value. If ARRAY is omitted, shifts the @_ array ..
Don't use <STDIN> instead of shift(). You CAN use
chomp(my $value = <STDIN>); # Instead of hard coding "5"
If you want the input from the keyboard.
Profanity is the one language all programmers know best.
| [reply] [d/l] |
Re: My very confusing questions
by davido (Cardinal) on Jul 08, 2014 at 05:40 UTC
|
It would help if you posted the code that you have a question about. What you posted doesn't do what you claim. It stringifies a sub reference, turning it into a hash key that indexes a subref as its value, and follows that up by stringifying another subref which becomes a hash key that indexes an undefined value. If warnings are enabled, it also produces one of those. There is just too much missing from the code you showed us, and what you did show us doesn't actually work... we won't be able to help with that part of the question until you fix things.
If your question is what does shift do in a subroutine, you already mostly answered it yourself.
shift shifts the lowest-indexed element off of an array, and returns that element as its return value. Inside a subroutine, the implicit array that shift operates on is @_. Inside of a subroutine, @_ contains the parameters that were passed to the sub when it is called. Therefore:
sub add {
return shift() + shift();
}
...is about the same as...
sub add {
return $_[0] + $_[1];
}
# ...or...
sub add {
my ( $left, $right ) = @_;
return $left + $right;
}
The biggest difference being that the first and third examples disassociate the values from the caller, so that they are not aliased back to the caller.
As for what return does, well, subroutines are able to return a value to their caller. For example:
my $size = length( $some_string );
If I needed to write my own length function, here is one way to do it:
sub mylength {
my $string = shift;
my $size = $string =~ tr///c; # Don't worry how it works.
return $size;
}
# Invoke it like this:
my $length = mylength("Hello world.");
# $length now holds 12.
return is how we tell a subroutine explicitly what value to return. Perl has another feature that dictates that if there is no explicit return statement, the last expression to be evaluated becomes the subroutine's return value. Relying on that feature is useful for really small subroutines, but for anything non-trivial can lead to confusion. So we nearly always use return for anything that isn't absolutely clear otherwise.
| [reply] [d/l] [select] |
Re: My very confusing questions
by Laurent_R (Canon) on Jul 08, 2014 at 06:28 UTC
|
You haven't shown code actually doing something. We can't explain the output you are getting since we don't know how you used the data structure you are showing. I am actually surprised that:
my %h = (
sub { return sprintf ("0x%x", shift) },
sub { return sprintf ("%b", shift) },
sub { return sprintf ("%d", shift) },
);
actually compiles and does not give you at least a warning about a hash with an odd number of elements.
Now, if you are trying to understand shift and @_, I would suggest you make some tries with examples simpler than subroutine references (especially with faulty examples of dispatch tables). Try simpler things such as:
$result = process_args(1, 2, 3, 4);
print "Result is $result \n";
sub process_args {
my $c = shift:
print "Arg array is: @_ \n";
my $d = shift;
print "First argument is $c \n";
print "second argument if $d \n";
print "Arg array is now: @_ \n";
return $c + $d;
}
Back to your code, it should be corrected at least as follows to make it a proper dispatch table:
my %h = (
hex => sub { return sprintf ("0x%x", shift) },
bin => sub { return sprintf ("%b", shift) },
dec => sub { return sprintf ("%d", shift) },
);
The subrefs in the dispatch table can now be called as follows:
my $hex_val = $h{hex}->(5);
shift will take the argument (5) and pass it to sprintf and the anonymous sub will return the result of sprintf.
Update: fixed a typo in the following line:
print "Arg array is now: @_ \n";
Many thanks to AnomalousMonk for spotting it. I also applied AnomalousMonk's suggestion to add a couple of arguments to the process_args() function call, so that the printing of @_ after the two shift commands would display more clearly what is going on. AnomalousMonk also pointed another error in the code to call the subrefs in the dispatch table. Now fixed.
| [reply] [d/l] [select] |
Re: My very confusing questions
by davido (Cardinal) on Jul 08, 2014 at 16:49 UTC
|
Given your updated code, it appears that we've answered your questions. We just haven't done so in a way that is specific to your code example. Since you updated your code and mentioned the update, I'm going to assume that you still haven't figured out what's going on. Let's look at your code:
my %h = (
decimalhex => sub{return sprintf("0x%x", shift)},
decimalbinary => sub{sprintf("%b", shift)},
hexdecimal => sub{return sprintf("%d", shift)},
);
my $var = <STDIN>;
print $h{decimalhex}($var), "\n";
print $h{decimalbinary}($var), "\n";
print $h{hexdecimal}($var), "\n";
So first you are setting up a hash where each element contains a reference to a subroutine. This subroutine is executed later on when you dereference it.
As you dereference the subroutine, $var is placed on the subroutine's call stack. From within the subroutine, it becomes the single value in @_. The subroutine then shifts that single value off of the call stack, and shift's return value is applied as a parameter to sprintf. The return value of sprintf is then supplied as the return value of the anonymous subroutine such that it becomes the parameter to print.
It's important to note that shifting a value out of @_ has no effect on the value outside of the subroutine. The shift doesn't propagate out of the subroutine's scope, so the original value in $var is untouched; available to be passed as a parameter again to the next sub.
You could write your code as follows, in which case it might be a little easier to understand:
sub dechex {
my $number = shift @_;
my $string = sprintf "0x%x", $number;
return $string;
}
sub decbin {
my $number = shift @_;
my $string = sprintf "%b", $number;
return $string;
}
sub hexdec {
my $number = shift @_;
my $string = sprintf "%d", $number;
return $string;
}
my( $dh, $db, $hd ) = ( dechex($val), decbin($val), hexdec($val) );
print $dh, "\n"
print $db, "\n";
print $hd, "\n";
As for what "return" does; it explicitly specifies the value to be returned, and immediately exists the subroutine, returning a value and control back to the caller.
In Perl, subroutines implicitly return a value, but it's clearer to make it explicit with return.
| [reply] [d/l] [select] |
Re: My very confusing questions
by choroba (Cardinal) on Jul 08, 2014 at 21:29 UTC
|
| [reply] |
|
|
Surely parameterless shift and pop, operate on @ARGV in the main, or @_ in a sub, as described. But unshift and push, unseasoned with parameters, are a syntax error.
/usr/bin/perl -le '@ARGV=qw(this that more less);pop;shift;print join
+"<>", @ARGV;'
that<>more
/usr/bin/perl -le '@ARGV=qw(this that more less);pop;shift;push;print
+join "<>", @ARGV;'
Not enough arguments for push at -e line 1, near "push;"
/usr/bin/perl -le '@ARGV=qw(this that more less);pop;shift;unshift;pri
+nt join "<>", @ARGV;'
Not enough arguments for unshift at -e line 1, near "unshift;"
/usr/bin/perl -le '@ARGV=qw(this that more less);pop;shift;push "X";pr
+int join "<>", @ARGV;'
Type of arg 1 to push must be array (not constant item) at -e line 1,
+near ""X";"
/usr/bin/perl -le '@ARGV=qw(this that more less);pop;shift;unshift "X
+";print join "<>", @ARGV;'
Type of arg 1 to unshift must be array (not constant item) at -e line
+1, near ""X";"
Or is there a cunning way to make them dance?
Cheers, R.
Pereant, qui ante nos nostra dixerunt!
| [reply] [d/l] [select] |
Re: My very confusing questions
by perlfan (Parson) on Jul 08, 2014 at 14:18 UTC
|
You are confused because of things that implied in Perl.
Let's back up. You are treating your hash of subroutines like an array of subroutine references. Do you mean to do the following?
my %h = (
func1 => sub { return sprintf ("0x%x", shift) },
func2 => sub { return sprintf ("%b", shift) },
func3 => sub { return sprintf ("%d", shift) },
);
You Then would selectively call the sub you want using using the subroutine reference invocation idiom,
$h{func1}->();
Assuming you do, then the shift operates on @_ - this is the implicit parameters list. For clarity, it is recommended that you unpack parameters, for example:
my %h = (
func1 => sub {
my $value = shift @_;
return sprintf ("0x%x", $value)
},
func2 => sub {
my $value = shift @_;
return sprintf ("%b", $value);
},
func3 => sub {
my $value = shift @_;
return sprintf ("%d", $value);
},
);
More about shift and why it's used inside of subroutines to unpack parameters:
- shift/unshift operate on the left hand side (LHS) of a list, in this case it is @_
- shift is like pop, but pop operates on the right hand side (RHS) of the list.
- unshift is like push, but push operates on the RHS of the list
- You must use shift on @_ in order to operate on the parameters passed to a subroutine as one traditionally would expect (i.e., Left to Right)
But of course, TIMTOWTDI. Some examples:
Pass in some number of key/value pairs as a simple list
sub foo {
# requires an even number of parameters in @_
my %params = @_;
return;
}
# to call:
foo(qw/key1 val1 key2 val2 key3 val3/);
As a singular hash reference with some number of key/value pairs already defined
sub foo {
# get singular hash reference with any number of key/value pairs
my $params = shift @_; # or alternatively, my $params = $_[0]
# some may want to dereference (I usually don't)
my %params = %$params;
return;
}
# to call:
foo( {'key1' => 'val1', 'key2' => 'val2', 'key3' => 'val3' });
# or without braces,
foo( 'key1' => 'val1', 'key2' => 'val2', 'key3' => 'val3' );
I HTH. | [reply] [d/l] [select] |
Re: My very confusing questions
by AnomalousMonk (Archbishop) on Jul 08, 2014 at 15:50 UTC
|
my %h = (
decimalhex => sub{return sprintf("0x%x", shift)},
decimalbinary => sub{sprintf("%b", shift)},
hexdecimal => sub{return sprintf("%d", shift)},
);
This is not your originally posted code! (You have made changes in other places as well.) Here in the monastery, it is considered a very Bad Thing and is greatly Frowned Upon to make a change to one's post without citing the change in an update note of some kind. You can always change your post if you post as a registered user, as you did, and you are welcome to do so, but making changes without citation renders the comments and replies of the humble monks incoherent. By the method you have chosen to pose your confusing questions, you have actually sown more confusion! Please see How do I post a question effectively?.
| [reply] [d/l] |
|
|
| [reply] |