3

I want to add to Result list N to 0 digits.

The sample query

?- add(5,R).

should return the answer:

R = [5,4,3,2,1,0].

I already tried the following code but it did not work.

add(0, 0).
add(N, [R]) :-
   N1 is N-1,
   add(N1, [R|N]).
repeat
  • 18,496
  • 4
  • 54
  • 166

3 Answers3

3

You're so close!

add(0, [0]).
add(N, [N|R]) :- 
  N > 0, 
  N1 is N-1, 
  add(N1, R).

So, what's different here?

  1. add(0, [0]) has [0] instead of 0 because you're building a list, not an integer; otherwise you get the rather awkward looking [5,4,3,2,1|0] result.

  2. N > 0 as a guard, to ensure that we don't loop crawling through negative numbers forever once we hit the base case.

  3. The work is being done in the head of the second clause of add/2 instead of the body of it. To wit, our pattern is add(N, [N|R]) instead of add(N, [R]). This is because this term adds N to the head of the list rather than adding it before recurring.

  4. Similarly, you have a simple inversion in [R|N]; this would build lists kind of backwards.

All in all, I think you were very close. A little more experimenting at the prompt may have been sufficient to fix it. Have you tried using trace/0 yet?

Daniel Lyons
  • 22,421
  • 2
  • 50
  • 77
  • First, thank you about your explanation. I learnd it very well. I did not know about tracing. I searched it for a while. It is very useful. Thank you again. – user3703977 Nov 25 '15 at 21:01
2

We show an analog of this answer (which dealed with consecutive integers ascending from 0).

:- use_module(library(clpfd)).
:- set_prolog_flag(toplevel_print_anon, false).

Based on equidistant_stride/2 we query:

?- N = 10, Zs = [N|_Zs0], length(_Zs0, N), equidistant_stride(Zs, -1).
N = 10, Zs = [10,9,8,7,6,5,4,3,2,1,0].

Let's re-run1 the runtime measurements we did in this previous answer!

?- between(1,6,E),
   N is 10^E,
   garbage_collect,
   call_time(numlist(0, N, _), T1_in_ms),
   garbage_collect,
   call_time((_Zs = [N|_Z], length(_Z, N), equidistant_stride(_Zs, -1)), T2_in_ms).
  N =      10, T1_in_ms =  0, T2_in_ms =   0
; N =     100, T1_in_ms =  1, T2_in_ms =   0
; N =    1000, T1_in_ms =  1, T2_in_ms =   1
; N =   10000, T1_in_ms =  3, T2_in_ms =  12
; N =  100000, T1_in_ms = 14, T2_in_ms =  32
; N = 1000000, T1_in_ms = 90, T2_in_ms = 280.

Edit

Past revisions of this answer inadvertently sk(r)ewed runtime measurements in favor of . How? It's simple: A reverse/2 goal followed numlist/3, even though it is useless in this setting.

This should be better now: Thx 2 @JanWielemaker 4 reporting!


Footnote 1: Using SWI-Prolog version 7.3.11 (64-bit).

Community
  • 1
  • 1
repeat
  • 18,496
  • 4
  • 54
  • 166
  • 1
    @JanWielemaker. Better now! Isn't it nice to see these little binary predicates cooperate like this? – repeat Nov 27 '15 at 10:46
  • 1
    Yes and no. Your cheating a little by adding `reverse/2`. It is just as easy to write a numlist/3 that gets the order right. That is why I omitted the reverse for the timing comparison. What remains is the comparison between an obvious **algorithm** and a declarative specification for which we merely have to hope that the constraint solver is clever enough to find the right algorithm. Given a complex search problem, it is wise to try constraints first. A good **algorithm** however is **predictable**. – Jan Wielemaker Nov 27 '15 at 16:29
  • @JanWielemaker. True, a little:) I did not intend to rig the comparison. Will edit to improve fairness. – repeat Nov 27 '15 at 17:12
  • @JanWielemaker. Regarding the predictability of algorithm performance I'm sceptical: (1) In general, yes, (2) but, (3) with varying accuracy, (4) better for abstract measures than for measured runtime, because (5) actual CPUs do a mirage of stunts like caches, predictors, speculative execution, paged virtual memory to raise the average case performance of legacy code. – repeat Nov 27 '15 at 17:23
  • @JanWielemaker. How about SWI supporting huge-pages? TLB is a precious resource, after all. And how about SIMD-izing the GC (say for marking)? Now that the low hanging fruit got picked, vector gather/scatter has reached mainstream CPUs. It's time we use it! 4 U c, I got lots of splendid ideas:) – repeat Nov 27 '15 at 17:27
