Creating the #< BIP in prolog - list

I'm basically trying to create 'my own' version of the #< operator in prolog, i.e. something to the tune of at_less(F1,F2), where it returns true if F1#
So some example inputs:
?- at_less(1.2,0)
yes
?-at_less(0,1.2)
no
?-at_less(f(1,2,a),f(1,2,b)).
yes
Obviously I don't want to use the #< operator, as that would be redundant :)
I can understand how one would compare two atoms, with something like
%atom_pr(+A1,+A2): A1 #< A2, where A1 and A2 are atoms.
atom_pr(L1,L2):-
atom_codes(L1,First), %atom codes for first
atom_codes(L2,Second),%atom codes for second
code_compare(First,Second). %compare the codes, return true or false
code_compare([HF|TF],[HS|TS]):-
( HF=HS ->
code_compare(TF,TS)
;
HF<HS ->
true
;
fail
).
code_compare([],X):-
true.
code_compare([],[]).
%(I understand this is probably not the most efficient way of going about this, but I'm %just beginning!)
Is there something similar I can do for all constants, rather than just atoms? Is there a command similar to atom_codes/2? If not, the only thing I can think of doing is breaking it into a great number of if -> else statements, checking to see if the first is an atom and the second isn't etc. etc., but this seems like a kinda tedious/poor method perhaps?
Thanks in advance for the help!
Edit: Thanks to the help below, I've gotten a program running that is functional (at least from what I can tell). I've put it in the code below, in case someone else wanders here. I believe it is, however, extremely inefficient, so there's plenty of room for improvement :)
%flat_pr(+F1, +F2): F1#<F2, where F1 and F2 are flat ground terms
flat_pr(F1,F2):-
( compound(F1) -> %these ifs here to check what type F1 and F2 are
( compound(F2) -> %comparing/succeeding/failing as appropriate
compound_compare(F1,F2) %(atom>integer>float>compound)
; % I believe these ifs could definitely be cut down though
( atom(F2) ->
true
;
( float(F2) ->
true
;
( integer(F2) ->
true
)
)
)
)
)
;
( atom(F1) ->
( compound(F2) ->
false
;
( atom(F2) ->
atom_pr(F1,F2)
;
( float(F2) ->
false
;
( integer(F2) ->
false
)
)
)
)
)
;
( float(F1) ->
( compound(F2) ->
false
;
( atom(F2) ->
true
;
( float(F2) ->
number_pr(F1,F2)
;
( integer(F2) ->
true
)
)
)
)
;
fail
)
;
( integer(F1) ->
( compound(F2) ->
false
;
( atom(F2) ->
true
;
( float(F2) ->
false
;
( integer(F2) ->
number_pr(F1,F2)
)
)
)
)
)
.
compound_compare(F1,F2):- %compares compounds (arity first)
functor(F1,N1,A1), %get arity
functor(F2,N2,A2),
( A1<A2 -> %compare arity
true
;
( A1>A2 ->
false
)
;
( A1=A2 -> %if arity the same
F1 =.. L1, %compound -> list
F2 =.. L2,
list_compare(L1,L2) %compare the lists
)
)
.
list_compare([],[]). %base case
list_compare([H|T],[H1|T1]):-
( flat_pr(H,H1) -> %if H#<H1
list_compare(T,T1) %compare Tails
;
false %else false
)
.
atom_pr(L1,L2):-
atom_codes(L1,First), %atom codes for first
atom_codes(L2,Second),%atom codes for second
code_compare(First,Second). %compare the codes, return true or false
number_pr(L1,L2):- %simple number comparison...straight forward
( L1=<L2 ->
true
;
fail
).
code_compare([HF|TF],[HS|TS]):- %just runs through atom codes
( HF=HS ->
code_compare(TF,TS)
;
HF<HS ->
true
;
fail
).
code_compare([],X):-
true.
code_compare([],[]).
I'd love to see ways to improve this though! Cheers

Not sure about BIN-Prolog but in SWI-Prolog atom_codes does the trick:
?- atom_codes('jack\&^',K).
K = [106, 97, 99, 107, 38, 94].
?- atom_codes('17.2345',K).
K = [49, 55, 46, 50, 51, 52, 53].
Update:
If you need to compare terms, 1) freeze them (see further) to make the terms ground and 2) use unif (=..) to transform a term to a list, e.g., f(1,2) becomes [f,1,2], and then for each element: a) if it is an atom or a number, then use atom_codes; b) if it is a term, apply the same procedure recursively.
Freezing ensures that the variables are compared in order of their appearance. By "freezing" I mean the following predicate taken from the classical Sterling and Shapiro's book "The Art of Prolog":
numvars('#VAR'(N),N,N1) :- N1 is N+1.
numvars(Term,N1,N2) :- nonvar(Term), functor(Term,_,N),
numvars(0,N,Term,N1,N2).
numvars(N,N,_,N1,N1).
numvars(I,N,Term,N1,N3) :- I<N, I1 is I+1,
arg(I1,Term,Arg), numvars(Arg,N1,N2),
numvars(I1,N,Term,N2,N3).
frz(A,B) :- frz(A,B,0).
frz(A,B,Min) :- copy_term(A,B), numvars(B,Min,_),!.

