Aqui está um script Perl apenas para mostrar como é possível usar o operador bitwise exclusive ou (XOR) do Perl ( ^
).
O script
Eu chamei de cmp.pl
.
#!/usr/bin/perl -w
use strict;
use warnings;
# $s1 = "http://unix.stackexchange.com/questions/tagged/linux?page=2&sort=newest&pagesize=15";
# $s2 = "http://unix.stackexchange.com/questions/tagged/linux?page=3&sort=newest&pagesize=15";
# $np = 115
my $s1 = $ARGV[0];
my $s2 = $ARGV[1];
my $np = $ARGV[2];
my $posOfDiff;
my $mask = $s1 ^ $s2;
while ($mask =~ /[^my $mask = $s1 ^ $s2;
while ($mask =~ /[^my $mask = $s1 ^ $s2;
printf "[$_] is 0x%02x\n", ord($_) for split //, $mask;
]/g) {
$posOfDiff = $-[0];
}
]/g) {
$posOfDiff = $-[0];
}
for (my $idx = 1; $idx <= $np; $idx++) {
my $newStr = $s1;
substr($newStr,$posOfDiff,1) = $idx;
print "$newStr\n";
}
Detalhes
O recurso exclusivo deste script é o uso do operador Perl ( ^
). O poder dessa abordagem está neste snippet de código:
...
[] is 0x00
[] is 0x00
[] is 0x00
[] is 0x00
[] is 0x00
[ ] is 0x01
[] is 0x00
[] is 0x00
...
O acima irá criar uma máscara ( $mask
) usando as duas seqüências. Uma máscara XOR é um vetor que conterá um 0 para valores que correspondam entre $s1
e $s2
e um 1 em que eles diferem. Você pode adicionar esta linha de código se quiser se convencer disso:
$ perl -we '$a="ab"; $b="ac"; $c=$a ^ $b; printf "[$_] is 0x%02x\n", ord($_) for split //, $c;'
[] is 0x00
[] is 0x01
$ perl -we '$a="ab"; $b="ad"; $c=$a ^ $b; printf "[$_] is 0x%02x\n", ord($_) for split //, $c;'
[] is 0x00
[] is 0x06
$ perl -we '$a="ab"; $b="ae"; $c=$a ^ $b; printf "[$_] is 0x%02x\n", ord($_) for split //, $c;'
[] is 0x00
[] is 0x07
Innards of mask
Este printf
produzirá resultados como este. OBSERVAÇÃO: que os caracteres não podem ser impressos, são valores hexadecimais. O 0x00 é o valor hexadecimal para o caractere nulo, 0x01 é um 1.
while ($mask =~ /[^in the loop
what we're looking for:58
]/g) {
print "in the loop\n";
print "what we're looking for:" . $-[0] . "\n";
O valor que está sendo retornado quando algo diferente de 0 significa que os valores são diferentes. Outros exemplos:
$mask =~ /[^#!/usr/bin/perl -w
use strict;
use warnings;
# $s1 = "http://unix.stackexchange.com/questions/tagged/linux?page=2&sort=newest&pagesize=15";
# $s2 = "http://unix.stackexchange.com/questions/tagged/linux?page=3&sort=newest&pagesize=15";
# $np = 115
my $s1 = $ARGV[0];
my $s2 = $ARGV[1];
my $np = $ARGV[2];
my $posOfDiff;
my $mask = $s1 ^ $s2;
while ($mask =~ /[^my $mask = $s1 ^ $s2;
while ($mask =~ /[^my $mask = $s1 ^ $s2;
printf "[$_] is 0x%02x\n", ord($_) for split //, $mask;
]/g) {
$posOfDiff = $-[0];
}
]/g) {
$posOfDiff = $-[0];
}
for (my $idx = 1; $idx <= $np; $idx++) {
my $newStr = $s1;
substr($newStr,$posOfDiff,1) = $idx;
print "$newStr\n";
}
]/g
Looping através da máscara
O outro recurso interessante do loop while
é que ele percorre apenas os caracteres de $mask
que não são nulos ( while
). Então, no seu exemplo, na verdade, estamos apenas executando o loop while 1 vez, já que há apenas uma diferença entre as duas strings. Se houvesse 2 diferenças, executaria 2 vezes. Então, essa é uma maneira bastante eficiente de fazer isso.
Se precisar de mais convincência, veja algumas linhas adicionais de código que podem ser adicionadas que mostram o loop $posOfDiff
em ação:
...
[] is 0x00
[] is 0x00
[] is 0x00
[] is 0x00
[] is 0x00
[ ] is 0x01
[] is 0x00
[] is 0x00
...
Estas linhas são exibidas apenas uma vez:
$ perl -we '$a="ab"; $b="ac"; $c=$a ^ $b; printf "[$_] is 0x%02x\n", ord($_) for split //, $c;'
[] is 0x00
[] is 0x01
$ perl -we '$a="ab"; $b="ad"; $c=$a ^ $b; printf "[$_] is 0x%02x\n", ord($_) for split //, $c;'
[] is 0x00
[] is 0x06
$ perl -we '$a="ab"; $b="ae"; $c=$a ^ $b; printf "[$_] is 0x%02x\n", ord($_) for split //, $c;'
[] is 0x00
[] is 0x07
Salvando a posição da diferença
Quando uma correspondência for encontrada, o corpo do loop while será executado e a posição será registrada na variável while
. Como? A beleza aqui é o uso da variável $ - [0]. Isso nos dará o deslocamento da posição da última partida bem sucedida.
$-[0] is the offset of the start of the last successful match.
Esta correspondência é o que está ocorrendo na porção de controle do loop $mask
, estamos procurando por caracteres em g
que NÃO sejam o caractere nulo ( $mask
), daí nosso caráter de diferença:
while ($mask =~ /[^in the loop
what we're looking for:58
]/g) {
print "in the loop\n";
print "what we're looking for:" . $-[0] . "\n";
OBSERVAÇÃO: O trailing %code% informa a função de correspondência no Perl para fazer isso globalmente, portanto, ele continuará encontrando correspondências até esgotar a string, %code% .
O que mais?
O resto deste script é praticamente clichê Perl, não vale a pena discutir mais.
Referências