Skip to content

Instantly share code, notes, and snippets.

@hernan604
Last active December 28, 2015 20:39
Show Gist options
  • Select an option

  • Save hernan604/7558496 to your computer and use it in GitHub Desktop.

Select an option

Save hernan604/7558496 to your computer and use it in GitHub Desktop.
Given a book, and two words from that book, create a method to give the smallest number of words between those two words.
package SmallExcerptBetweenTwoWords;
use strict;
use warnings;
use utf8;
use DDP;
use Moo;
=head1 SYNOPSIS
Given a book, and two words from that book, create a method to give the smallest number of words between those two words. Assume that the book is large (over 500 pages) and memory usage and performance is a concern.
=head2 USAGE
You can tell this program to use a file:
$small_excerpt = SmallExcerptBetweenTwoWords->new(
file => "some_text_file.txt ,
wanted_words => [ qw/blalblla sksks/ ]
);
$small_excerpt->start();
or you can specify the text as text_input:
$small_excerpt = SmallExcerptBetweenTwoWords->new(
text_input => "On the beach there WAS a BiG crazy woman throwin sand on everyone" ,
wanted_words => [ qw/biG womAn/ ]
);
$small_excerpt->start();
and you must tell which wanted_words you are looking for. you must pass 2 wanted words.
The words must no have spaces on it, because then its not a word.
=cut
has [qw/wanted_words wanted_words_lc /] => (
is => 'rw',
);
has [qw/best_result all_results/] => (
is => 'rw',
default => sub { {} },
);
has [qw/
file
text_input
errors
word_counter
answer
/ ] => (
is => 'rw',
);
sub validate {
my ( $self ) = @_;
$self->errors( [] );
#check the user passed file and its readable
if ( $self->file ) {
push( @{ $self->errors } ,
"File doesnt exists or i dont have permission" ) if
! -e $self->file
|| ! -r $self->file;
}
#check if user did not pass file and neither text_input
if ( ! $self->file and ! $self->text_input ) {
push( @{ $self->errors } , <<'ERROR_MSG'
You must set file or text_input before calling start() method. ie:
my $small_excerpt = SmallExcerptBetweenTwoWords->new(
file => "text" ,
...
);
or
my $small_excerpt = SmallExcerptBetweenTwoWords->new(
text_input => "some text bla bla bla bla" ,
...
);
ERROR_MSG
);
}
if ( ! $self->wanted_words
|| ref $self->wanted_words ne ref []
|| scalar @{ $self->wanted_words } != 2 ) {
push( @{ $self->errors }, <<'ERROR_MSG'
You must pass in exactly 2 words to look for ex:
SmallExcerptBetweenTwoWords->new(
wanted_words => [ qw/first_word second_word/ ]
...
);
or if you prefer:
SmallExcerptBetweenTwoWords->new(
wanted_words => [ 'first_word' 'second_word' ]
...
);
ERROR_MSG
);
}
if ( $self->wanted_words
and ref $self->wanted_words eq ref []
and scalar @{ $self->wanted_words } == 2
and lc @{ $self->wanted_words }[0] eq lc @{ $self->wanted_words }[1]
) {
push ( @{ $self->errors }, "You must use different words for param wanted_words " );
}
return 0 if scalar @{ $self->errors } > 0;
return 1;
}
before 'start' => sub {
my ( $self ) = @_;
FORCE_CLEANUP: {
$self->all_results( {} );
$self->best_result( {} );
$self->wanted_words_lc( [] );
$self->word_counter(0);
}
map { push( @{ $self->wanted_words_lc }, lc $_ ) } @{ $self->wanted_words };
};
sub start {
my ( $self ) = @_;
return if ! $self->validate;
if ( $self->file ) {
open FILE, $self->file;
READ_FILE : {
my $text;
my $leftover = "";
while ( read FILE, $text, 10 ) {
$text = $leftover . $text ;
while ( $text =~ m/((\S+)\s+)|(\S+)/g ) {
$leftover = ( defined $3) ? "$3" : "";
$self->compute_word( lc $2 ) if defined $2;
}
}
}
}
elsif ( $self->text_input ) {
#if the user passed an input, read it
while ( $self->text_input =~ m/(\S+)/ig ) {
$self->compute_word( lc $1 );
}
}
$self->show_results();
}
sub show_results {
my ( $self ) = @_;
print "\n\n";
if ( keys %{ $self->best_result } ) {
warn "------------------------------------------------------------------";
warn "The smallest number of words between '". join( ' and ', @{$self->wanted_words}) ."' is:\n";
warn "ANSWER: ".$self->best_result->{ words_between } . " words between\n";
} else {
warn "ANSWER: found zero results.\n";
}
}
sub compute_word {
my ( $self, $word ) = @_;
next unless defined $word;
REMOVE_UNWANTED_CHARS: {
$word =~ s/^(\W)//;
$word =~ s/(\W)$//;
}
if ( $self->is_word_1( $word ) ) {
$self->reset_word_counter() if $self->word_counter != 0;
$self->word_counter( $self->word_counter + 1 );
}
elsif ( $self->is_word_2( $word ) and $self->word_counter != 0 ) {
$self->word_counter( $self->word_counter + 1 );
my $words_between = $self->word_counter - 2;
SAVE_THE_BEST_RESULT : {
$self->best_result = {
words_between => $words_between,
} if ! keys $self->best_result
|| $self->best_result->{ words_between } > $words_between;
$self->answer( $self->best_result->{ words_between } );
}
$self->reset_word_counter();
}
elsif ( $self->word_counter != 0 ) {
$self->word_counter( $self->word_counter + 1 );
}
}
sub reset_word_counter {
my ( $self ) = @_;
$self->word_counter(0);
}
sub is_word_1 {
my ( $self, $word ) = @_;
return 1 if @{ $self->wanted_words_lc }[0] eq $word;
return 0;
}
sub is_word_2 {
my ( $self, $word ) = @_;
return 1 if @{ $self->wanted_words_lc }[1] eq $word;
return 0;
}
1;
echo "save the files above inside the same dir and execute: "
prove -I. tests.pl
use Test::More;
use utf8;
use lib "./";
use SmallExcerptBetweenTwoWords;
my $small_excerpt = SmallExcerptBetweenTwoWords->new(
file => "text" ,
wanted_words => [ qw/grande palaVra/ ],
);
$small_excerpt->start();
is_deeply(
$small_excerpt->best_result ,
{
words_between => 0
}
,"Found expected result"
);
is( $small_excerpt->answer , 0, "found correct answer, there are 0 words at the best answer" );
WORDS_THAT_DONT_EXISTS: {
$small_excerpt = SmallExcerptBetweenTwoWords->new(
file => "text" ,
wanted_words => [ qw/blalblla sksks/ ]
);
$small_excerpt->start();
isnt( $small_excerpt->answer , defined, "did not find any answer for the given words" );
}
#use DDP;warn p $small_excerpt->best_result;
LOWER_CASE_UPPERCASE:{
$small_excerpt = SmallExcerptBetweenTwoWords->new(
text_input => "On the beach there WAS a BiG crazy woman throwin sand on everyone" ,
wanted_words => [ qw/biG womAn/ ]
);
$small_excerpt->start();
is( $small_excerpt->answer , 1, "correct" );
}
PASS_ONLY_ONE_WORD_AS_ARGUMENT_SHOULD_THROW_ERROR: {
$small_excerpt = SmallExcerptBetweenTwoWords->new(
text_input => "On the beach there WAS a BiG crazy woman throwin sand on everyone" ,
wanted_words => [ qw/biG/ ]
);
$small_excerpt->start();
isnt( $small_excerpt->answer , defined, "user must pass 2 words" );
ok( scalar( @{ $small_excerpt->errors } ) >= 1, " There are errors" );
ok( grep( /You must pass in exactly 2 words to look for/ , @{ $small_excerpt->errors } ) , "error msg in place" );
}
DONT_PASS_A_FILE: {
$small_excerpt = SmallExcerptBetweenTwoWords->new(
wanted_words => [ qw/biG data/ ]
);
$small_excerpt->start();
isnt( $small_excerpt->answer , defined, "user did not pass a file, should fail" );
ok( scalar( @{ $small_excerpt->errors } ) >= 1, " There are errors" );
ok( grep( /You must set file or text_input before calling start/, @{ $small_excerpt->errors } ) , "error msg in place" )
}
ALLOW_ME_TO_PASS_TEXT_INPUT:{
$small_excerpt = SmallExcerptBetweenTwoWords->new(
text_input => "the sun \nis shinning the the moon is dark. if its dark we see moon, else we see sun." ,
wanted_words => [ qw/sun moon/ ]
);
$small_excerpt->start();
is( $small_excerpt->answer , 4, "worked" );
}
SOME_NEW_LINES_IN_INPUT:{
$small_excerpt = SmallExcerptBetweenTwoWords->new(
text_input => <<TEXT,
the sun
is shinning the
the moon is dark. if its dark we see moon, else we see sun.
TEXT
wanted_words => [ qw/sun moon/ ]
);
$small_excerpt->start();
is( $small_excerpt->answer , 4, "worked" );
}
DOUBLE_CHECK_NEWLINES_IN_INPUT:{
$small_excerpt = SmallExcerptBetweenTwoWords->new(
text_input => <<TEXT,
the sun
is shinning the
the moon is dark. if its dark we see moon, else we see sun.
The sun rises and the moon sets
TEXT
wanted_words => [ qw/sun moon/ ]
);
$small_excerpt->start();
is( $small_excerpt->answer , 3, "worked" );
}
BIG_TEXT_WITH_WORD_IN_BEGINING_AND_END:{
$small_excerpt = SmallExcerptBetweenTwoWords->new(
text_input => <<TEXT,
Sun Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla dolor odio, mollis ut tristique quis, malesuada ut lorem. Quisque at rutrum nulla. Duis tempor at ligula pharetra congue. Aenean quis elementum arcu, vel consectetur mauris. Praesent pharetra nisi ut risus tempor tempor. Nunc mattis erat nunc, vel tempus velit sodales aliquam. Phasellus ultrices ullamcorper aliquam.
Nulla scelerisque felis placerat odio pulvinar, eget egestas turpis lacinia. Nam id suscipit dolor. Suspendisse orci urna, tristique quis leo eget, suscipit auctor sem. Integer scelerisque consectetur velit, id pellentesque est rhoncus et. Integer at arcu lacus. Etiam auctor eros vitae elit malesuada placerat. Nulla turpis tortor, volutpat eget aliquet nec, faucibus vitae moon.
TEXT
wanted_words => [ qw/sun moon/ ]
);
$small_excerpt->start();
is( $small_excerpt->answer , 108, "worked" );
}
PASS_SAME_WORD:{
$small_excerpt = SmallExcerptBetweenTwoWords->new(
text_input => <<TEXT,
Sun Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla dolor odio, mollis ut tristique quis, malesuada ut lorem. Quisque at rutrum nulla. Duis tempor at ligula pharetra congue. Aenean quis elementum arcu, vel consectetur mauris. Praesent pharetra nisi ut risus tempor tempor. Nunc mattis erat nunc, vel tempus velit sodales aliquam. Phasellus ultrices ullamcorper aliquam.
Nulla scelerisque felis placerat odio pulvinar, eget egestas turpis lacinia. Nam id suscipit dolor. Suspendisse orci urna, tristique quis leo eget, suscipit auctor sem. Integer scelerisque consectetur velit, id pellentesque est rhoncus et. Integer at arcu lacus. Etiam auctor eros vitae elit malesuada placerat. Nulla turpis tortor, volutpat eget aliquet nec, faucibus vitae moon.
TEXT
wanted_words => [ qw/sun sun/ ]
);
$small_excerpt->start();
ok( grep( /You must use different words for param wanted_words/ , @{ $small_excerpt->errors } ), "user should not pass the same word twice" );
}
done_testing();
1;
um grande texto pode
incluir uma palavra pequena e grande mas um texto grande realmente não pode incluir uma palavra muito pequena. Será que pode incluir palavra grande ? eu não sei mas estas palavras grandes podem ser complicadas de encontrar pois uma palavra pode estar fora de ordem equanto outra palavra pode estar na ordem. Já o trecho que tem menos palavras entre grande
palavra só pode ser este, que vai resultar em zero.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment