FunBlast
FunBlast

Reputation: 3

Prolog rule which replaces with 0 every negative number from a list

I need to write a rule that replaces every negative number from a list with 0. This is my code:

neg_to_0(L,R) :-
  ( 
    nth1(X,L,E),
    E<0,
    replace(E,0,L,L2),
    neg_to_0(L2,R2)
  ) ;
  R = L.

replace(_, _, [], []).
replace(O, R, [O|T], [R|T2]) :-         replace(O, R, T, T2).
replace(O, R, [H|T], [H|T2]) :- H \= O, replace(O, R, T, T2).

I have a rule "replace" which takes the element that needs to be replaced with 0 and returns the new list, but it stops after the rule replaces the values and return the new list, so i made the function to recall the main function with the new data so it can replace the other negative values :

replace(E,0,L,L2),
neg_to_0(L2,R2)
);
R = L.

On the last iteration, when it could not detect any negative numbers, i made it so that it saves the last correct list, but i only get back a "True" instead of the correct list.

Upvotes: 0

Views: 314

Answers (1)

Nicholas Carey
Nicholas Carey

Reputation: 74325

Your code seems... awfully complex.

You seem to be trying to write procedural (imperative) code. Prolog is not an imperative language: one describes "truth" and lets Prolog's "inference engine" figure it out. And, pretty much everything is recursive by nature in Prolog.

So, for your problem, we have just a few simple cases:

  • The empty list [], in which case, the transformed list is... the empty list.

  • A non-empty list. [N|Ns] breaks it up into its head (N) and its tail (Ns). If N < 0, we replace it with 0; otherwise we keep it as-is. And then we recurse down on the tail.

To replace negative numbers in a list with zero, you don't need much more than this:

%
% negatives_to_zero/2 replaces negative numbers with 0
%
negatives_to_zero( []     , []     ) .  % nothing to do for the empty list
negatives_to_zero( [N|Ns] , [M|Ms] ) :- % for a non-empty list,
  M is max(N,0),                        % get the max of N and 0,
  negatives_to_zero(Ns,Ms).             % and recurse down on the tail 

You can easily generalize this, of course to clamp numbers or lists of numbers, and constrain them to lie within a specified range:

%--------------------------------------------------------------------------------
% clamp( N , Min, Max, R )
%
% Constrain N such that Min <= N <= Max, returning R
% 
% Use -inf (negative infinity) to indicate an open lower limit
% Use +inf (infinity) or +inf (positive infinity) to indicate an open upper limit
% -------------------------------------------------------------------------------
clamp( Ns , -inf , +inf , Ns ) .
clamp( N  , Min , Max , R  ) :- number(N)   , clamp_n(N,Min,Max,R).
clamp( Ns , Min , Max , Rs ) :- listish(Ns) , clamp_l(Ns,Min,Max,Rs).

clamp_n( N , _   , _   , R ) :- \+number(N), !, R = N.
clamp_n( N , Min , Max , R ) :- T is max(N,Min), R is min(T,Max).

clamp_l( []     , _   , _   , []     ) .
clamp_l( [X|Xs] , Min , Max , [Y|Ys] ) :- clamp_n(X,Min,Max,Y), clamp(Xs,Min,Max,Ys).

listish( T     ) :- var(T), !, fail.
listish( []    ) .
listish( [_|_] ) .

Upvotes: 1

Related Questions