In Perl, if a function performs a Wantarray dance, or can we expect the caller to use a card?

On a (highly rated) perlmonks site, I found the following snippet that trims spaces on both sides of a string:

sub trim { @_ = $_ if not @_ and defined wantarray; @_ = @_ if defined wantarray; for (@_ ? @_ : $_) { s/^\s+//, s/\s+$// } return wantarray ? @_ : $_[0] if defined wantarray; } 

I do not understand why the author is trying to deal with the error of almost every line. Why not just trim the string and use the map programmer when it passes the array?

What is the difference between this trim, called so:

 my @test_array = ( 'string1', ' string2', 'string3 ', ' string4 '); my @result = trim(@test_array); 

Or a simple crop called so when you need to crop an array:

 my @test_array = ( 'string1', ' string2', 'string3 ', ' string4 '); my @result = map { trim($_) } @test_array; 
+8
perl map
source share
4 answers

First of all, it is better to discard this card:

 #e.1. sub trim { my @ar = @_; for (@ar) { s/^\s+//, s/\s+$// }; return wantarray ? @ar : $ar[0]; } 

Secondly, consider the above example and compare it with:

 #e.2. sub trim { for (@_) { s/^\s+//, s/\s+$// }; } 

what's the difference?

e.1. returns a new trimmed array, and e.2. modifies the original array.

Well, what does the original cryptic routine do?

This automatically magically (yes, it is Perl) changes the original array if you do not assign the return value to something OR leaves the original array untouched and returns a new trimmed array if you assign the return value to another variable .

How?

Check to see if anyarray is defined. while the function is on the right side, and the return value is assigned to the variable "defined wantarray" - true (regardless of the context of the scalar / array).

+11
source share

Abort this line by line because it was not:

 sub trim { @_ = $_ if not @_ and defined wantarray; # if there are no arguments, but a return value is requested # then place a copy of $_ into @_ to work on @_ = @_ if defined wantarray; # if the caller expects a return value, copy the values in @_ into itself # (this breaks the aliasing to the caller variables) for (@_ ? @_ : $_) { s/^\s+//, s/\s+$// } # perform the substitution, in place, on either @_ or $_ depending on # if arguments were passed to the function return wantarray ? @_ : $_[0] if defined wantarray; # if called in list context, return @_, otherwise $_[0] } 

I agree that the code gets a little tedious with all the wantarray checks, but the result is a function that has a level of flexibility with Perl built-in functions. The end result of creating a smart function is to clear the call site (excluding cyclic constructs, temporary variables, repetition, ...), which, depending on the frequency of use of the function, can significantly improve the readability of the code.

The function may be slightly simplified:

 sub trim { @_ = @_ ? @_ : $_ if defined wantarray; s/^\s+//, s/\s+$// for @_ ? @_ : $_; wantarray ? @_ : shift } 

The first two lines can be collapsed into one, since they do the same thing (assigning @_ ) only with different source values. And there is no need for external verification of return ... if defined wantarray at the end, since returning a value in a void context does nothing.

But would I probably change the last line to wantarray ? @_ : pop wantarray ? @_ : pop , as this makes it behave like a list (the last element in a scalar context).

After everything is said and done, it allows you to use the following types of calls:

 my @test_array = ( 'string1', ' string2', 'string3 ', ' string4 '); my @result = trim @test_array; my $result = trim $test_array[0]; trim @test_array; # in place trim 

and even supports a call loop:

 my @result = map trim, @test_array; 

or in more detail:

 my @result = map trim($_), @test_array; 

and it can be used inside a while loop similar to chomp

 while (<$file_handle>) { trim; # do something } 

Opinions about dwimmery in Perl are mixed. I personally like it when the functions give me the ability to encode the caller in a way that makes sense, rather than working with a hard interface function.

+7
source share

The author probably wanted to imitate the behavior of the standard chomp function. There is no need to do this in its own function.

 man perlfunc chomp VARIABLE chomp( LIST ) chomp 

[...] If you break the list, each element is broken. [...]

+5
source share

Note that this is just Text :: Trim . See its description for various uses. Playback with wantarray allows wantarray to distinguish between different contexts and implement different semantics for each.

Personally, I prefer only one semantics, because it is easier to understand and use. I would not use the $_ default variable or in-place modification according to the Nylon Smile example.

+1
source share

All Articles