I need to write a Scheme function (union s1 s2)
that when given
two sets, let's say
s1 = ((1 3) (5 13) (25 100))
s2 = ((2 4) (17 26) (97 100))
will give
(union s1 s2) ----> ((1 4) (5 13) (17 100))
Also if
s1 = ((1 3) (5 13) (25 110) (199 300))
s2 = ((2 4) (17 26) (97 100) (110 200) (288 500))
then
(union s1 s2) ----> ((1 4) (5 13) (17 500))
Can anybody suggest me how to do this? I have no idea how to start.
This is a very simple problem. Each your range specification consists of list of intervals, ordered on their
car
values (and moreover, any element'scadr
is smaller than the following element'scar
).You just start with an empty ranges specification, a list of one empty range:
( () )
.Then, on each step you take one head element from one of the lists that has the smallest
car
value (of the two, since the lists are ordered, remember?) , and add it into your ranges specification. When both lists are exhausted (empty), you're done.More specifically, it will be beneficial to maintain the resulting ranges specification in reversed order, separately holding a most recent range. If the incoming range (from one of the two heads) merges with it, you update the last range. If not, you push the last range into the reversed list, and hold the incoming head as the new last range.
Finishing up is equally trivial. It will also be easier to handle the inputs if you maintain the two lists in one list
(list s1 s2)
and write a special functionget-input
that takes this list and produces its result as a list,next-range
and nexttwo-lists
(or()
to signal that the both lists are exhausted).Just so you know, it is an instance of a general pattern of iteration (usually coded in Scheme with named-let or otherwise tail-recursive functions, but there's also an explicit
do
construct), processing an input list's elements one by one, combining them with an accumulator. Keeping previous parts of accumulator reversed is a pattern too, viz. a zipper.An example (your 2nd):
Instead of working with 2 sets, I would suggest
car
)That way, you always compare the first and second element of the input (merged) list, and you know that they are ordered, which greatly simplifies your code:
Example execution (your 2nd):
I wrote the code, it's 11 lines long and quite simple if you follow this approach.
EDIT
Since you ask for the code, here's the initial version I wrote:
Optionally, if you want/need to avoid
append
andsort
, you could have your ownmerge
procedure like so:and replace the second line of the
union
procedure withHope this helps.
Sounds like a fun problem! It also sounds like a homework problem, so I'm going to try to point you to the answer rather than writing it for you myself. My apologies in advance if I'm misunderstanding the situation.
First, I'm assuming that the individual range sets are ordered, and non-overlapping.
This problem is one that fits well into the recursive mold, with a twist. Specifically, it's an example of "iterating over two complex pieces of data", section 17 of How To Design programs:
http://www.htdp.org/2003-09-26/Book/curriculum-Z-H-22.html#node_chap_17
Even more specifically, you're in case 3, where you actually need to consider all possible combinations.
In fact, it's even a bit worse than that, because in the case where both sets are nonempty, you care about which interval starts lower.
To get started, you're going to want to
Follow the HtDP template, and you should be all right. This is a tricky problem, though.
Assuming r5rs scheme with no srfi, I would suggest making a function with an inner recursive accumulator function that is initially given s1, s2, intvl-acc='(), set-acc='(), last two ones holding temporary interval union and list of accumulated unions.
It should follow the following conditions: (I'll write in pseudocode, dividing into three cases.)
[base case]
[accumulating case]
[otherwise:]
Hope it helped.