how to understand this recursion in SML? - recursion

why does hd dates become the oldest one? I just cannot figure out the process
fun oldest(dates : (int * int * int) list) =
if null dates
then NONE
else
let
val d = oldest(tl dates)
in
if isSome d andalso is_older(valOf d, hd dates)
then d
else SOME(hd dates)
end

In my opinion, this is much easier to understand with a small helper function and case analysis by pattern matching.
(My advice is to get comfortable with patterns and case analysis, avoiding conditionals and selector functions. It's much easier to reason about one thing at a time than to remember a whole chain of logic and destructuring.)
Rewriting your code in that manner might give this:
fun oldest_of (d, d') = if is_older (d, d') then d else d'
fun oldest [] = NONE
| oldest (d::ds) = case oldest ds of
NONE => SOME d
| SOME d' => SOME (oldest_of (d, d'))
That is,
If the list is empty, there is no oldest date
Otherwise, find the oldest date in the input's tail;
If there isn't one, the input's first element must be the oldest
Otherwise, pick the oldest of that and the input's first element
Now it's (hopefully) obvious that the NONE case in the recursion can only occur if the tail, ds, is empty - that is, if the input has exactly one element.
Let's lift that out into its own case:
fun oldest [] = NONE
| oldest [d] = SOME d
| oldest (d::ds) = SOME (oldest_of (d, valOf (oldest ds)))
This reads a lot like this definition of what the oldest date is:
If there are no dates, there is no oldest date
If there is only one date, that is the oldest
If there are at least two dates, it is the oldest of the first date and the oldest one of the rest
which doesn't need much inductive thinking.

You can prove it by induction:
In the case of an empty list, oldest will return NONE.
If the list contains an element, we'll go into the else branch, letting d be NONE, because tl of a list with one element returns an empty list. isSome d will be false, which is why we will return SOME(hd dates) — the only element, which is by definition the oldest.
Assuming the function works for the case of a list of n-1 items, let's see what happens when the list has n items: We will go into the else branch and assign to d the oldest element from the tail of the list (which works, because it contains n-1 items). Now there's two possible scenarios:
a. d is older than the first element of the list. In that case, d will be returned, because both isSome d and is_older(valOf d, hd dates) will be true.
b. d is not older than the first element of the list. We will therefore return SOME(hd dates) — the first element of the list.
We have shown that it works for n elements if it works for n-1, and we have shown that it works for n=0 and n=1 (technically we could have skipped step 2). It will therefore work for a list of any size.

Related

How to return in prolog all elements from right to left greater than an integer in one predicate?

I have to write a code that return all elements from a given list which are strictly greater than a given integer, it returns from left to right. I cannot use recursion or any other function except the built-in functions: append/3, append/2, member/2, select/3, reverse/2, findall/3, bagof/3, setof/3, sumlist/2
Example case:
greater_list([1,9,2,8,3,7,12],7, X).
X = 12 ? ;
X = 8 ? ;
X = 9 ? ;
no
I can write it with recursion or help predicates, but without them I do not know how to start. I could use findall/3 but it would not return element by elements, but a list of elements greater than that given number.
I can write it with recursion or help predicates, but without them I do not know how to start.
I would be interested in how you think you can solve this with helper predicates but not without.
But for starting, consider this: What you need to do is to enumerate certain elements of the list. That is, enumerate elements of the list that have some property.
So to start, you need to know how to enumerate elements of the list. Once you know how to do that, you can worry about the property that they must fulfill.
You can enumerate list elements using member/2:
?- member(X, [1,9,2,8,3,7,12]).
X = 1 ;
X = 9 ;
X = 2 ;
X = 8 ;
X = 3 ;
X = 7 ;
X = 12.
Now, we want to enumerate elements, but only those that fulfill the property X > 7. This is equivalent to saying that "X is a member of the list, and X > 7". In Prolog, (something like) "and" is written with a comma (,):
?- member(X, [1,9,2,8,3,7,12]), X > 7.
X = 9 ;
X = 8 ;
X = 12.
Your predicate is supposed to take a variable limit, not hardcode the limit of 7. This will be similar to:
?- Limit = 7, member(X, [1,9,2,8,3,7,12]), X > Limit.
Limit = 7,
X = 9 ;
Limit = 7,
X = 8 ;
Limit = 7,
X = 12.
Packing this up in a predicate definition will get you started. It looks like the order in which the elements are enumerated here is the reverse of what is intended. Maybe one of your built-ins helps you with this...
(Also, if you know how to write this using findall, you can then use member to enumerate the elements of the findall'ed list. But you shouldn't get in the habit of using findall in general, and especially not if the required solution isn't even a list. Beginners and bad teachers tend to over-emphasize putting things in lists, because that is what you have to do in lesser programming languages. Free yourself from thinking in other languages, even if your teacher can't.)
You can use findall/3 to get a list of the sought elements and then use member/2 to enumerate the members of that list:
greater_list(L,Limit,X) :-
findall(E,(member(E,L),E>Limit),Es),
member(X,Es).
Then:
?- greater_list([1,9,2,8,3,7,12],7, X).
X = 9 ;
X = 8 ;
X = 12.
?- greater_list([],7, X).
false.
And in a roundabout way:
?- findall(X, greater_list([1,9,2,8,3,7,12],7, X), Xs).
Xs = [9, 8, 12].
NB. this relies on recursion, I didn't notice that you couldn't use it at first
Instead of reversing the list, you can write the predicate without other helper predicates and consider first the recursive case. This ensures the first element to be checked against N will be the last element of the list.
greater_list([_|L], N, X) :- greater_list(L,N,X).
greater_list([X|_], N, X) :- X > N.
The lack of a clause for the empty list means that the predicate fails for empty lists.
The first clause above declares that X is item from a list that is greater than N if it is such an item in the sublist L.
The second clause, tried on backtracking, declares that the predicate is also true if X is the front element of the list and it is greater than N.
Both clause make Prolog search first in the sublist, and only when backtracking, consider the values stored in the list. As backtracking unfolds from deeper recursion levels first, the rule will be applied in a way that checks the last element first, then second to last, etc.
[eclipse 2]: greater_list([1,9,2,8,3,7,12],7, X).
X = 12
Yes (0.00s cpu, solution 1, maybe more) ? ;
X = 8
Yes (0.00s cpu, solution 2, maybe more) ? ;
X = 9
Yes (0.00s cpu, solution 3, maybe more) ? ;
No (0.00s cpu)

