4

I'm trying to solve an exercise in order to become more familiar with prolog.

The task is following:

% Sten wants to send Lisa 100 flowers. He can choose from lilies, roses and tulips.
% One lily costs $50, rose $10 and tulip $1. Find how many flowers of each type he 
% must buy, so that he spends exactly $500.

I have solved that exercise, but in somewhat bulky way I guess. My code is:

% numbers 1..100
digit(1). digit(2). digit(3). digit(4). digit(5). digit(6). digit(7). digit(8).
digit(9). digit(10). digit(11). digit(12). digit(13). digit(14). digit(15). digit(16). 
digit(17). digit(18). digit(19). digit(20). digit(21). digit(22). digit(23). digit(24). 
digit(25). digit(26). digit(27). digit(28). digit(29). digit(30). digit(31). digit(32). 
digit(33). digit(34). digit(35). digit(36). digit(37). digit(38). digit(39). digit(40).
digit(41). digit(42). digit(43). digit(44). digit(45). digit(46). digit(47). digit(48). 
digit(49). digit(50). digit(51). digit(52). digit(53). digit(54). digit(55). digit(56). 
digit(57). digit(58). digit(59). digit(60). digit(61). digit(62). digit(63). digit(64). 
digit(65). digit(66). digit(67). digit(68). digit(69). digit(70). digit(71). digit(72). 
digit(73). digit(74). digit(75). digit(76). digit(77). digit(78). digit(79). digit(80).
digit(81). digit(82). digit(83). digit(84). digit(85). digit(86). digit(87). digit(88). 
digit(89). digit(90). digit(91). digit(92). digit(93). digit(94). digit(95). digit(96). 
digit(97). digit(98). digit(99). digit(100).

quantity(A1,A2,A3):-
    var(A1), var(A2), var(A3),
    digit(A1), digit(A2), digit(A3),
    X is A1+A2+A3, X is 100,
    Y is (A1*50)+(A2*10)+(A3*1), Y is 500.

Can somebody suggest a better method for initializing those rules? For example in Haskell I could do something like this:

let numbers = [1..100]

Thanks in advance.

false
  • 10,264
  • 13
  • 101
  • 209
pacman.
  • 217
  • 1
  • 2
  • 9

3 Answers3

7

Using SWI-Prolog:

:- use_module(library(clpfd)).

flowers(L, R, T) :-
        [L,R,T] ins 0..sup,
        L+R+T #= 100,
        L*50 + R*10 + T*1 #= 500.

Example query:

?- flowers(Lilies, Roses, Tulips), label([Lilies,Roses,Tulips]).
Lilies = 1,
Roses = 39,
Tulips = 60 ;
false.
mat
  • 40,498
  • 3
  • 51
  • 78
6

Some versions of Prolog have the between/3 predicate. You could say

digit(X):-between(1,100,X).

If between is not available, you could say

digit(X):-member(X,[1,2,3,4,5 and so on]).

If you do not want to use member/2, use recursion.

Edit: you can also implement between/3 like this:

my_between(X,Y,Z):-X<Y,(Z=X;X2 is X+1,my_between(X2,Y,Z)).

A robust and efficient implementation of between/3 may be more complicated, but for your purposes, this should be enough.

Pavel Bazant
  • 510
  • 3
  • 10
  • 1
    Your second option still requires him to spell out all 100 numbers, so it still requires `O(n)` typing. It's also quite inefficient. – sepp2k Jan 04 '11 at 16:08
  • 1
    Yes that's right. On the other hand, digit(X) :- 1 =< X, X =< 100. does not work at all, as he calls digit with X uninstantiated. – Pavel Bazant Jan 04 '11 at 16:17
  • Right, my bad. I didn't read the problem description carefully enough. – sepp2k Jan 04 '11 at 16:23
  • What about: ?- maplist(between(0,100), [L,R,T]), L+R+T =:= 100, L*50 + R*10 + T*1 =:= 500. Of course the constraint version is much faster than this simple generate-and-test. – mat Jan 04 '11 at 17:31
3
quantity(lilies(L),roses(R),tulips(T)) :- 
    between(0,100,L),
    between(0,100,R),
    between(0,100,T),
    L + R + T =:= 100,
    L*50 + R*10 + T =:= 500 .
Greg Buchholz
  • 900
  • 4
  • 17