Zadeh has asked for the wisdom of the Perl Monks concerning the following question:
|
---|
Replies are listed 'Best First'. | ||
---|---|---|
Re: shift vs @_
by Fletch (Bishop) on Oct 02, 2006 at 18:24 UTC | ||
Well, if you mean why not use things from @_ directly: items in @_ are aliases to the things passed in; if you're not careful you can unintentionally modify something of your caller's.
Now if you just mean why not always use the my( $a, $b, $c ) = @_; form vice shift: there's times when you want to pull off some items and then do something list-y with the remaining contents of @_. Best example off the top of my head would be something that builds a hash from key/value pairs like:
Unless you pull that first argument off with shift you'd have to do something like @_[1..$#_] which just looks crufty. | [reply] [Watch: Dir/Any] [d/l] [select] | |
by jasonk (Parson) on Oct 02, 2006 at 19:41 UTC | ||
Now if you just mean why not always use the my( $a, $b, $c ) = @_; form vice shift: there's times when you want to pull off some items and then do something list-y with the remaining contents of @_. Best example off the top of my head would be something that builds a hash from key/value pairs like: Another good example is subclassing...
| [reply] [Watch: Dir/Any] [d/l] | |
by ambrus (Abbot) on Oct 07, 2006 at 11:52 UTC | ||
I don't usually use shift even if I want to do something with the rest of the arguments. I do, like, my($one, %hash) = @_;. | [reply] [Watch: Dir/Any] [d/l] | |
Re: shift vs @_
by grep (Monsignor) on Oct 02, 2006 at 18:36 UTC | ||
Again I'll note that this works only for non-true values, IOW if 0(zero) is acceptable then don't do this. | [reply] [Watch: Dir/Any] [d/l] [select] | |
by ikegami (Patriarch) on Oct 02, 2006 at 21:14 UTC | ||
More specifically, if one of the following is acceptable, then don't do this: | [reply] [Watch: Dir/Any] [d/l] [select] | |
by exussum0 (Vicar) on Oct 02, 2006 at 20:59 UTC | ||
| [reply] [Watch: Dir/Any] | |
Re: shift vs @_
by hgolden (Pilgrim) on Oct 02, 2006 at 18:22 UTC | ||
I think the "shift" idiom caught on out of laziness, though it's not as slick with more than one argument. You can write: Likewise, you can write: for only one argument. Hays | [reply] [Watch: Dir/Any] [d/l] [select] | |
Re: shift vs @_
by sgifford (Prior) on Oct 02, 2006 at 19:18 UTC | ||
For example, this code could allow a function to act as a regular sub, or as a method (assuming the first argument will never be a reference unless it's called as a method):
Similarly, if you have a sub that takes a list as its final argument, shifting off the non-list arguments can make it clearer what you're doing, especially if you pass the list on to any other subs:
This can also be useful if you decide to goto another sub, since that sub will see the modified @_ as its argument list. Finally, as a matter of style, I usually get $self or $class with shift in methods and constructors, since it makes it clearer what the explicit arguments are, as compared to the implicit object reference or class name passed in as the first argument. Update: Corrections and clarifications from jimt. | [reply] [Watch: Dir/Any] [d/l] [select] | |
by jimt (Chaplain) on Oct 02, 2006 at 19:54 UTC | ||
Eeek! This sort of thing can rapidly get you into serious trouble. First of all, there's the fact that this won't operate properly if it's called as a class method such as Some::Package->foo( qw(arg1 arg2 arg3) ); $_[0] is a string, in that case. Secondly, it breaks down if you pass in a normal reference as your first argument. foo({'hashkey' => 'hashval'}, qw(arg4 arg5)); You end up setting a normal arrayref as $self. And thirdly, and potentially most importantly, if the conditional fails, then the variable maintains its scope. There are other threads on the board talking about the pitfalls of constructs like my $xyz if 0, which is what can happen here. The short answer is in this case $xyz would end up acting like a static variable. The first time, it gets initialized to something random (well, not random - it's 0 (or undef?), but that's not guaranteed and could change at any time (not that it has) ), and on subsequent calls, it maintains its value. It doesn't directly cause problems here, probably, but observe this contrived function.
Note how $self survived between calls and was around to increment. Bugs of this nature can be horrible to track down. It's also frowned upon to try to use this for static variables - use a closure or wrap the subroutine in an additional lexical scope to have "static" variables scoped to the subroutine. Besides, it's tougher to read this way. In general, it's best to always avoid my $foo if $something constructs, except for obfuscated contests or the like. | [reply] [Watch: Dir/Any] [d/l] [select] | |
by sgifford (Prior) on Oct 02, 2006 at 20:29 UTC | ||
which doesn't seem to suffer from the largest of the problems. I've also added some clarifications about when it will and won't work. As an aside, you would have a very hard time convincing me that this behavior of my and conditionals is anything but a bug in Perl. :) | [reply] [Watch: Dir/Any] [d/l] [select] | |
by jhourcle (Prior) on Oct 03, 2006 at 15:05 UTC | ||
I can get around some of those problems. First, if we assume that the subroutine was written as a function originally, and had no concept of self: shift if UNIVERSAL::isa ($_[0], __PACKAGE__);We can also deal with the possibility that we need self for the class name (eg, incase soemone calls it as a method to override inherited routines, but there's still existing code that has code that assumes it's a function:
It completely handles your first issue, however, this will break if you have a method that takes as its first argument an item of its same type (and someone tries calling it as a function), or if someone does some sort of multiple inheritance, that results in the first argument inheriting from this class (and then calls it as a function). It also adds an additional problem case where a function argument that's a string containing the name of a package that inherits the package in question is assumed to be the 'Class->method()' syntax.. Read more... (424 Bytes) | [reply] [Watch: Dir/Any] [d/l] [select] | |
by Zadeh (Beadle) on Oct 02, 2006 at 21:05 UTC | ||
| [reply] [Watch: Dir/Any] | |
by Tanktalus (Canon) on Oct 02, 2006 at 23:24 UTC | ||
This has been covered a few times. Check out: | [reply] [Watch: Dir/Any] | |
by Zadeh (Beadle) on Oct 03, 2006 at 19:35 UTC | ||
by sgifford (Prior) on Oct 02, 2006 at 22:14 UTC | ||
Benchmark: timing 1000000 iterations of use_direct, use_list, use_shift... use_direct: 7 wallclock secs ( 6.48 usr + -0.01 sys = 6.47 CPU) @ 154559.51/s (n=1000000) use_list: 10 wallclock secs ( 9.85 usr + 0.06 sys = 9.91 CPU) @ 100908.17/s (n=1000000) use_shift: 6 wallclock secs ( 6.48 usr + 0.01 sys = 6.49 CPU) @ 154083.20/s (n=1000000) | [reply] [Watch: Dir/Any] [d/l] [select] | |
by Anonymous Monk on Oct 23, 2014 at 06:31 UTC | ||
by Anonymous Monk on Oct 23, 2014 at 06:33 UTC | ||
by chromatic (Archbishop) on Oct 03, 2006 at 02:03 UTC | ||
Besides style, is there any performance penalty paid by shift? Yes; if you do it repeatedly hundreds of thousands of times in a tight loop, you might slow down your program as much as performing one IO operation. In other words, none that you will ever notice. | [reply] [Watch: Dir/Any] | |
by Anonymous Monk on Nov 12, 2010 at 02:47 UTC | ||
| [reply] [Watch: Dir/Any] | |
Re: shift vs @_
by shmem (Chancellor) on Oct 02, 2006 at 19:33 UTC | ||
If @_ needs to be intact for subsequent calls, as in
the non-destructive methods are used. Also, as the variables passed in via the vector @_ are Otherwise, it just doesn't matter. Since after setting up the variables in the sub's scope @_ isn't looked at anymore, arguments may be shifted or not. In these cases saying $var = shift or $var = $_[0] does the same for the sub, although the impact on @_ is different. So, saying $var = shift and @list = @_ is just caring about copying, but done that, not caring about @_ any more. Using the arguments in a sub without prior assignment (i.e. without copying, as $_[0] .. $_[$#_]) modifies the thingies in the caller. --shmem <update> changed reference to alias as per tye's post </update> _($_=" "x(1<<5)."?\n".q·/)Oo. G°\ / /\_¯/(q / ---------------------------- \__(m.====·.(_("always off the crowd"))."· ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print} | [reply] [Watch: Dir/Any] [d/l] [select] | |
by BerntB (Deacon) on Oct 03, 2006 at 09:21 UTC | ||
Do I have to use the $_[x] notation for that? Should I send them in as references? | [reply] [Watch: Dir/Any] [d/l] | |
by shmem (Chancellor) on Oct 03, 2006 at 20:50 UTC | ||
You can also use prototypes for your subs and access your arguments inside the sub as references:
output:
--shmem _($_=" "x(1<<5)."?\n".q·/)Oo. G°\ / /\_¯/(q / ---------------------------- \__(m.====·.(_("always off the crowd"))."· ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print} | [reply] [Watch: Dir/Any] [d/l] [select] | |
by tye (Sage) on Oct 03, 2006 at 21:26 UTC | ||
Re: shift vs @_
by tomhukins (Curate) on Oct 02, 2006 at 20:11 UTC | ||
I can think of one reason not yet mentioned: Multi-line shift makes patches more readable. Imagine you have:
If I want to add another argument (maybe at this point I should consider hash-based arguments as above, but let's assume I can't, maybe for backwards compatibility), I might rewrite this as:
The patch to add this argument only contains one additional line with the new argument, making the code easier for people reviewing changes in your version control system or reviewing patches before committing them. If you change my ($foo, $bar) = @_; to my ($foo, $bar, $baz) = @_; traditional patch/diff output shows one line removed and one added which makes it a little less clear that the change does nothing other than add one new argument. | [reply] [Watch: Dir/Any] [d/l] [select] | |
by Argel (Prior) on Oct 02, 2006 at 20:26 UTC | ||
| [reply] [Watch: Dir/Any] [d/l] | |
by Argel (Prior) on Oct 02, 2006 at 20:39 UTC | ||
| [reply] [Watch: Dir/Any] | |
by tomhukins (Curate) on Oct 02, 2006 at 20:44 UTC | ||
by GrandFather (Saint) on Oct 02, 2006 at 21:25 UTC | ||
That depends on the diff tool you use. TortoiseSVN's diff shows changes within a line for example, so the extra verbiage associated with the shift is not required. Besides, generally the number of times you have to check deltas to solve a problem are many fewer than the number of times you look at the code in the process of writing or using it. An idiom that provides clarity in writing and use is more important than occasional merge or delta issues. Using the list assignment form makes it easier to match the parameter list in the call with the required parameters - the assignment list self documents the required parameters (especially if good names are used). DWIM is Perl's answer to Gödel | [reply] [Watch: Dir/Any] | |
Re: shift vs @_
by eyepopslikeamosquito (Archbishop) on Oct 02, 2006 at 21:51 UTC | ||
This question is discussed at length in the excellent Perl Best Practices book in the third item of Chapter 9, "Argument Lists: Always unpack @_ first". Notice that this chapter is available free online as the book's sample chapter. In this item, TheDamian argues that either version is acceptable, with the shift-based version preferred when one or more arguments needs to be sanity checked or documented with a trailing comment. The most important thing though, is to unpack them at the start of the subroutine and to avoid accessing them directly as $_[0], $_[1] etc. | [reply] [Watch: Dir/Any] [d/l] | |
by shmem (Chancellor) on Oct 02, 2006 at 23:15 UTC | ||
The most important thing though, is to unpack them at the start of the subroutine and to avoid accessing them directly as $_[0], $_[1] etc. - in most cases, yes. Operating directly on $_[0] is always fine if you know what you are doing, and why, e.g. the sub and the caller are designed that way to avoid costly copying. Whether in such cases a reference should be passed in the first place is another story. But then, $_[0] is a reference already... --shmem _($_=" "x(1<<5)."?\n".q·/)Oo. G°\ / /\_¯/(q / ---------------------------- \__(m.====·.(_("always off the crowd"))."· ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print} | [reply] [Watch: Dir/Any] [d/l] [select] | |
Re: shift vs @_
by Anonymous Monk on Oct 03, 2006 at 08:27 UTC | ||
Starting with is a potentially costly operation, since you are copying everything else in @_ - and it is slightly different as you lose the aliasing. And writing: or is not something I fancy. | [reply] [Watch: Dir/Any] [d/l] [select] | |
Re: shift vs @_
by exussum0 (Vicar) on Oct 03, 2006 at 12:24 UTC | ||
It's valid perl. It assigns a scalar. You will likely run into the bug right away, but who knows when, eh? I can't do the equiv w/ shift in the form of... Unless i do.. Now I have to worry about indicies. At least shift requires none. | [reply] [Watch: Dir/Any] [d/l] [select] | |
by radiantmatrix (Parson) on Oct 06, 2006 at 14:33 UTC | ||
I can't easily do my $x, $y = @_; It's valid perl. It assigns a scalar. Only because the way you called my creates a scalar context. Try my( $x, $y ) = @_;, which creates a list context, and so produces the same result as my $x = $_[0]; my $y = $_[1];. This works with any array or list. Check out the difference between:
See, a list in scalar context will give its last element. An array in scalar context will give the number of its elements. However, if we make both sides of the assignment have list context, then elements get copied.
<–radiant.matrix–>
A collection of thoughts and links from the minds of geeks The Code that can be seen is not the true Code I haven't found a problem yet that can't be solved by a well-placed trebuchet | [reply] [Watch: Dir/Any] [d/l] [select] | |
by exussum0 (Vicar) on Oct 06, 2006 at 14:39 UTC | ||
| [reply] [Watch: Dir/Any] | |
by radiantmatrix (Parson) on Oct 06, 2006 at 16:36 UTC | ||
by exussum0 (Vicar) on Oct 06, 2006 at 20:01 UTC | ||
by Cop (Initiate) on Dec 23, 2007 at 19:48 UTC | ||
What wrong with my $x, $y = @_? Putting perl specifics away (not worried about the syntax like whether you should say my (a,b)). More and more languages are support this kind of de-group operation. | [reply] [Watch: Dir/Any] | |
by blazar (Canon) on Dec 25, 2007 at 15:24 UTC | ||
I personally believe that for once Cop's question is legitimate, but one cannot put "Perl specifics away" since this is Perl: while avoiding a pair of parentheses may represent an occasional advantage -I for one try to avoid them all the time, if possible- in this particular case wouldn't square at all with Perl's whole syntax and semantics. Anyway, as this very thread and tons of similar ones show, Perl's current argument passing mechanism is both fascinating for the far reaching consequences it gets out of an extreme simplicity on the one hand, and awkward on the other one. This is why after some initial period of being skeptical I now cherish Perl 6's message passing. For very simple stuff I can still rely on @_ and not worry at all. Suppose I want to write a sub that will take a list (of strings) and concat it after making all lowercase every element out of two starting with the first, and all uppercase the other ones; then I'd write it like this:
But if I were to write a sub that would take two integers and return the product of integer numbers comprised between them, then I would write:
| [reply] [Watch: Dir/Any] [d/l] [select] |