1

Is there a way in prolog to make the following shorter:

rule(prop, [1/2,2/2]).
rule(prop, [1/3,2/3,3/3]).
rule(prop, [1/4,2/4,3/4,4/4]).
rule(prop, [1/5,2/5,3/5,4/5,5/5]).
rule(prop, [1/6,2/6,3/6,4/6,5/6,6/6]).
rule(prop, [1/7,2/7,3/7,4/7,5/7,6/7,7/7]).
Brutalized
  • 85
  • 2
  • 9

2 Answers2

4

TL;DR: Why not delegate the responsibility for handling recursion—and getting it right, too?

This answer follows up on this previous answer by @lurker. We do not cover the entire question, but instead focus on showing how a predicate like list_props/3 can be defined so that all recursion is delegated to the tried and true Prolog predicates length/2, numlist/2 and maplist/3:

:- use_module(library(between), [numlist/2]).
:- use_module(library(lists),   [maplist/3]).

To customize the versatile maplist/3 we define:

denom_num_expr(B, A, A/B).

Sample query using 4.3.2:

| ?- length(_Ds, N), numlist(N, _Ds), maplist(denom_num_expr(N), _Ds, Qs).
N = 1, Qs = [1/1] ? ;
N = 2, Qs = [1/2,2/2] ? ;
N = 3, Qs = [1/3,2/3,3/3] ? ;
N = 4, Qs = [1/4,2/4,3/4,4/4] ? ;
N = 5, Qs = [1/5,2/5,3/5,4/5,5/5] ? ;
N = 6, Qs = [1/6,2/6,3/6,4/6,5/6,6/6] ? ;
N = 7, Qs = [1/7,2/7,3/7,4/7,5/7,6/7,7/7] ? ;
N = 8, Qs = [1/8,2/8,3/8,4/8,5/8,6/8,7/8,8/8] ? ;
N = 9, Qs = [1/9,2/9,3/9,4/9,5/9,6/9,7/9,8/9,9/9] ? ...
Community
  • 1
  • 1
repeat
  • 18,496
  • 4
  • 54
  • 166
  • 1
    Yes, much more elegant way to get `list_prop/3` (+1). For no particular reason other than habit, I tend to use gprolog for little Prolog tasks, which doesn't have `numlist/3`, but `numlist/3` is easy to create. – lurker Feb 21 '16 at 00:45
  • 1
    We need more common ground! While `numlist/3` is offered by both SWI and SICStus, they offer different modes, IIRC. (With SICStus, we could kick the redundant `length/2` goal.) – repeat Feb 21 '16 at 01:03
  • 1
    @lurker. I take back my previous statement about "SICStus offering more (usable) modes for `numlist`"; that was wishful thinking. – repeat Feb 21 '16 at 03:17
  • 1
    @lurker. On the plus side, SICStus has `numlist/2`, which is a clear idiom. I wish it will work someday as a generator when both arguments are unbound, just like above conjunction with `length/2`. – repeat Feb 21 '16 at 09:16
3

The following code isn't necessarily "shorter" for the case of 6 different rules, but it is more scalable, which is probably what you really mean.

You can break this down as follows. First, a rule that generates one list:

list_props(N, N, [N/N]).
list_props(X, N, [X/N|T]) :-
    X >= 1,
    X < N,
    X1 is X + 1,
    list_props(X1, N, T).

When you call this, it generates one list of proportions from the first argument to the last with the last argument being the denominator. For example:

| ?- list_props(1, 4, L).

L = [1/4,2/4,3/4,4/4] ? a

| ?-

Note that you could enforce that N be an integer >= 1 using integer(N) and conditions, but I was being brief and didn't do that in the above.

You can use this in your top level predicate:

rule(prop, L) :-
    between(2, 7, X),
    list_props(1, X, L).

Which yields:

| ?- rule(prop, L).

L = [1/2,2/2] ? ;

L = [1/3,2/3,3/3] ? ;

L = [1/4,2/4,3/4,4/4] ? ;

L = [1/5,2/5,3/5,4/5,5/5] ? ;

L = [1/6,2/6,3/6,4/6,5/6,6/6] ? ;

L = [1/7,2/7,3/7,4/7,5/7,6/7,7/7] ? ;

(2 ms) no
| ?-
lurker
  • 56,987
  • 9
  • 69
  • 103