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

I have a $line='boy boy girl boy girl boy girl girl'; I want to replace 'boy' with 'man', but I don't want to replace all the 'boy's, I only want to replace (randomly) one of the 'boy' with 'man'. How do I do that? I have a feeling that it has something to do with pos() but I really don't know exactly how?

Replies are listed 'Best First'.
Re: Randomly regex substitute
by ikegami (Patriarch) on Dec 24, 2010 at 05:41 UTC
    my @words = split ' ', $line; my @boy_idxs = grep $words[$_] eq 'boy', 0..$#words; $words[$boy_idxs[rand(@boy_idxs)]] = 'man'; $line = join ' ', @words;

    Update: This one doesn't mess up the whitespace:

    my @boy_idxs; push @boy_idxs, $-[0] while $line =~ /\b boy \b/xg; substr($line, $boy_idxs[rand(@boy_idxs)], length('boy')) = 'man';

    @-

Re: Randomly regex substitute
by Marshall (Canon) on Dec 24, 2010 at 06:35 UTC
    Another way...
    I used "cowboy" instead of "man" for the substitute for "boy" to make sure that I could put a different length string in there. This will have to "get smarter" if for example "girlyboy" was one of tokens in $line".
    #!/usr/bin/perl -w use strict; my $line='boy boy girl boy girl boy girl girl'; my $count =()= $line =~ /boy/g; #number of boys on line print "number of boys = $count\n"; my $num = int(rand($count)) + 1; #1..$count print "looking for nth boy: $num\n"; my $found_position =-1; while ($num--) { $found_position= index($line,"boy", $found_position+1); print "replace index = $found_position\n"; } substr ($line, $found_position, length("boy"), "cowboy"); print "$line\n"; __END__ One example run: number of boys = 4 looking for nth boy: 3 replace index = 0 replace index = 4 replace index = 13 boy boy girl cowboy girl boy girl girl
    Update: Another solution:
    #!/usr/bin/perl -w use strict; use Data::Dumper; my $line='boy boy girl boy girl boy girlyboy girl girl'; my @indicies; while ($line =~ /\bboy\b/gi) { push (@indicies, pos($line)-3); #pos is a bit tricky } print Dumper \@indicies; my $num = int(rand(@indicies)); #0..$count print "line = $line\n"; print "looking for nth boy: ",$num+1,"\n"; substr ($line, $indicies[$num], length("boy"), "cowboy"); print " $line\n"; __END__ Some runs: $VAR1 = [ 0, 4, 13, 22 ]; line = boy boy girl boy girl boy girlyboy girl girl looking for nth boy: 3 boy boy girl cowboy girl boy girlyboy girl girl ..... $VAR1 = [ 0, 4, 13, 22 ]; line = boy boy girl boy girl boy girlyboy girl girl looking for nth boy: 1 cowboy boy girl boy girl boy girlyboy girl girl ..... $VAR1 = [ 0, 4, 13, 22 ]; line = boy boy girl boy girl boy girlyboy girl girl looking for nth boy: 4 boy boy girl boy girl cowboy girlyboy girl girl
    oh, "Girly Boy" came about from an off hand comment from our out-going California Governor, Arnold Schwarzenegger - it became a bit of a joke, nothing was meant in any seriously derogatory way.
Re: Randomly regex substitute
by Jim (Curate) on Dec 24, 2010 at 06:36 UTC
      Just curious, what if instead of replacing ONE boy, we are going to replace N boys? substr will definitely fail if the replacement string is of a different length?

        You replied to my post, but I think you intended to ask someone else this question — someone who used substr in his or her solution.

        My solution works with any arbitrary regular expression pattern and any arbitrary replacement string. I think my solution is the one most likely to earn the OP an A+.

Re: Randomly regex substitute
by samarzone (Pilgrim) on Dec 24, 2010 at 07:15 UTC

    Yet another solution (without claiming efficiency over previous solutions)

    #!/usr/bin/perl -wl use strict; my $str = "boy boy girl boy girl boy girl girl"; my $l = int(rand(rindex($str,"boy") - 1)); $str =~ s/^(.{$l}.*?)\bboy\b/$1man/; print $str;
    --
    Regards
    - Samar

      Unfortunately, this one isn't very random - at least with regard to the first occurrence of 'boy'. I see where you tried to correct it with the '- 1', but that doesn't work:

      #!/usr/bin/perl -wl use strict; my %data; for (1..10000){ my $str = "boy boy girl boy girl boy girl girl"; my $l = int(rand(rindex($str,"boy") - 1)); $str =~ s/^(.{$l}.*?)\bboy\b/$1man/; $data{$str}++; } print "$_: $data{$_}" for sort keys %data;

      Output:

      boy boy girl boy girl man girl girl: 3323 boy boy girl man girl boy girl girl: 4310 boy man girl boy girl boy girl girl: 1881 man boy girl boy girl boy girl girl: 486

      (Don't ask me why I know about this problem. :)))


      --
      "Language shapes the way we think, and determines what we can think about."
      -- B. L. Whorf
Re: Randomly regex substitute
by JavaFan (Canon) on Dec 24, 2010 at 10:18 UTC
    my $N = 1; # Number of replacements. my $a = "boy boy girl boy girl boy girl girl"; my @b = split ' ', $a; my $c = grep {$_ eq "boy"} @b; my @d = map {$_ eq "boy" && rand($c--) < $N ? do {$N--; "man"} : $_} @ +b; say "@d";
Re: Randomly regex substitute
by oko1 (Deacon) on Dec 24, 2010 at 18:01 UTC

    Somewhat different approach:

    #!/usr/bin/perl -w use strict; my $line='boy boy girl boy girl boy girl girl'; my ($pos, @pos); my $last = 0; { $pos = index $line, 'boy', $last; last if $pos == -1; push @pos, $pos; $last = $pos + 1; redo; } substr $line, $pos[rand(@pos)], 3, 'man'; print "$line\n";

    Update: Same idea but using 'pos()':

    #!/usr/bin/perl -wl use strict; my @pos; $_ = 'boy boy girl boy girl boy girl girl'; push @pos, pos() - 3 while /boy/g; substr $_, $pos[rand @pos], 3, 'man'; print;

    --
    "Language shapes the way we think, and determines what we can think about."
    -- B. L. Whorf
Re: Randomly regex substitute
by Anonymous Monk on Dec 24, 2010 at 07:20 UTC
    I am curious to know the name of your school, class, teacher :)