http://www.perlmonks.org?node_id=1030963

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

Hi. If I have the following:

$string="A111B111A111B111"; $yes_or_no="no";

Is there a way to read that string and set/reset $yes_or_no to "yes" each time it finds an A and back "no" when it finds the subsequent B?

Thanks!

Joe

Replies are listed 'Best First'.
Re: Change variable multiple times within a single string?
by toolic (Bishop) on Apr 27, 2013 at 13:51 UTC
    split
    use warnings; use strict; my $string="A111B111A111B111"; my $yes_or_no="no"; for (split //, $string) { if ($_ eq 'A') { $yes_or_no = 'yes'; print "$_ yes_or_no=$yes_or_no\n"; } if ($_ eq 'B') { $yes_or_no = 'no'; print "$_ yes_or_no=$yes_or_no\n"; } } __END__ A yes_or_no=yes B yes_or_no=no A yes_or_no=yes B yes_or_no=no
Re: Change variable multiple times within a single string?
by hdb (Monsignor) on Apr 27, 2013 at 14:01 UTC

    Use a translation table (hash):

    use strict; use warnings; my $string="A111B111A111B111"; my $yes_or_no="no"; my %decision = ( A => 'yes', B=> 'no' ); for (split //, $string ) { $yes_or_no = $decision{$_} if exists $decision{$_}; print "$_ ($yes_or_no)\n"; }

      Hi hdb,
      Your solution would not only either set/reset and print the value 'yes' and 'no' for 'A' and 'B', but would also do for subsequent '1' also. And such that either 'yes' or 'no', were printed when the value of '1' is seen.
      Though, the OP wanted 'yes' or 'no', for 'A' and 'B'.
      So, I added this next if /1/; like so:

      for (split //, $string ) { next if /1/; $yes_or_no = $decision{$_} if exists $decision{$_}; print "$_ ($yes_or_no)\n"; }
      Though the OP didn't give any information on what he expect when '1' is seen.

      If you tell me, I'll forget.
      If you show me, I'll remember.
      if you involve me, I'll understand.
      --- Author unknown to me

        That is not correct! The value of the variable $yes_or_no will only be changed on the occurrence of 'A' or 'B' in the string. That is what  if exists $decision{$_}; is for, there is no entry of for '1' in the hash.

        Printing the variable in each line is only showing the current value. The OP had not really said what he wanted to do while running through the string, so I made something up.

        In a way, the print statement is completely superfluous, as the script would set the variable according to the wishes of the OP, but it would not be observable...

Re: Change variable multiple times within a single string?
by davido (Cardinal) on Apr 27, 2013 at 16:09 UTC

    my $state = 'no'; my $string = 'A111B111A111B111'; while( $string =~ m/ (?<chunk> (?<condition>[AB]) (?<value>\d+) ) /gx ) { $state = $+{condition} eq 'A' ? 'yes' : 'no'; print "($+{chunk}) => State: ($state) => Value ($+{value})\n"; }

    Dave

      That prints all yeses for me and a bunch of warnings (change == to eq, perhaps?).

        :) Thanks. Fixed.

        (== should have been eq)


        Dave

Re: Change variable multiple times within a single string?
by BillKSmith (Monsignor) on Apr 27, 2013 at 15:56 UTC
    If you only need the final state. This code returns 'yes' only if there is an 'A' in the string and there is no 'B' after it.
    use warnings; use strict; my $string="A111B111A111B111"; my $yes_or_no= ($string =~ m/A[^B]*$/) ? 'yes' : 'no'; print "yes_or_no=$yes_or_no\n";
    Update: The original description is not quite accurate. It should read "This code returns 'yes' only if there is at least one 'A' in the string and there is no 'B' after the final 'A'."
    Bill
Re: Change variable multiple times within a single string?
by AnomalousMonk (Archbishop) on Apr 27, 2013 at 19:58 UTC

    Another possibility:

    >perl -wMstrict -le "for my $s (qw( B111 B111B111 B111B111B111 A111 A111A111 A111A111A111 A111B111 A111B111A111 A111B111A111B111 A111B111A111B111A111 B111A111 B111A111B111 B111A111B111A111 B111A111B111A111B111 B BB BBB A AA AAA BA BAB AB ABA xxx xx x), '', ) { local our $y_n; ()= $s =~ m{ ([AB]) (?{ $y_n = $^N eq 'A' ? 'yes' : 'no' }) }xmsg; if (defined $y_n) { printf qq{%3s: '%s' \n}, $y_n, $s; } else { warn qq{no A or B found in '$s'}; } } " no: 'B111' no: 'B111B111' no: 'B111B111B111' yes: 'A111' yes: 'A111A111' yes: 'A111A111A111' no: 'A111B111' yes: 'A111B111A111' no: 'A111B111A111B111' yes: 'A111B111A111B111A111' yes: 'B111A111' no: 'B111A111B111' yes: 'B111A111B111A111' no: 'B111A111B111A111B111' no: 'B' no: 'BB' no: 'BBB' yes: 'A' yes: 'AA' yes: 'AAA' yes: 'BA' no: 'BAB' no: 'AB' yes: 'ABA' no A or B found in 'xxx' at -e line 1. no A or B found in 'xx' at -e line 1. no A or B found in 'x' at -e line 1. no A or B found in '' at -e line 1.

    Update: And allowing for the possibility of side-effects (e.g., the values in the  %xlat hash could be anonymous subroutine references) (note:  //p and  ${^MATCH} only available in Perl 5.10+):

    >perl -wMstrict -le "my %xlat = qw(A yes B no); ;; my $set = join '', map quotemeta, keys %xlat; $set = qr{ [$set] }xms; ;; my $xlat_keys = join ' or ', keys %xlat; ;; for my $s (qw( B111 B111B111 B111B111B111 A111 A111A111 A111A111A111 A111B111 A111B111A111 A111B111A111B111 A111B111A111B111A111 B111A111 B111A111B111 B111A111B111A111 B111A111B111A111B111 B BB BBB A AA AAA BA BAB AB ABA xxx xx x), '', ) { local our $y_n; use re 'eval'; ()= $s =~ m{ $set (?{ $y_n = $xlat{${^MATCH}} }) }xmspg; if (defined $y_n) { printf qq{%3s: '%s' \n}, $y_n, $s; } else { warn qq{no $xlat_keys found in '$s'}; } } " no: 'B111' no: 'B111B111' no: 'B111B111B111' yes: 'A111' yes: 'A111A111' yes: 'A111A111A111' no: 'A111B111' yes: 'A111B111A111' no: 'A111B111A111B111' yes: 'A111B111A111B111A111' yes: 'B111A111' no: 'B111A111B111' yes: 'B111A111B111A111' no: 'B111A111B111A111B111' no: 'B' no: 'BB' no: 'BBB' yes: 'A' yes: 'AA' yes: 'AAA' yes: 'BA' no: 'BAB' no: 'AB' yes: 'ABA' no A or B found in 'xxx' at -e line 1. no A or B found in 'xx' at -e line 1. no A or B found in 'x' at -e line 1. no A or B found in '' at -e line 1.
Re: Change variable multiple times within a single string?
by dd-b (Monk) on Apr 27, 2013 at 19:37 UTC

    Another variant I haven't seen; processing through the list backwards lets you stop as soon as you find an 'A' or 'B'. (That does mean this won't work if setting that variable is just a proxy for a more complex process with side-effects that really does need to be executed for each letter in the string.)

    #! /usr/bin/perl $string="A111B111A111B111"; $yes_or_no="no"; $yes_or_no = ''; for (my $x=1; $x<=length($string); $x++) { my $c = substr($string, -1 * $x, 1); $yes_or_no = "yes" if ($c eq 'A'); $yes_or_no = "no" if ($c eq 'B'); last if $yes_or_no; } print "yes_or_no $yes_or_no\n";
      use warnings; use strict; my $string="A111B111A111B111"; my $yes_or_no="no"; for (reverse(split //, $string)) { if ($_ eq 'A') { $yes_or_no = 'yes'; print "$_ yes_or_no=$yes_or_no\n"; last; } if ($_ eq 'B') { $yes_or_no = 'no'; print "$_ yes_or_no=$yes_or_no\n"; last; } }