1

I am trying to generate a list with random values between 1 and 10, but when I run the code, I'm receiving "false"...

geraL(0,_).
geraL(C,Y):-
  C is C-1,
  random(1,10,U),
  Y = [U|Y],
  geraL(C,Y).


?-geraL(13,X).
repeat
  • 18,496
  • 4
  • 54
  • 166
mapgiordani
  • 65
  • 1
  • 10

3 Answers3

4

No need to write recursive code!

Simply use random/3, length/2, and maplist/2 like so:

?- length(Zs, 13), maplist(random(0,10), Zs).
Zs = [8, 9, 3, 5, 0, 7, 6, 7, 9, 9, 2, 4, 7].

?- length(Zs, 13), maplist(random(0,10), Zs).
Zs = [1, 7, 7, 6, 2, 5, 2, 9, 9, 2, 0, 7, 1].

?- length(Zs, 13), maplist(random(0,10), Zs).
Zs = [4, 6, 7, 0, 1, 3, 4, 9, 0, 8, 3, 8, 5].

?- length(Zs, 13), maplist(random(0,10), Zs).
Zs = [3, 1, 2, 1, 5, 3, 9, 8, 3, 7, 8, 1, 7].

Note the impurity! We get different answers—at least, most of the time...

For reproducible results, write down the actual method and its initial state, .

Community
  • 1
  • 1
repeat
  • 18,496
  • 4
  • 54
  • 166
  • 1
    Clearly the best answer! – false Apr 01 '16 at 13:16
  • 2
    @false this is the best way to solve the original problem, jwhich makes it a great addition to the list of answers. But the purpose of my original answer was to directly address the OP's question regarding why their implementation didn't work. – lurker Apr 01 '16 at 19:49
  • 1
    @repeat, isn't it a desirable thing for a generator of random lists of numbers not to have reproducible results? :) – lurker Apr 01 '16 at 19:50
  • @lurker. Yes and no. But mostly no. Nonetheless, I *was* surprised when I learned that IBM had investigated the impact of altitude on transient failure rates (single-bit flips) of non-ECC dynamic RAM in the 1960s;) – repeat Apr 01 '16 at 20:01
  • 1
    Well, I guess I wouldn't say "mostly" no, but I could definitely see "maybe yes, maybe know, depending upon the circumstances" as in whether it was by algorithm/hardware design, or by accident. :) – lurker Apr 01 '16 at 20:03
2

We'll cover each issue individually:

  • geraL(0, _) says that if you want zero random numbers, anything could be the result. I'm sure that's not what you mean. What should the result be if the number of elements is 0?

  • You should have a constraint that says C > 0 in your general case to avoid executing this rule when it's not desired

  • C is C - 1 will always fail because for any value of C, C and C-1 can never be the same value. You cannot reinstantiate a variable once it has a value within a predicate clause, except through backtracking.

  • Although Y = [U|Y] is allowed, it's a cyclical list definition (a list defined in terms of itself with an additional head element), and I'm sure that's not what you want, either.

If you fix these items, your program will work. The first two are fixed by using an auxiliary (different) variable. The last one is defining the base case correctly to associate the empty list with 0, not an anonymous variable.

geraL(0, []).   % Fix for item 1: zero values should be an empty list
geraL(C, Y):-
  C > 0,        % Fix for item 2
  C1 is C-1,    % Fix for item 3: use C1 for the new value
  random(1, 10, U),
  Y = [U|T],    % Fix for item 4: use T for the new value (tail)
  geraL(C1, T).
lurker
  • 56,987
  • 9
  • 69
  • 103
  • 2
    I had `geraL(0, []) :- !.` instead of the added condition of `C > 0`, but the latter is better, since it defines a constraint, instead of controlling the flow. +1 – SQB Mar 31 '16 at 18:21
  • 1
    @SQB. Beware of [tag:prolog-cut]: it often makes code lose steadfastness. (It does not in this particular case.) – repeat Apr 01 '16 at 09:54
  • @lurker: did you know that exchanging the two clauses will: 1. make your code deterministic, no leftover choicepoint whatsoever, 2. make it even a bit faster in many Prologs since clause-indexing can be exploited? – false Apr 01 '16 at 19:53
1

There is a built-in predicate findnsols/4 which finds at most N solutions.

This predicate can be used for the equivalent of a loop. Since random/3 is deterministic, we need to add repeat/0 to the goal to make it repeat.

geraL(C, Y) :- findnsols(C, U, (repeat, random(1, 10, U)), Y).

Also, please note that random/3 has been deprecated in SWI Prolog; it is advised to use random/1 or random_between/3 instead.
Most notably, random/3 excludes the upper bound, so random(1, 10, U) will never yield U = 10.

SQB
  • 3,926
  • 2
  • 28
  • 49
  • To me, using `findnsols/4` in this context does not appear as a reasonable choice. – repeat Apr 01 '16 at 09:52
  • 1
    `findnsols/4` is a combination of two quite unreliable abstractions: `findall/3` which has a whole bunch of problems w.r.t quantification and `call_nth/2`. – false Apr 01 '16 at 13:30
  • 1
    `random/3` may be considered obsolete by SWI, but `random_between/3` is not available out of the box with SICStus (details: https://sicstus.sics.se/sicstus/docs/latest4/html/sicstus.html/lib_002drandom.html). So if your code is to run with both SWI and SICStus, better keep using `random/3`... – repeat Apr 01 '16 at 13:37
  • Note on the mandatory `repeat/0` with anything random: only one `repeat` statement is actually used in a call stack, so I suggest placing `repeat` first before everything else, like `findnsols(C, U, (repeat, main_predicate), Y).`, where any number of random generators can be anywhere in `main_predicate`. In other words, don't write `repeat` in detailed code. – Zhanwen Chen Oct 21 '19 at 02:20