RabbitFarm
2025-04-06
The Weekly Challenge 315 (Prolog Solutions)
The examples used here are from the weekly challenge problem statement and demonstrate the working solution.
Part 1: Find Words
You are given a list of words and a character. Write a script to return the index of word in the list where you find the given character.
This can be done with a basic predicate with findall. Or we can use maplist! maplist is more fun in my opinion, so we’ll go with that.
First off a small utility predicate to see if a word contains a given letter.
-
contains_letter(Letter, Index-Word, Index):-
atom_chars(Word, C),
member(Letter, C).
contains_letter(_, _, -1).
◇
-
Fragment referenced in 4.
You can see that we are sending that previous predicate a pair. The first element in that pair is an index. Let’s build a word index, mainly for the sake of avoiding the use of between and findall. Those builtin predicates are fine, but aesthetically to me it seems nicer to not use them if we absolutely don’t have to.
-
word_index(Words, Index):-
word_index(Words, 0, Index).
word_index([], _, []).
word_index([H|T], N, [I-H|Index]):-
succ(N, I),
word_index(T, I, Index).
◇
-
Fragment referenced in 4.
Now we’ll use maplist to find the words.
-
find_words(Words, Letter, Indices):-
word_index(Words, Index),
maplist(contains_letter(Letter), Index, I),
delete(I, -1, Indices).
◇
-
Fragment referenced in 4.
The rest of the code just wraps this predicate into a file.
Sample Run
$ gprolog --consult-file prolog/ch-1.p | ?- find_words([the, weekly, challenge], e, Indices). Indices = [1,2,3] ? yes | ?- find_words([perl, raku, python], p, Indices). Indices = [1,3] ? yes | ?- find_words([abc, def, bbb, bcd], b, Indices). Indices = [1,3,4] ? yes | ?-
Part 2: Find Third
You are given a sentence and two words. Write a script to return all words in the given sentence that appear in sequence to the given two words.
In the first part I mentioned the between and findall predicates. Here we will use them to find successive words.
-
find_third(Words, One, Two, Thirds):-
length(Words, WordLength),
N is WordLength - 2,
findall(Third, (
between(1, N, I),
succ(I, J),
nth(I, Words, One),
nth(J, Words, Two),
succ(J, K),
nth(K, Words, Third)
), Thirds).
◇
-
Fragment referenced in 6.
Finally, let’s assemble our completed code into a single file.
Sample Run
$ gprolog --consult-file prolog/ch-2.p | ?- find_third([’Perl’, is, a, my, favourite, language, but, ’Python’, is, my, favourite, too], my, favourite, Thirds). Thirds = [language,too] yes | ?- find_third([’Barbie’, is, a, beautiful, doll, also, also, a, beautiful, princess], a, beautiful, Thirds). Thirds = [doll,princess] yes | ?- find_third([we, will, we, will, rock, you, rock, you], we, will, Thirds). Thirds = [we,rock] yes | ?-
References
posted at: 17:41 by: Adam Russell | path: /prolog | permanent link to this entry
Finding a Third of the Words
The examples used here are from the weekly challenge problem statement and demonstrate the working solution.
Part 1: Find Words
You are given a list of words and a character. Write a script to return the index of word in the list where you find the given character.
This can be done in essentially one line. Rather than write a true Perl one-liner for the command line though, we’ll package this into a single subroutine.
Here’s our one subroutine.
-
sub find_words{
my($s, $c) =
@_;
return grep {$s->[$_] =~ m/$c/} 0 ..
@{$s} - 1;
}
◇
-
Fragment referenced in 2.
Putting it all together...
The rest of the code just runs some simple tests.
-
MAIN:{
say q/(/ . join(q/, /, find_words([q/the/, q/weekly/, q/challenge/], q/e/)). q/)/;
say q/(/ . join(q/, /, find_words([q/perl/, q/raku/, q/python/], q/p/)) . q/)/;
say q/(/ . join(q/, /, find_words([q/abc/, q/def/, q/bbb/, q/bcd/], q/b/)) . q/)/;
}
◇
-
Fragment referenced in 2.
Sample Run
$ perl perl/ch-1.pl (0, 1, 2) (0, 2) (0, 2, 3)
Part 2: Find Third
You are given a sentence and two words. Write a script to return all words in the given sentence that appear in sequence to the given two words.
Similar to the first part this will be a single short subroutine. We’re just going to loop over the words and match as we go. There are two small things to note here: we strip out any punctuation from our sentence and the empty string q// is considered by Perl to be a false value. The latter is only important in that is how we initialize $next.
-
sub find_third{
my ($s, $first, $second) =
@_;
$s =~ s/[[:punct:]]//g;
my
@thirds = ();
my($previous, $current, $next) = (q//, q//, q//);
do{
push
@thirds, $_ if $next;
$current = $_;
$next = 1 if $previous eq $first && $current eq $second;
$next = 0 unless $previous eq $first && $current eq $second;
$previous = $current;
} for split q/\s+/, $s;
return
@thirds;
}
◇
-
Fragment referenced in 6.
The rest of the code drives some tests.
-
MAIN:{
say q/(/ . join(q/, /, find_third(q/Perl is a my favourite language but Python is my favourite too./, q/my/, q/favourite/)). q/)/;
say q/(/ . join(q/, /, find_third(q/Barbie is a beautiful doll also also a beautiful princess./, q/a/, q/beautiful/)) . q/)/;
say q/(/ . join(q/, /, find_third(q/we will we will rock you rock you./, q/we/, q/will/)) . q/)/;
}
◇
-
Fragment referenced in 6.
Sample Run
$ perl perl/ch-2.pl (language, too) (doll, princess) (we, rock)
References
posted at: 17:29 by: Adam Russell | path: /perl | permanent link to this entry