Related

Remove brackets from a list in Prolog [duplicate]

I am doing some easy exercises to get a feel for the language.
is_list([]).
is_list([_|_]).
my_flatten([],[]).
my_flatten([X|Xs],RR) :-
my_flatten(Xs,R),
(is_list(X), !, append(X,R,RR); RR = [X | R]).
Here is a version using cut, for a predicate that flattens a list one level.
my_flatten([],[]).
my_flatten([X|Xs],RR) :-
my_flatten(Xs,R),
if_(is_list(X), append(X,R,RR), RR = [X | R]).
Here is how I want to write it, but it does not work. Neither does is_list(X) = true as the if_ condition. How am I intended to use if_ here?
(Sorry, I somewhat skipped this)
Please refer to P07. It clearly states that it flattens out [a, [b, [c, d], e]], but you and #Willem produce:
?- my_flatten([a, [b, [c, d], e]], X).
X = [a,b,[c,d],e]. % not flattened!
And the solution given there succeeds for
?- my_flatten(non_list, X).
X = [non_list]. % unexpected, nothing to flatten
Your definition of is_list/1 succeeds for is_list([a|non_list]). Commonly, we want this to fail.
What you need is a safe predicate to test for lists. So let's concentrate on that first:
What is wrong with is_list/1 and if-then-else? It is as non-monotonic, as many other impure type testing predicates.
?- Xs = [], is_list([a|Xs]).
Xs = [].
?- is_list([a|Xs]). % generalization, Xs = [] removed
false. % ?!? unexpected
While the original query succeeds correctly, a generalization of it unexpectedly fails. In the monotonic part of Prolog, we expect that a generalization will succeed (or loop, produce an error, use up all resources, but never ever fail).
You have now two options to improve upon this highly undesirable situation:
Stay safe with safe inferences, _si!
Just take the definition of list_si/1 in place of is_list/1. In problematic situations, your program will now abort with an instantiation error, meaning "well sorry, I don't know how to answer this query". Be happy for that response! You are saved from being misled by incorrect answers.
In other words: There is nothing wrong with ( If_0 -> Then_0 ; Else_0 ), as long as the If_0 handles the situation of insufficient instantiations correctly (and does not refer to a user defined program since otherwise you will be again in non-monotonic behavior).
Here is such a definition:
my_flatten(Es, Fs) :-
list_si(Es),
phrase(flattenl(Es), Fs).
flattenl([]) --> [].
flattenl([E|Es]) -->
( {list_si(E)} -> flattenl(E) ; [E] ),
flattenl(Es).
?- my_flatten([a, [b, [c, d], e]], X).
X = [a,b,c,d,e].
So ( If_0 -> Then_0 ; Else_0 ) has two weaknesses: The condition If_0 might be sensible to insufficient instantiations, and the Else_0 may be the source of non-monotonicity. But otherwise it works. So why do we want more than that?
In many more general situations this definition will now bark back: "Instantiation error"! While not incorrect, this still can be improved. This exercise is not the ideal example for this, but we will give it a try.
Use a reified condition
In order to use if_/3 you need a reified condition, that is, a definition that carries it's truth value as an explicit extra argument. Let's call it list_t/2.
?- list_t([a,b,c], T).
T = true.
?- list_t([a,b,c|non_list], T).
T = false.
?- list_t(Any, T).
Any = [],
T = true
; T = false,
dif(Any,[]),
when(nonvar(Any),Any\=[_|_])
; Any = [_],
T = true
; Any = [_|_Any1],
T = false,
dif(_Any1,[]),
when(nonvar(_Any1),_Any1\=[_|_])
; ... .
So list_t can also be used to enumerate all true and false situations. Let's go through them:
T = true, Any = [] that's the empty list
T = false, dif(Any, []), Any is not [_|_] note how this inequality uses when/2
T = true, Any = [_] that's all lists with one element
T = true, Any = [_|_Any1] ... meaning: we start with an element, but then no list
list_t(Es, T) :-
if_( Es = []
, T = true
, if_(nocons_t(Es), T = false, ( Es = [_|Fs], list_t(Fs, T) ) )
).
nocons_t(NC, true) :-
when(nonvar(NC), NC \= [_|_]).
nocons_t([_|_], false).
So finally, the reified definition:
:- meta_predicate( if_(1, 2, 2, ?,?) ).
my_flatten(Es, Fs) :-
phrase(flattenl(Es), Fs).
flattenl([]) --> [].
flattenl([E|Es]) -->
if_(list_t(E), flattenl(E), [E] ),
flattenl(Es).
if_(C_1, Then__0, Else__0, Xs0,Xs) :-
if_(C_1, phrase(Then__0, Xs0,Xs), phrase(Else__0, Xs0,Xs) ).
?- my_flatten([a|_], [e|_]).
false.
?- my_flatten([e|_], [e|_]).
true
; true
; true
; ... .
?- my_flatten([a|Xs], [a]).
Xs = []
; Xs = [[]]
; Xs = [[],[]]
; ... .
?- my_flatten([X,a], [a]).
X = []
; X = [[]]
; X = [[[]]]
; X = [[[[]]]]
; ... .
?- my_flatten(Xs, [a]).
loops. % at least it does not fail
In Prolog, the equivalen of an if … then … else … in other languages is:
(condition -> if-true; if-false)
With condition, if-true and if-false items you need to fill in.
So in this specific case, you can implement this with:
my_flatten([],[]).
my_flatten([X|Xs],RR) :-
my_flatten(Xs,R),
( is_list(X)
-> append(X,R,RR)
; RR = [X | R] ).
or we can flatten recursively with:
my_flatten([],[]).
my_flatten([X|Xs],RR) :-
my_flatten(Xs,R),
( flatten(X, XF)
-> append(XF,R,RR)
; RR = [X | R] ).
Your if_/3 predicate is used for reified predicates.
This worked for me:
myflat([], []).
myflat([H|T], L) :-
myflat(H, L1),
myflat(T, L2),
append(L1, L2, L).
myflat(L, [L]).