1

Use !

:- use_module(library(clpfd)).
:- set_prolog_flag(toplevel_print_anon, false).

We define n_to_0/2 like this:

n_to_0(N,[Z|Zs]) :-
   length(Zs,N),
   [Z|Zs] ins 0..N,
   chain([Z|Zs],#>).

Sample query as given by the OP:

?- n_to_0(5,Zs).
Zs = [5,4,3,2,1,0].

How about the most general query using n_to_0/2?

?- n_to_0(N,Zs).
  N = 0, Zs =             [0]
; N = 1, Zs =           [1,0]
; N = 2, Zs =         [2,1,0]
; N = 3, Zs =       [3,2,1,0]
; N = 4, Zs =     [4,3,2,1,0]
; N = 5, Zs =   [5,4,3,2,1,0]
; N = 6, Zs = [6,5,4,3,2,1,0]
...

Edit

@JanWielemaker pointed out that n_to_0/2 (as defined above) is abysmally slow—particularly when comparing it to its non-clpfd counterpart: Thanks a lot for reporting! See for yourself...

?- between(1, 3, E),
   N is 10^E, 
   call_time((numlist(0, N, _Zs0), reverse(_Zs0, _)), T1_in_ms),
   call_time(n_to_0(N, _), T2_in_ms).
  E = 1, N =   10, T1_in_ms = 0, T2_in_ms =     1
; E = 2, N =  100, T1_in_ms = 0, T2_in_ms =   104
; E = 3, N = 1000, T1_in_ms = 0, T2_in_ms = 29701
...

Check out this new, improved, clpfd-based answer!

Community
  • 1
  • 1
repeat
  • 18,496
  • 4
  • 54
  • 166
  • 1
    Yes, this can work too but out teacher does not want us to use libraries. – user3703977 Nov 25 '15 at 21:01
  • 2
    It is surely nicely declarative. It also uses specialized constraints, which makes it more a _library_ solution. And, it is really **slow**. Just visit http://swish.swi-prolog.org/p/pjAHBXed.pl – Jan Wielemaker Nov 26 '15 at 22:34
  • 1
    Regarding the wording "library solution": It is in my view limiting and detrimental to provide a Prolog system where integer constraints are not at least autoloaded. Almost all Prolog snippets on Stackoverflow would or already *do* benefit from declarative integer arithmetic. I wish SWI would act like GNU Prolog and B-Prolog, and just make CLP(FD) constraints available out of the box. There is no conflict with other library predicates anyways, now that SWI finally, like SICStus, has `transpose_ugraph/2`, 8 years after the `transpose/2` conflict was first reported and long solved in SICStus. – mat Nov 27 '15 at 08:15
  • 1
    @mat. I second your opinion. Of course! Do you remember your first Prolog query? Your first Prolog query *ever*? I do and I guess you do, too: `?- stadt(X).` (Note "Stadt" is German for "town" / "city"). Other suitable "first" queries include IMO `?- 1+1 #= 2.` and `?- X #> 1, X #< 4.` and `?- X #\= 0.` and many more... but definitely not `?- use_module(library(clpfd)).`. – repeat Nov 27 '15 at 11:04