Related
This question already has answers here:
Prolog converting integer to a list of digit
(6 answers)
Closed 1 year ago.
I wrote a Prolog predicate which transforms a list of digits in a number. So, for example, if the input is the list [1, 2, 3] then the output will be the number 123. This is my code:
convertListToNum([], Col, Col).
convertListToNum([H|T], Col, R):-
Col2 is Col * 10 + H,
convertListToNum(T, Col2, R).
convertListToNumMain(L, R):-
convertListToNum(L, 0, R).
As you can see, the arguments of the predicate are L=input, R=output, since I provide the list L as input into ConvertListToNumMain and I will get as output the number in R. If I try this predicate, it works as expected: convertListToNumMain([1, 2, 3], R) will give as output R=123.
Now what I want to do is use this function by providing the output number instead of the input list. I will use L=output, R=input. So I will not provide the list L anymore, I will provide the number R and expect to receive the list as output. So what I want is to call this: convertListToNumMain(L, 123) and I expect to receive as output L=[1, 2, 3]. However, when I call this I get the error Arguments are not sufficiently instantiated. I assume it is because in the line Col2 is Col * 10 + H I try to work with the first element of the list even tough the list is not instantiated, but I am not sure. What can I do to make this work?
I want to convert a list of digits to a number and then the number back to a list of digits and I thought I could get away with this, but apparently not. The alternative is to create another complex and inefficient predicate which will at each step find the most significant digit of a number, then add it to the list, find that number without that most significant digit and then make the next call. Or at least that is my idea. That is a lot of code and (it seems to me) more inefficient. So is there any way I can use what I already have by providing the output? If not, is there a better way than my idea above to convert a number into a list of digits (recursively, of course, since I'm using Prolog)?
It's not that complicated. Or inefficient.
int_list( N , Ds ) :- nonvar(N), number_to_digits(N,Ds).
int_list( N , Ds ) :- nonvar(Ds), digits_to_number(Ds,0,N).
digits_to_number( [] , N , N ) .
digits_to_number( [D|Ds] , T , N ) :-
T1 is 10 * T + D ,
digits_to_number(Ds,T1,N).
number_to_digits( 0 , [0] ).
number_to_digits( N , Ds ) :-
N > 0,
digits(N, [], Ds).
digits( N , T , Ds ) :-
N > 0,
D is N mod 10,
N1 is N div 10,
digits(N1,[D|T],Ds).
digits( 0 , Ds , Ds ) .
But it might be easier to use built-in predicate number_codes/2:
int_digits( N , Ds ) :- nonvar(Ds) , codes_digits(Cs,Ds) , number_codes(N,Cs) .
int_digits( N , Ds ) :- nonvar(N) , number_codes(N,Cs) , codes_digits(Cs,Ds) .
codes_digits( [] , [] ) .
codes_digits( [C|Cs] , [D|Ds] ) :- code_digit(C,D), codes_digits(Cs,Ds).
code_digit(C,D) :- nonvar(C), D is C-48.
code_digit(C,D) :- nonvar(D), C is D+48.
I am trying to get the first N chars of a string. From looking at the following question I understand I can use
> sub_atom(str, X, Y, W, Z).
Getting last char of a string in Prolog
The problem is that I can't find good documentation for this function, here is the formal doc:
http://www.swi-prolog.org/pldoc/man?predicate=sub_atom/5
I will be happy for a link or explanation for how this func works. Also, I will be happy for help on the following example:
How to get all chars that are before "/" in "prolog/a" that will work something like this:
sub_atom(prolog/a, X , Y , W, Z). => prolog
I think the documentation is clear enough:
sub_atom(+Atom, ?Before, ?Len, ?After, ?Sub)
Atom is the initial atom from which you ant to deduct the Subatom.
Before is the position that the subatom starts, counting starts from 1.
Len is the length of the Subatom
After is the length of the the remaining subatom, e.g sub_atom(abc, 1, 1, After, b). gives After = 1 (remaining is c which is of length 1)
Sub is the Subatom acquired from the initial atom.
In your example the problem is that prolog/r is not an atom but a compound term because it contains '/'. Though "prolog/r" (in double quotes as a string) is an atom.
I suggest to use atomic_list _concat/3 in order to achieve that
?- atomic_list_concat(L, '/', "prolog/r").
L = [prolog, r].
I'm new to Prolog and I'm facing some exercises, this one for example ask me to write a predicate histogram/1 to take a list as argument and print an horizontal histogram with the occurrences of numbers 0..9 in the list. For example:
?- histogram([1,3,4,1,1,4,7]).
0
1 ***
2
3 *
4 **
5
6
7 *
8
9
true.
My program is the following, and works as requested, but I've a doubt: as you can see I used cut predicate and semicolon in that way (first line), only to obtain TRUE after the histogram (just to fit the example provided, I don't really care about it!), if you omit this you'll get always FALSE as N<10 will fail...
My question is: is this a proper way to use the cut predicate? If not, how could I improve my program (any other suggestion will be appreciated)?
histogram(X):-
valid(X),
( print(0,X)
; !
).
print(N,X):-
write(N) , tab(2), count(N,X) , nl,
N1 is N+1,
N1 < 10 ,
print(N1,X).
count(_,[]).
count(E,[E|C]):-
write('*') ,
count(E,C).
count(E,[Y|C]):-
E\=Y ,
count(E,C).
valid([]).
valid([X|T]):-
integer(X),
X>=0,
X<10,
valid(T).
maybe
print(N,X):- N < 10, write(N), tab(2), count(N,X), nl, N1 is N+1, print(N1,X).
print(10,_).
and the whole program can be made much shorter using SWI-Prolog library, for instance
histogram(X) :-
forall(between(0,9,N), (findall(*,member(N,X),H), format('~d ~s~n', [N, H]))).
Kindly, could you help me in the following:
I am writing a Prolog program that takes two numbers digits then combine them as one number, for example:
Num1: 5
Num2: 1
Then the new number is 51.
Assume V1 is the first number digit and V2 is the second number digit. I want to combine V1 and V2 then multiply the new number with V3, so my question is how I can do it?
calculateR(R, E, V1, V2, V3, V4):-
R is V1 V2 * V3,
E is R * V4.
Your help is appreciated.
Here is another solution that is based on the idea of #aBathologist and that relies on ISO predicates only, and does not dependent on SWI's idiosyncratic modifications and extensions. Nor does it have most probably unwanted solutions like calculateR('0x1',1,1,17). nor calculateR(1.0e+30,0,1,1.0e+300). Nor does it create unnecessary temporary atoms.
So the idea is to restrict the definition to decimal numbers:
digit_digit_number(D1, D2, N) :-
number_chars(D1, [Ch1]),
number_chars(D2, [Ch2]),
number_chars(N, [Ch1,Ch2]).
Here is a version which better clarifies the relational nature of Prolog - using library(clpfd) which is available in many Prolog systems (SICStus, SWI, B, GNU, YAP). It is essentially the same program as the one with (is)/2 except that I added further redundant constraints that permit the system to ensure termination in more general cases, too:
:- use_module(library(clpfd)).
digits_radix_number(Ds, R, N) :-
digits_radix_numberd(Ds, R, 0,N).
digits_radix_numberd([], _, N,N).
digits_radix_numberd([D|Ds], R, N0,N) :-
D #>= 0, D #< R,
R #> 0,
N0 #=< N,
N1 #= D+N0*R,
digits_radix_numberd(Ds, R, N1,N).
Here are some uses:
?- digits_radix_number([1,4,2],10,N).
N = 142.
?- digits_radix_number([1,4,2],R,142).
R = 10.
?- digits_radix_number([1,4,2],R,N).
R in 5..sup, 4+R#=_A, _A*R#=_B, _A in 9..sup, N#>=_A,
N in 47..sup, 2+_B#=N, _B in 45..sup.
That last query asks for all possible radices that represent [1,4,2] as a number. As you can see, not anything can be represented that way. The radix has to be 5 or larger which is not surprising given the digit 4, and the number itself has to be at least 47.
Let's say we want to get a value between 1450..1500, what radix do we need to do that?
?- digits_radix_number([1,4,2],R,N), N in 1450..1500.
R in 33..40, 4+R#=_A, _A*R#=_B, _A in 37..44,
N in 1450..1500, 2+_B#=N, _B in 1448..1498.
Gnah, again gibberish. This answer contains many extra equations that have to hold. Prolog essentially says: Oh yes, there is a solution, provided all this fine print is true. Do the math yourself!
But let's face it: It is better if Prolog gives such hard-to-swallow answer than if it would say Yes.
Fortunately there are ways to remove such extra conditions. One of the simplest is called "labeling", where Prolog will "try out" value after value:
?- digits_radix_number([1,4,2],R,N), N in 1450..1500, labeling([],[N]).
false.
That is clear response now! There is no solution. All these extra conditions where essentially false, like all that fine print in your insurance policy...
Here's another question: Given the radix and the value, what are the required digits?
?- digits_radix_number(D,10,142).
D = [1,4,2]
; D = [0,1,4,2]
; D = [0,0,1,4,2]
; D = [0,0,0,1,4,2]
; D = [0,0,0,0,1,4,2]
; ... .
So that query can never terminate, because 00142 is the same number as 142. Just as 007 is agent number 7.
Here is a straight-forward solution that should work in any Prolog close to ISO:
digits_radix_to_number(Ds, R, N) :-
digits_radix_to_number(Ds, R, 0,N).
digits_radix_to_number([], _, N,N).
digits_radix_to_number([D|Ds], R, N0,N) :-
N1 is D+N0*R,
digits_radix_to_number(Ds, R, N1,N).
?- digits_radix_to_number([1,4,2],10,R).
R = 142.
Edit: In a comment, #false pointed out that this answer is SWI-Prolog specific.
You can achieve your desired goal by treating the numerals as atoms and concatenating them, and then converting the resultant atom into a number.
I'll use atom_concat/3 to combine the two numerals. In this predicate, the third argument with be the combination of atoms in its first and second arguments. E.g.,
?- atom_concat(blingo, dingo, X).
X = blingodingo.
Note that, when you do this with two numerals, the result is an atom not a number. This is indicated by the single quotes enclosing the the result:
?- atom_concat(5, 1, X).
X = '51'.
But 51 \= '51' and we cannot multiply an atom by number. We can use atom_number/2 to convert this atom into a number:
?- atom_number('51', X).
X = 51.
That's all there is to it! Your predicate might look like this:
calculateR(No1, No2, Multiplier, Result) :-
atom_concat(No1, No2, NewNoAtom),
atom_number(NewNoAtom, NewNo),
Result is NewNo * Multiplier.
Usage example:
?- calculateR(5, 1, 3, X).
X = 153.
Of course, you'll need more if you want to prompt the user for input.
I expect #Wouter Beek's answer is more efficient, since it doesn't rely on converting the numbers to and from atoms, but just uses the assumption that each numeral is a single digit to determine the resulting number based on their position. E.g., if 5 is in the 10s place and 1 is in the 1s place, then the combination of 5 and 1 will be 5 * 10 + 1 * 1. The answer I suggest here will work with multiple digit numerals, e.g., in calculateR(12, 345, 3, Result), Result is 1234 * 3. Depending on what you're after this may or may not be a desired result.
If you know the radix of the numbers involved (and the radix is the same for all the numbers involved), then you can use the reverse index of the individual numbers in order to calculate their positional summation.
:- use_module(library(aggregate)).
:- use_module(library(lists)).
digits_to_number(Numbers1, Radix, PositionalSummation):-
reverse(Numbers1, Numbers2),
aggregate_all(
sum(PartOfNumber),
(
nth0(Position, Numbers2, Number),
PartOfNumber is Number * Radix ^ Position
),
PositionalSummation
).
Examples of use:
?- digits_to_number([5,1], 10, N).
N = 51.
?- digits_to_number([5,1], 16, N).
N = 81.
(The code sample is mainly intended to bring the idea across. Notice that I use aggregate_all/3 from SWI-Prolog here. The same could be achieved by using ISO predicates exclusively.)
I'm try to make a list of the values n,n+1,...2*n-1
for example if I have n=4 ==> [4,5,6,7]
I have managed to write this code but it shows the list of first n elements. Can you help me modify it?
create(N,L):-
create_list(N,[],L).
create_list(0,L,L).
create_list(N,R,L):-
N>0,
N1=N-1,
create_list(N1,[N|R],L).
Here's how you could write a logically pure version of create/2 based on list_from_to/3, defined in my answer to question "Fill list in SWI-Prolog":
create(From,Zs):-
To #= 2*From-1,
list_from_to(Zs,From,To).
Here the query you had in your question:
?- create(4,Zs).
Zs = [4,5,6,7] ;
false.
As create/2 is pure, it is versatile and can also be used in different ways:
?- create(N,[4,5,6,7]).
N = 4.
?- create(N,[4,_,_,_]).
N = 4.
You're doing 'in reverse', in a certain sense. Try
create(N,L):-
M is 2*N,
create_list(N,M,L).
create_list(N,M,[N|R]):-
N < M,
N1 = N+1, % note: check if your Prolog accepts is/2 instead
create_list(N1,M,R).
create_list(M,M,[]).