How to implement a not_all_equal/1 predicate

How would one implement a not_all_equal/1 predicate, which succeeds if the given list contains at least 2 different elements and fails otherwise?
Here is my attempt (a not very pure one):
not_all_equal(L) :-
( member(H1, L), member(H2, L), H1 \= H2 -> true
; list_to_set(L, S),
not_all_equal_(S)
).
not_all_equal_([H|T]) :-
( member(H1, T), dif(H, H1)
; not_all_equal_(T)
).
This however does not always have the best behaviour:
?- not_all_equal([A,B,C]), A = a, B = b.
A = a,
B = b ;
A = a,
B = b,
dif(a, C) ;
A = a,
B = b,
dif(b, C) ;
false.
In this example, only the first answer should come out, the two other ones are superfluous.
Here is a partial implementation using library(reif) for SICStus|SWI. It's certainly correct, as it produces an error when it is unable to proceed. But it lacks the generality we'd like to have.
not_all_equalp([A,B]) :-
dif(A,B).
not_all_equalp([A,B,C]) :-
if_(( dif(A,B) ; dif(A,C) ; dif(B,C) ), true, false ).
not_all_equalp([A,B,C,D]) :-
if_(( dif(A,B) ; dif(A,C) ; dif(A,D) ; dif(B,C) ; dif(B,D) ), true, false ).
not_all_equalp([_,_,_,_,_|_]) :-
throw(error(representation_error(reified_disjunction),'C\'est trop !')).
?- not_all_equalp(L).
L = [_A,_B], dif(_A,_B)
; L = [_A,_A,_B], dif(_A,_B)
; L = [_A,_B,_C], dif(_A,_B)
; L = [_A,_A,_A,_B], dif(_A,_B)
; L = [_A,_A,_B,_C], dif(_A,_B)
; L = [_A,_B,_C,_D], dif(_A,_B)
; error(representation_error(reified_disjunction),'C\'est trop !').
?- not_all_equalp([A,B,C]), A = a, B = b.
A = a, B = b
; false.
Edit: Now I realize that I do not need to add that many dif/2 goals at all! It suffices that one variable is different to the first one! No need for mutual exclusivity! I still feel a bit insecure to remove the dif(B,C) goals ...
not_all_equalp([A,B]) :-
dif(A,B).
not_all_equalp([A,B,C]) :-
if_(( dif(A,B) ; dif(A,C) ), true, false ).
not_all_equalp([A,B,C,D]) :-
if_(( dif(A,B) ; dif(A,C) ; dif(A,D) ), true, false ).
not_all_equalp([_,_,_,_,_|_]) :-
throw(error(representation_error(reified_disjunction),'C\'est trop !')).
The answers are exactly the same... what is happening here, me thinks. Is this version weaker, that is less consistent?
Here's a straightforward way you can do it and preserve logical-purity!
not_all_equal([E|Es]) :-
some_dif(Es, E).
some_dif([X|Xs], E) :-
( dif(X, E)
; X = E, some_dif(Xs, E)
).
Here are some sample queries using SWI-Prolog 7.7.2.
First, the most general query:
?- not_all_equal(Es).
dif(_A,_B), Es = [_A,_B|_C]
; dif(_A,_B), Es = [_A,_A,_B|_C]
; dif(_A,_B), Es = [_A,_A,_A,_B|_C]
; dif(_A,_B), Es = [_A,_A,_A,_A,_B|_C]
; dif(_A,_B), Es = [_A,_A,_A,_A,_A,_B|_C]
...
Next, the query the OP gave in the question:
?- not_all_equal([A,B,C]), A=a, B=b.
A = a, B = b
; false. % <- the toplevel hints at non-determinism
Last, let's put the subgoal A=a, B=b upfront:
?- A=a, B=b, not_all_equal([A,B,C]).
A = a, B = b
; false. % <- (non-deterministic, like above)
Good, but ideally the last query should have succeeded deterministically!
Enter library(reif)
First argument indexing
takes the principal functor of the first predicate argument (plus a few simple built-in tests) into account to improve the determinism of sufficiently instantiated goals.
This, by itself, does not cover dif/2 satisfactorily.
What can we do? Work with
reified term equality/inequality—effectively indexing dif/2!
some_dif([X|Xs], E) :- % some_dif([X|Xs], E) :-
if_(dif(X,E), true, % ( dif(X,E), true
(X = E, some_dif(Xs,E)) % ; X = E, some_dif(Xs,E)
). % ).
Notice the similarities of the new and the old implementation!
Above, the goal X = E is redundant on the left-hand side. Let's remove it!
some_dif([X|Xs], E) :-
if_(dif(X,E), true, some_dif(Xs,E)).
Sweet! But, alas, we're not quite done (yet)!
?- not_all_equal(Xs).
DOES NOT TERMINATE
What's going on?
It turns out that the implementation of dif/3 prevents us from getting a nice answer sequence for the most general query. To do so—without using additional goals forcing fair enumeration—we need a tweaked implementation of dif/3, which I call diffirst/3:
diffirst(X, Y, T) :-
( X == Y -> T = false
; X \= Y -> T = true
; T = true, dif(X, Y)
; T = false, X = Y
).
Let's use diffirst/3 instead of dif/3 in the definition of predicate some_dif/2:
some_dif([X|Xs], E) :-
if_(diffirst(X,E), true, some_dif(Xs,E)).
So, at long last, here are above queries with the new some_dif/2:
?- not_all_equal(Es). % query #1
dif(_A,_B), Es = [_A,_B|_C]
; dif(_A,_B), Es = [_A,_A,_B|_C]
; dif(_A,_B), Es = [_A,_A,_A,_B|_C]
...
?- not_all_equal([A,B,C]), A=a, B=b. % query #2
A = a, B = b
; false.
?- A=a, B=b, not_all_equal([A,B,C]). % query #3
A = a, B = b.
Query #1 does not terminate, but has the same nice compact answer sequence. Good!
Query #2 is still non-determinstic. Okay. To me this is as good as it gets.
Query #3 has become deterministic: Better now!
The bottom line:
Use library(reif) to tame excess non-determinism while preserving logical purity!
diffirst/3 should find its way into library(reif) :)
EDIT: more general using a meta-predicate (suggested by a comment; thx!)
Let's generalize some_dif/2 like so:
:- meta_predicate some(2,?).
some(P_2, [X|Xs]) :-
if_(call(P_2,X), true, some(P_2,Xs)).
some/2 can be used with reified predicates other than diffirst/3.
Here an update to not_all_equal/1 which now uses some/2 instead of some_dif/2:
not_all_equal([X|Xs]) :-
some(diffirst(X), Xs).
Above sample queries still give the same answers, so I won't show these here.