Explanation for group by 3 elements from a list function using FOLD in OCAML

I have a piece of code that does the following: group 3 elements of a list of n elements. The main function is called group_by_3. For example, executing group_by_3 [1;2;3;4;5;6;7] will give me ([1;2;3],[4;5;6],[7]).
let group_by_3 lst =
let accum = ( [], [], 0 )
in
let f (all_groups, current_group, size) x =
if size = 3
then ( (List.rev current_group) :: all_groups,
[x], 1 )
else ( all_groups, x::current_group, size+1)
in
let (groups, last, _) = List.fold_left f accum lst
in List.rev ( List.rev last :: groups)
I don't really understand why this works (it is provided in class).
What are all_groups, current_group, size?
What does this do?
if size = 3
then ( (List.rev current_group) :: all_groups,
[x], 1 )
else ( all_groups, x::current_group, size+1)
Thank you!
All groups is the final answer, current is the group of three elements filled while parsing the input and size is the number of elements actually in current I guess.
Do you know the behavior of fold_left?
What are all_groups, current_group, size?
These are the three pieces of state required to produce the groupings by visiting each item in the input sequence. These individual states are combined in a 3-tuple to form a single state for the fold. Ultimately we'll only care about all_groups, and the other two are just intermediate state necessary to construct that.
all_groups: This is a list value that accretes completed groupings. Whenever we've seen enough new elements to satisfy the group size, we make a new group and add it to this list.
current_group: This is also a list value, but more of a temporary buffer to build up a grouping until it reaches size. When it's big enough, it gets added to all_groups and is reset to a new group/list with the current item [x].
size this is just a counter to track how many items are in current_group.
What does this do?
if size = 3 simply decides whether we want to keep accumulating elements or if we've got enough for a grouping.
then ( (List.rev current_group) :: all_groups, [x], 1 ) is building/returning the new accumulator value, which is a 3-tuple of all_groups, current_group, and size. The List.rev call is necessary because of the way the list is being grown, in the else branch; it's fastest to add items to the front of a list, but this is the reverse of the input sequence thus we reverse them. x is the current item, which will be the first item in the new, growing group. 1 is of course the size of that new group.
else ( all_groups, x::current_group, size+1) is popping the current item x onto the front of the current_group list and incrementing the size counter.
Below that section is the logic that takes care of any straggler items that don't fit neatly into groupings of three.

working with powers in ocaml

I am trying to write a recursive function that will return true if second number is power of first number.
For example:
find_power 3 9 will return true
find_power 2 9 will return false because the power of 2 is 8 not 9
This is what I have tried but I need a recursive solution
let rec find_power first second =
if (second mod first = 0)
return true
else
false ;;
A recursive function has the following rough form
let rec myfun a b =
if answer is obvious then
obvious_answer
else
let (a', b') = smaller_example_of_same_problem a b in
myfun a' b'
In your case, I'd say the answer is obvious if the second number is not a multiple of the first or if it's 1. That is essentially all your code is doing now, it's testing the obvious part. (Except you're not handling the 0th power, i.e., 1.)
So, you need to figure out how to make a smaller example of the same problem. You know (by hypothesis) that the second number is a multiple of the first one. And you know that x * a is a power of a if and only if x is a power of a. Since x is smaller than x * a, this is a smaller example of the same problem.
This approach doesn't work particularly well in some edge cases, like when the first number is 1 (since x is not smaller than x * 1). You can probably handle them separately.

