1

I would like to pass the single capture of a reg-ex as a scalar to a subroutine, how do I go about doing this? Here is an example:

sub myfunc($)
{
   my ($value)=@_;

   # Do something with $value...
}

# This is the data we want to parse
my $some_var='value: 12345'; # For example

# We want to extract the value '12345' from $some_var 
# and pass it to the myfunc subroutine as a scalar

# Attempt #1: This doesn't work    
myfunc($some_var=~/value: (\d+)/);

# Attempt #2: This does work, but seems overly complicated
myfunc(join('',$some_var=~/value: (\d+)/));

Is there a better way than Attempt #2?

Update:

Oesor's answer gives exactly what I was looking for to avoid calling join:

myfunc(($some_var=~/value: (\d+)/)[0]);
Michael Goldshteyn
  • 71,784
  • 24
  • 131
  • 181

4 Answers4

7

Yes there is a better way! Use the $ capture group variables.

 $some_var =~ /value: (\d+)/;
 myfunc($1);

You can also chain and nest them:

$some_var =~ /(\w+): (\d+)/;
# now $1 eq "value" and $2 eq "12345"

# and
$some_var =~ /((\w+): (\d+))/;
# now $1 eq "value: 12345" and $2 eq "value" and $3 eq "12345"
  • This certainly will work, but it is even more verbose than my second example. I'm looking for a small change to my Attempt #1 to make it work correctly (i.e., something other than join). – Michael Goldshteyn Feb 03 '14 at 17:15
  • 2
    Sorry, deleted my comment because it was incorrect, then you replied to it. In any case, I believe this is the clearest way. Your regex will never match more than one time, so why use an array to represent the data? – Erin Ishimoticha Feb 03 '14 at 17:27
5

Generally the answer is to not use prototypes. You use one value by how you handle the argument list, rather than imposing context on it:

sub myfunc { 
  my ($val) = @_; 
  say $val;
}; 
my $var = 'value: 12345'; 
myfunc($var =~ /value: (\d+)/);
myfunc(qw/1 2 3 4 5/)

emits:

12345
1

You can use a list slice to take a subset of a list (in this case, the first item) which then has scalar context imposed on it:

sub myfunc($) { 
  my ($val) = @_; 
  say $val;
}; 
my $var = 'value: 12345'; 
myfunc(($var =~ /value: (\d+)/)[0]);

emits:

12345

This allows the regex operator to operate in list context and return the list of results rather than the imposed scalar context which will return the number of results, slices off a single element list, which then has the scalar context imposed on the first item in the list.

Oesor
  • 6,632
  • 2
  • 29
  • 56
4

In case you want to test if match was successful in the first place, before unconditional function call

sub myfunc
{
   my ($value)=@_;

}

my $some_var='value: 12345'; # For example

myfunc($1) if $some_var =~ /value: (\d+)/;

# or
if (my ($var) = $some_var =~ /value: (\d+)/) {
  myfunc($var);
}
mpapec
  • 50,217
  • 8
  • 67
  • 127
2

Force array context by changing your prototype:

sub myfunc(@) {
    # Do something
}

or better yet, remove it all together:

sub myfunc {
    # Do something
}
RobEarl
  • 7,862
  • 6
  • 35
  • 50
  • Is there a way to do this at the call site, so as not to have to change the sub? Maybe a way to extract the first element of the array resulting from the regex match with capture? – Michael Goldshteyn Feb 03 '14 at 17:23
  • 2
    Even better, don't impose context with prototypes: perl -E "sub myfunc { say for @_ }; my $var = 'value: 12345'; myfunc($var =~ /value: (\d+)/);" emits '12345' – Oesor Feb 03 '14 at 17:24
  • 2
    @MichaelGoldshteyn: You can use a list slice: perl -E "sub myfunc($) { say for @_ }; my $var = 'value: 12345'; myfunc(($var =~ /value: (\d+)/)[0]);" 12345 – Oesor Feb 03 '14 at 17:27
  • @Oesor, yeah, his answer covered that. But I want to pass the first element of the array returned from the regex match in list context rather than take the prototype off of the sub (which may be called from other places). – Michael Goldshteyn Feb 03 '14 at 17:28
  • @Oesor, list slice was exactly what I was looking for, post your comment as an answer and you will get the checkmark! – Michael Goldshteyn Feb 03 '14 at 17:29
  • 3
    Resist the temptation to go perlgolfing! – Erin Ishimoticha Feb 03 '14 at 17:29
  • 4
    `$1` and siblings are common perl programming practice, while [prototypes are not](http://modernperlbooks.com/mt/2009/08/the-problem-with-prototypes.html). – mpapec Feb 03 '14 at 17:33
  • OK, but storing the results of a match (and passing it to a sub in this case) are also idiomatic Perl, or so I would argue. – Michael Goldshteyn Feb 03 '14 at 17:34
  • 1
    @MichaelGoldshteyn: there's actually more implicit steps taking place in the answer you accepted than serafina's whether you like $1 or not. I also find splitting it up more readable. – user3183018 Feb 03 '14 at 17:47