# RabbitFarm

### 2020-11-08

#### # Command Line Arguments with SWI_Prolog’s library(optparse)

SWI-Prolog has a nice library which allows you to pass arguments on the command line to your Prolog programs. Here are a couple of example of it’s use done with the two parts of Perl Weekly Challenge 085 implemented in Prolog.

## Part 1

You are given an array of real numbers greater than zero. Write a script to find if there exists a triplet (a,b,c) such that 1 < a+b+c < 2. Print 1 if you succeed otherwise 0.

### Solution

``````
:- use_module(library(optparse)).
/*
You are given an array of real numbers greater than zero.
Write a script to find if there exists a triplet (a,b,c)
such that 1 < a+b+c < 2. Print 1 if you succeed otherwise 0.
*/
opts_spec(
[
[opt(numbers),
default([1.2, 0.4, 0.1, 2.5]),
longflags([numbers])]
]).

ch_1(L):-
member(A, L),
member(B, L),
member(C, L),
A =\= B,
B =\= C,
A =\= C,
D is A + B + C,
D > 1,
D < 2,
writeln(1).

ch_1(_):-
writeln(0).

main:-
opts_spec(OptsSpec),
ch_1(N),
halt.
``````

### Sample Run

``````
\$ swipl -s ch-1.p -g main --numbers="[1.0, 0.2, 3.4, 0.1]"
1
\$ swipl -s ch-1.p -g main --numbers="[1.0, 1.2, 3.4, 0.1]"
0
``````

### Notes

You can see from the Sample Run that we are passing a Prolog list as a command line argument. To avoid conflicts with the shell trying to interpret our square brackets and commas we put the list itself in quotes. But how does Prolog know what `--numbers` means?

To use `library(optparse)` you must define a specification for each of your command line arguments that you expect to take. Here we just have one. The line `opt(numbers)` specifies the term that will be used to obtain the value in your program, `default([1.2, 0.4, 0.1, 2.5])` provides a default value if the argument is not used on the command line. `longflags([numbers])` indicates that to look for a flag of the form `--numbers`. we could have also used `shortflags([numbers])` instead if we would prefer to use `-numbers`. I don’t know of any strong arguments for either one but find, personally, that the long form is more intuitive.

The values passed on the command line are extracted using `opt_arguments` which will follow the specification you’ve provided. `_AdditionalArguments` refers to any arguments passed on the command line without dashes but here we do not have any. `[numbers(N)]` is a list of the parsed key(value) pairs from the command line. So for this example `N` is the list value we entered on the command line and it is then used as needed.

## Part 2

You are given a positive integer \$N. Write a script to find if it can be expressed as a ** b where a > 0 and b > 1. Print 1 if you succeed otherwise 0.

### Solution

``````
:- use_module(library(clpfd)).
:- use_module(library(optparse)).
/*
You are given a positive integer \$N.
Write a script to find if it can be expressed
as a ^ b where a > 0 and b > 1.
Print 1 if you succeed otherwise 0.
*/
opts_spec(
[
[opt(number),
default(0),
longflags([number])]
]).

/*
Ok, I'll admit, this is a pretty silly use of clpfd when
a simple logarithm calculation would do the job! Still clpfd
is more fun.
*/
ch_2(N) :-
N0 is N -1,
A in 0 .. N0,
B in 1 .. N0,
N #= A ^ B,
label([A,B]),
writeln(1).
ch_2(_) :-
writeln(0).

main:-
opts_spec(OptsSpec),
ch_2(N),
halt.
``````

### Sample Run

``````
\$ swipl -s ch-2.p -g main --number=7
0
\$ swipl -s ch-2.p -g main --number=8
1
``````

### Notes

For an extra bit of fun I decided to use `library(clpfd)` for this although it is most definitely a bit of over engineering! This can be done rather simply using logarithms. Here we see the same pattern as done in Part 1: define the specification, extract the values from the command line, and then use the values. Here the single argument is just a single value and so there is no need to wrap the value in quotes, however, if you add quotes anyway it will have no effect. For example:
``````
\$ swipl -s ch-2.p -g main --number="100"
1
``````

## Reference

optparse documentation

posted at: 17:11 by: Adam Russell | path: /prolog | permanent link to this entry

## Part 1

You are given an array of real numbers greater than zero. Write a script to find if there exists a triplet (a,b,c) such that 1 < a+b+c < 2. Print 1 if you succeed otherwise 0.

### Solution

``````
use strict;
use warnings;
##
# You are given an array of real numbers greater than zero.
# Write a script to find if there exists a triplet (a,b,c)
# such that 1 < a+b+c < 2. Print 1 if you succeed otherwise 0.
##
use boolean;
use Math::Combinatorics;

sub build_constraints{
my @constraints;
my \$a_not_equal_b = sub { \$_[0] != \$_[1] };
my \$a_not_equal_c = sub { \$_[0] != \$_[2] };
my \$b_not_equal_c = sub { \$_[1] != \$_[2] };
my \$sum_greater_than_1 = sub { 1 < (\$_[0] + \$_[1] + \$_[2]) };
my \$sum_less_than_2 = sub { 2 > (\$_[0] + \$_[1] + \$_[2]) };
return (
\$a_not_equal_b,
\$a_not_equal_c,
\$b_not_equal_c,
\$sum_greater_than_1,
\$sum_less_than_2
);
}

MAIN:{
my \$combinations = Math::Combinatorics->new(
count => 3,
data => [@ARGV],
);
my \$found;
while(my @combination = \$combinations->next_combination()){
\$found = true;
for my \$constraint (build_constraints()){
if(!\$constraint->(@combination)){
\$found = false;
last;
}
}
do{ print "1\n"; last; } if(\$found);
}
print "0\n" if(!\$found);
}
``````

### Sample Run

``````
\$ perl perl/ch-1.pl 0.1 1.2 3.4 0.2
1
\$ perl perl/ch-1.pl 1.1 1.2 3.4 0.2
0
``````

### Notes

I decided to try a constraint programming approach for this. While there are several modules for doing this available on CPAN I didn’t want to quite go so deep down that rabbithole. Instead I took the simpler path of implementing each constraint as a subroutine stored in an array. For each candidate combination the constraints are tests. Since all constraints must be satisfied if any one returns a false value then we can move immediately to the next combination.

## Part 2

You are given a positive integer \$N. Write a script to find if it can be expressed as a ** b where a > 0 and b > 1. Print 1 if you succeed otherwise 0.

### Solution

``````
use strict;
use warnings;
##
# You are given a positive integer \$N.
# Write a script to find if it can be expressed
# as a ^ b where a > 0 and b > 1.
# Print 1 if you succeed otherwise 0.
##
use boolean;

sub log_a{
my(\$a, \$n) = @_;
return log(\$n)/log(\$a);
}

MAIN:{
my \$N = \$ARGV[0];
my \$found = false;
for my \$a (2 .. \$N){
my \$b = log_a(\$a, \$N);
if(\$b =~ /^[-]?\d+\$/ && \$b > 1){
print "1\n";
\$found = true;
last;
}
}
print "0\n" if(!\$found);
}
``````

### Sample Run

``````
\$ perl perl/ch-2.pl 7
0
\$ perl perl/ch-2.pl 9
1
``````

### Notes

I was tempted to repeat roughly the same design as Part 1 and use constraints but that really would be over engineering it! Instead here we just loop over all possible values `\$a` and test using logarithms to see if `\$b` holds an integer value. There seems to be a number of ways to do the test to determine if a scalar holds an integer but a regex seems maybe the most idiomatically Perlish way.

posted at: 16:20 by: Adam Russell | path: /perl | permanent link to this entry