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

$a = 'aa'; $a =~ s/a/b/; print $a
but cannot use
$a = 'aa'; $b = 's/a/b/';#or $b = qr(s/a/b/) $a =~ $b; print $a
did not work! have to write like this
($a) = map {eval $b} $a;
I used to think qr take care of all regex expressions, but it's sad to find substitution donot work along.

Replies are listed 'Best First'.
Re: surprised to find cannot assign s/../../ to a variable
by ikegami (Patriarch) on Jul 05, 2024 at 15:03 UTC
    When you don't provide a valid operator (m//, s/// or tr///) on the right-hand side of the binding operator (=~), it's presumed you meant to use a match operator (m//). That means that
    $a =~ $b
    is short for
    $a =~ m/$b/;

    There's no reason to expect any of the following to evaluate the Perl code found in $b:

    $p = '4+5*6'; $str =~ m/$p/; # Doesn't perform arithmetic. $p = 'm/a/'; $str =~ m/$p/; # Doesn't search for `a`. $p = 's/a/b/'; $str =~ m/$p/; # Doesn't perform a substitution.

    Same:

    $p = '4+5*6'; $str =~ $p; # Doesn't perform arithmetic. $p = 'm/a/'; $str =~ $p; # Doesn't search for `a`. $p = 's/a/b/'; $str =~ $p; # Doesn't perform a substitution.

    qr// compiles a regular expression, not Perl code. It doesn't help at all here.

    $p = '4+5*6'; $re = qr/$p/; $str =~ m/$re/; # Still doesn't. $p = 'm/a/'; $re = qr/$p/; $str =~ m/$re/; # Still doesn't. $p = 's/a/b/'; $re = qr/$p/; $str =~ m/$re/; # Still doesn't.

    Same:

    $p = '4+5*6'; $re = qr/$p/; $str =~ $re; # Still doesn't. $p = 'm/a/'; $re = qr/$p/; $str =~ $re; # Still doesn't. $p = 's/a/b/'; $re = qr/$p/; $str =~ $re; # Still doesn't.

    have to write like this

    Yes, if you have Perl code you wish to compile and run, you will need to pass it to eval EXPR, do, require, use, or to the perl program itself.


    I used to think qr take care of all regex expressions

    You're not trying to "take care of a regex expression", whatever that means. You are trying to perform a substitution. That requires the use of the substitution operator (s///), but you used a match operator (m//) in your broken code.

    qr// compiles a regular expression. That's it. It doesn't perform any matches or substitutions. To perform matches or substitutions, you still need Perl code that uses the match operator (m//) or the substitution operator (s///).


    Proper use:

    my $pat = 'a'; my $re = qr/$pat/; $str =~ m/$re/ # Performs a match. $str =~ /$re/ # Same $str =~ $re # Same $str =~ m/$pat/ # No need to compile in advance. $str =~ /$pat/ # Same $str =~ $pat # Same
    my $pat = 'a'; my $repl = 'b'; my $re = qr/$pat/; $str =~ s/$re/$repl/ # Performs a substitution. $str =~ s/$pat/$repl/ # No need to compile in advance.

    Use String::Substitution if the replacement string contains $1, etc.

Re: surprised to find cannot assign s/../../ to a variable
by LanX (Saint) on Jul 05, 2024 at 12:57 UTC
    qr is meant for regexes only, s/// is a complex command where only the left part is a regex, see also m//

    For storing complex code use a subroutine.

    DB<6> sub repl { $_[0] =~ s/a/b/ } DB<7> p $a aa DB<8> repl $a DB<9> p $a ba

    Tho your motivation is unclear for me , smells like an XY problem

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery

      I was try to write
      my $namematch = defined $options{r} ? qr($options{r}) : qr{s/\.[^.]+$/ +/r}; my @name = $file =~ $namematch;
      now I have to write this
      my $namematch = qr($options{r}); my @name = defined $options{r} ? $file =~ $namematch : $file =~ s/(\.[ +^.]+)+$//r;
      there will be many cases I donot want write substitudion code dead in the script.
        Maybe you need two options? $options{pattern} = some-pattern; $options{replacement} = some-replacement
        my $pattern = defined $options{pattern} ? qr{$options{pattern}} : $def +ault_pattern; my $replacement = defined $options{replacement} ? $options{replacement +} : $default_replacement; $text =~ s/$pattern/$replacement/;
        As I said

        > > For storing complex code use a subroutine

        > > sub repl { $_[0] =~ s/a/b/ }

        Hence...

        DB<9> %options = ( r => sub { $_[0] =~ s/(a)/A/gr } ) # r is set DB<10> x @name = $options{r}->('abc') 0 'Abc' DB<11> $options{b} //= sub { $_[0] =~ s/(b)/B/gr } # b defaulted if + undefined DB<12> x @name = $options{b}->('abc') 0 'aBc' DB<13> $options{r} //= sub { $_[0] =~ s/(b)/B/gr } # r exists DB<14> x @name = $options{r}->('abc') 0 'Abc' DB<15>

        Your use of a list assignment to @name in combination with /r looks wrong tho

        Anyway, welcome in the world of functional programming

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

Re: surprised to find cannot assign s/../../ to a variable
by tybalt89 (Monsignor) on Jul 05, 2024 at 15:31 UTC

    How about:

    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11160391 use warnings; # wanted #my $namematch = defined $options{r} ? qr($options{r}) : qr{s/\.[^.]+$ +//r}; #my @name = $file =~ $namematch; my $file = 'foobar.txt'; my %options = ( r => '(.{3})', other => '.{3}(.{3})' ); # case for defined { my $namematch = defined $options{r} ? sub { /$options{r}/ } : sub { s/ +\.[^.]+$//r }; my @name = map { $namematch->() } $file; print "defined case, \@name = @name\n"; } # case for not defined { %options = (); my $namematch = defined $options{r} ? sub { /$options{r}/ } : sub { s/ +\.[^.]+$//r }; my @name = map { $namematch->() } $file; print "not defined case, \@name = @name\n"; }

    Outputs:

    defined case, @name = foo not defined case, @name = foobar

    Avoids the eval

      yeah,thanks. But refed subroutine cannot do all eval do. $options{r} did not mean I want pattern match only, I use substitution if it's simpler.
Re: surprised to find cannot assign s/../../ to a variable
by The_Dj (Scribe) on Jul 09, 2024 at 03:07 UTC
    Going back to your original question;
    You can do this:
    $a='aa'; $b='s/a/b/'; eval '$a =~ '.$b;

    That means you can write:
    my $namematch = $options{r} // 's/\.[^.]+$/+/r'; my @name; eval '@name = $file =~ '.$namematch;

    But you're basically just having Perl write Perl for you. This may be what you want.
    SANITIZE YOUR INPUT. The value in $options{r} could be used for injection.

    HTH.
      yeah, this is the best workaround