Prolog query fails

This is supposed to calculate the sum of two lists. The lists can be of different size.
sum([],[],[]).
sum(A,[],A).
sum([],B,B).
sum([A|Int1],[B|Int2],[C|Int3]) :-
(
C =:= A + B
;
((C =:= A), B = [])
;
((C =:= B), A = [])
),
sum(Int1,Int2,Int3).
It seems to work correctly, except when trying to find the sum of two lists. Then it gives the following error:
ERROR: =:=/2: Arguments are not sufficiently instantiated
I don't see why. There's a recursive and a basis step, what exactly is not yet instantiated and how do I fix it?
[1] While your disjunctions in the last clause are -- to some extent -- conceptually correct, Prolog considers these disjunctions in sequence. So it first considers C =:= A + B. But either A or B can be the empty list! This is what causes the error you reported, since the empty list is not allowed to occur in a numeric operation.
[2] You need to use C is A + b (assignment) i.o. C =:= A + B (numeric equivalence).
[3] If you say [A|Int1] and then A = [], then this means that [A|Int1] is not (only) a list of integers (as you claim it is) but (also) a list of lists! You probably intend to check whether the first or the second list is empty, not whether either contains the empty list.
Staying close to your original program, I would suggest to reorder and change things in the following way:
sumOf([], [], []):- !.
sumOf([], [B|Bs], [C|Cs]):- !,
C is B,
sumOf([], Bs, Cs).
sumOf([A|As], [], [C|Cs]):- !,
C is A,
sumOf(As, [], Cs).
sumOf([A|As], [B|Bs], [C|Cs]):-
C is A + B,
sumOf(As, Bs, Cs).
For example:
?- sumOf([1,2,3], [1,-90], X).
X = [2, -88, 3]
Notice my use of the cut (symbol !) in the above. This makes sure that the same answer is not given multiple times or -- more technically -- that no choicepoints are kept (and is called determinism).
You should read a tutorial or a book. Anyway, this is how you add two things to each other:
Result is A + B
This is how you could add all elements of one list:
sum([], 0). % because the sum of nothing is zero
sum([X|Xs], Sum) :-
sum(Xs, Sum0),
Sum is X + Sum0.
And this is how you could add the sums of a list of lists:
sums([], 0).
sums([L|Ls], Sums) :-
sums(Ls, Sums0),
sum(L, S),
Sums is Sums0 + S.

number distribution without repeatable numbers

we have a problem in which we need to order/distribute the given set/s such that the numbers are not repeatable
here is an example ,say i have 4 sets
{A,A,A,A,A,A}
{B,B}
{C}
{D,D,D}
the resultant should be something like A,D,A,B,A,D,C,A,D,A,B,A
with no repeatable occurrence.
any thoughts,Algorithms..could be appreciated.
EDIT : sorry for not being clear, by occurrence I meant patterns like AA or BB or CC shouldn't
in the resultant it's OK to have ADAD
Thanks
Dee
A moment's consideration yielded this algorithm:
Let A be the symbol repeated the most times.
Let N be the number of occurrences of A.
Let Rest be the concatenation of the remaining symbols, in order.
Let Buckets be a list of length N, where each element Buckets[i] is an array containing a single A.
Iterate over Buckets: for each index i, pop an element from Rest and append it to Buckets[i]. When you reach the end of Buckets, start from the first index again. When you reach the end of Rest, you are done.
The answer is the concatenation of the contents of Buckets.
Your example:
Let A = 'A'.
Let N = 6.
Let Rest = [B, B, C, D, D, D].
Let Buckets = [[A], [A], [A], [A], [A], [A]].
After iterating, Buckets is [[A, B], [A, B], [A, C], [A, D], [A, D], [A, D]]. The output is ABABACADADAD.
Always pick the bucket with the most amount left.
My rusty Matlab skills did this:
generate a random distribution:
symbols = ceil(rand()*10)+1
maxn = ceil(rand()*20)
distribution= [floor(rand(1,symbols)*maxn);(1:symbols)]'
last = -1;
sequence=[]; #output vector
while sum(distribution(:,1))>0 ; #while something is left
distribution= sortrows(distribution); #sort the matrix
if last == distribution(end,2) #pick the one with the one with the second most elements
if a(end-1,1) ==0 #this means there are no fillers left
break
end
last = distribution(end-1,2);
distribution(end-1,1)--;
else #pick the one with the most elements
last = distribution(end,2);
distribution(end,1) --;
endif
sequence(end+1)=last;
end
sequence
rest = distribution'
Note: My symbols are numbers instead of letters.
Edit: Here is some (beautified) output from the script.

Resources