Prolog , return boolean from consecutive recursive checks

Hello everyone,
The following code does recursive checks. For each call , F gets a value of either 1 or 0 , due to a condition . I want my test_liars predicate return True if all checks had result 1 , and False if at least one call , set F's value to 0.
What test_liars actually does , is not something really eager to explain , but I can if asked.
test_liars should return True to Flag's argument, asked :
test_liars(2,[3,2,1,4,2],[1,0,0,1,0],Flag)
given different list ,rather than [1,0,0,1,0], it must return False
test_liars(_,[],_,_) .
test_liars(N,[HF|TF],[HT|TT],Flag) :-
(HT == 0 -> ( N >= HF -> F = 1 ; F = 0)
; ( N < HF -> F = 1 ; F = 0)),
test_liars(N,TF,TT,Flag),
write(F),
(F == 0 -> Flag = 'True' ; Flag = 'False').
First of all, I think it is more elegant to transform the nested if-then-else structure into a predicate. For instance test_liar/4:
% test_liar(N,HT,HF,F).
test_liar(N,0,HF,1) :-
N >= HF,
!.
test_liar(N,1,HF,1) :-
N < HF,
!.
test_liar(_,_,_,0).
Which makes things easier. Now you can write:
test_liars(_,[],_,_).
test_liars(N,[HF|TF],[HT|TT],Flag) :-
test_liar(N,HT,HF,F),
test_liars(N,TF,TT,Flag),
write(F),
(F == 0 -> Flag = 'True' ; Flag = 'False').
Nevertheless we are not there yet. Your Flag should return 'False' if at least one element is a liar. That means that in the base case, there are no liars, so we should return 'True':
test_liars(_,[],_,'True').
In the inductive case we thus have to construct some kind of "and", like:
custom_and('True',1,'True') :-
!.
custom_and(_,_,'False').
Now we only need to call this custom_and/3 on the outcome of the recursive test_liars and test_liar:
test_liars(N,[HF|TF],[HT|TT],Flag) :-
test_liar(N,HT,HF,F),
test_liars(N,TF,TT,SubFlag),
write(F),
custom_and(SubFlag,F,Flag).
Or now the full code:
test_liar(N,0,HF,1) :-
N >= HF,
!.
test_liar(N,1,HF,1) :-
N < HF,
!.
test_liar(_,_,_,0).
custom_and('True',1,'True') :-
!.
custom_and(_,_,'False').
test_liars(_,[],_,'True').
test_liars(N,[HF|TF],[HT|TT],Flag) :-
test_liar(N,HT,HF,F),
test_liars(N,TF,TT,SubFlag),
write(F),
custom_and(SubFlag,F,Flag).

