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 1 ⟩≡


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.

build word index 2 ⟩≡


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 the indices of words that contain the letter 3 ⟩≡


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.

"ch-1.p" 4


contains letter 1
build word index 2
find the indices of words that contain the letter 3

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 5 ⟩≡


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.

"ch-2.p" 6


find third 5

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

The Weekly Challenge 315
Generated Code

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.

find words 1 ⟩≡


sub find_words{
my($s, $c) = @_;
return grep {$s->[$_] =~ m/$c/} 0 .. @{$s} - 1;
}

Fragment referenced in 2.

Putting it all together...

"ch-1.pl" 2


preamble 3
find words 1
main 4

preamble 3 ⟩≡


use v5.40;

Fragment referenced in 2, 6.

The rest of the code just runs some simple tests.

main 4 ⟩≡


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.

find third 5 ⟩≡


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.

"ch-2.pl" 6


preamble 3
find third 5
main 7

main 7 ⟩≡


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

The Weekly Challenge 315
Generated Code

posted at: 17:29 by: Adam Russell | path: /perl | permanent link to this entry