What does this code mean and do?

I am completely new to Prolog and I can't understand this piece of code. How would you read these 4 clauses? What do they do?
a([]).
a([_|L]):-b(L).
b([_]).
b([_|L]):-a(L).
Thank you.
As #repeat suggested in his comment, run a general query, and here's what you get:
| ?- a(Xs).
Xs = [] ? ;
Xs = [_,_] ? ;
Xs = [_,_] ? ;
Xs = [_,_,_,_] ? ;
Xs = [_,_,_,_] ? ;
Xs = [_,_,_,_,_,_] ? ;
Xs = [_,_,_,_,_,_] ? ;
...
And:
| ?- b(Xs).
Xs = [_] ? ;
Xs = [_] ? ;
Xs = [_,_,_] ? ;
Xs = [_,_,_] ? ;
Xs = [_,_,_,_,_] ? ;
Xs = [_,_,_,_,_] ? ;
Xs = [_,_,_,_,_,_,_] ? ;
Xs = [_,_,_,_,_,_,_] ? ;
...
So a(Xs) succeeds if Xs is a list containing an even number of elements, and b(Xs) succeeds if Xs is a list containing an odd number of arguments. As you can see, a/1 and b/1 succeed twice in each case except a([]). which succeeds only once. So it is not an efficient predicate for determining list length parity.
Let's rewrite these with more descriptive names:
even([]).
even([_|L]) :- odd(L).
odd([_]).
odd([_|L]) :- even(L).
Now let's "read" what they say:
[] is an even list
[_|L] is an even list if L is an odd list
[_] is an odd list
[_|L] is an odd list if L is an even list
These sound logical, but why do even/1 and odd/1 succeed twice with the exception of even([])? If you look at the definition of odd/1, there are two ways for [_] to succeed. One is via the odd([_]). clause. The second is by the second odd/1, since you would have:
odd([_|[]]) :- even([]). % [_] == [_|[]]
Since both even/1 and odd/1 calls (with the exception of even([]).) eventually recurse down to a call to odd([_]), you'll get two solutions.
One way to eliminate the ambiguity in the logic is to refactor them a little. Consider the following recursive rules:
A list of at least 2 elements has an even number of elements if the tail of the list, after the first 2, is also even.
A list of at least 2 elements has an odd number of elements if the tail of the list, after the first 2, is also odd.
Translating these rules to Prolog, including the base cases as before:
even([]).
even([_,_|L]) :- even(L).
odd([_]).
odd([_,_|L]) :- odd(L).
Now the results will be:
| ?- even(Xs).
Xs = [] ? ;
Xs = [_,_] ? ;
Xs = [_,_,_,_] ? ;
Xs = [_,_,_,_,_,_] ? ;
...
And
| ?- odd(Xs).
Xs = [_] ? ;
Xs = [_,_,_] ? ;
Xs = [_,_,_,_,_] ? ;
...
Following CapelliC's suggestion of using a DCG, similar rules can be written:
even --> [] | [_,_], even.
odd --> [_] | [_,_], odd.
With results:
| ?- phrase(even, L).
L = [] ? ;
L = [_,_] ? ;
L = [_,_,_,_] ? ;
...
And
| ?- phrase(odd, L).
L = [_] ? ;
L = [_,_,_] ? ;
L = [_,_,_,_,_] ? ;
...
Following #false's suggestion, an even more direct "fix" to the original code would be to eliminate the redundant base case for odd([_]). since it's already covered by the base case for even([]). This is also a little simpler than the above solution since it takes advantage of the interdependency between the even/1 and odd/1 predicates (in the above solution, even/1 and odd/1 stand on their own).
even([]).
even([_|L]) :- odd(L).
odd([_|L]) :- even(L).
Or, in DCG:
even --> [] | [_], odd.
odd --> [_], even.
the argument schema is important:
a) the list is clearly a counter, since we never consider the content.
b) just a suggestion: read logic as productions, or as DCG
a-->[];[_],b.
b-->[_];[_],a.
to be called - for instance
?- phrase(a, [w,h,a,t]).

List creation in Erlang

From "Erlang Programming" by Cesarini exercise 3-2
As I go through "Erlang Programming" I get weird list creation issues. From exercise 3-2 I wrote two similar functions.
create( 0 ) -> [];
create( N ) when N > 0 -> [ N | create( N-1 ) ].
reverse_create( 0 ) -> [];
reverse_create( N ) when N > 0 -> [ reverse_create( N-1 ) | N ].
so create(3) generates as I'd expect.
exercise3:create(3).
[3,2,1]
but reverse_create does not generate the list I expect.
exercise3:reverse_create(3).
[[[[]|1]|2]|3]
What do I need to change so that reverse_create(3) returns [1,2,3]? Thanks for explaining.
reverse_create returns a list and you using that as head element to create the list which is resulting in nested lists. Try this solution:
reverse_create( 0 ) -> [];
reverse_create( N ) when N > 0 -> reverse_create( N-1 ) ++ [N].
EDIT: A much better implementation would be:
reverse_create2(N) -> reverse_create_helper(N, []).
reverse_create_helper(0, Acc) ->
Acc;
reverse_create_helper(N, Acc) ->
reverse_create_helper(N-1, [N|Acc]).
Normally a function such as reverse_create would be done in a tail-recursive fashion with an accumulator.
reverse_create(N) ->
reverse_create(N, []).
reverse_create(0, Acc) ->
Acc;
reverse_create(N, Acc) when N > 0 ->
reverse_create(N - 1, [N | Acc]).
Of course, you could always do:
reverse_create(N) -> lists:reverse(create(N)).
This would actually run faster. But that obviously isn't the intent of the exercise. :)
I'm reading the same book, so I'm no more expert than you, but this worked for me...
create(0) -> [];
create(N) when N > 0 -> create(N-1) ++ [N].
reverse_create(0) -> [];
reverse_create(N) when N > 0 -> [N|create(N-1)].
This is
reverse_create(0) -> [];
reverse_create(N) ->
list_create_1(1, N, []).
list_create_1(I, N, List) when N >= I ->
list_create_1(I + 1, N, [I | List]);
list_create_1(_, _, List) -> List.