0

I recently started learning ocaml and am having trouble figuring out a problem.

Goal:

Given a list of integers and a function, sort the integers into equivalence classes based on the given function as lists inside another list. The order of the equivalence lists must be the same as the order given in the original list.

Update: I have decided to try and get it working without library function.

This is my code so far. It compiles but does not run. Any help with syntax would be appreciated.

let buckets f lst =
  let rec helpfunction f lst newlist = 
     match lst with
      | [] ->newlist
      | h::t -> match newlist with
             | [] -> helpfunction f lst [h]@newlist
             | [a]::_ -> if f h a 
                  then helpfunction f t [a::h]@newlist
                  else helpfunction f t ([a]::[h]@mylist

IMPORTANT: this is a homework question, so I am not looking for the code pasted for me. Im trying to logic through it on my home with a bit of help with the overall thought-process and syntax. Once I get it working I would to work on making it more efficient

  • You haven't asked a specific question. Are you looking for help getting your code to compile (I see a few errors)? Or do you want advice on whether your proposed method is workable? – Jeffrey Scofield Feb 19 '20 at 05:57
  • A little bit of A, a little bit of B. Step 1 for me is to get my code working, then I will focus on efficiency – LostinRecursion Feb 20 '20 at 00:36

2 Answers2

0

Well, let's try to solve the task without doing it actually :)

Roughly speaking, the algorithm looks as follows:

  1. Take the integer x from the source list

  2. If the target list of lists (each of them contains integers of the same equivalence class) contains the list of the appropriate equivalence class add x to this list

  3. Otherwise, create a new equivalence class list containing a single value ([x]) and add it to the target list

  4. Repeat 1-3 for the other integers in the target list.

(1), (3) and (4) seems to be quite trivial to implement.

The challenges are how to implement (2) and how to do it efficiently :)

"Target list contains the list that..." can be rephrased as "there exists a list lst in a target list such as..." - this gives us a strong hint that List.exists can be somehow used here. What to test is also more or less clear: each member of the same equivalence class list are equivalent by definition, so we can take just the head and compare it with x using p.

The question is how to add an integer to a list within another list. Lists are immutable, so we cannot just drop it there... But we can take the target list and transform, or fold it into another one. I bet you got the hint: List.fold_left is another piece of the puzzle.

So, summarizing all this we can come to the following "more precise" algorithm

  1. Start from the empty target list res

  2. Take an integer x from the source list l

  3. Use List.exists to check if there is a list lst in res such as for its head h p x h = true

  4. If yes, use List.fold_left to transform the current res into a new one, where lst replaced with x::lst and all other equivalence class lists are copied as is

  5. If no, introduce a new equivalence class (add [x] to res)

  6. Repeat 2-5 for the next integer in the source list until it is empty.

The implementation that (almost) literally follows this algorithm is relatively simple. But it will be not very efficient because exists and fold will iterate over the same list twice doing literally the same check. With some tweaks, it should be possible to make it in a single run, but this non-efficient one should be good enough as the starting point...

Konstantin Strukov
  • 2,899
  • 1
  • 10
  • 14
  • 1
    That loops over the list of classes twice. It's better to do the exists and fold in one go. Check if the list of classes is empty (create a new class for the current item). Otherwise check if the head class is equivalent to the current item (add it and return new_class :: tail). Otherwise recurse on the tail. – Goswin von Brederlow Feb 19 '20 at 13:55
  • My idea was to start with the "dumb" and straightforward solution first that is simpler to explain step by step in plain words without the code. But I agree with you... – Konstantin Strukov Feb 19 '20 at 14:15
  • Thank you for the response. My the source list is just a basic list, not a list of lists. Do you think it would be best to immediatly start by sorting the list into a list of list, where each internal list holds just 1 item? – LostinRecursion Feb 20 '20 at 00:16
  • @KonstantinStrukov I think I have the hang of the logic now. Does my syntax in my new code above look correct? – LostinRecursion Feb 20 '20 at 00:44
  • "My the source list is just a basic list, not a list of lists" @LostinRecursion sure it is. `int list list` is gonna be the type of the target list, not the source one. Sorry if I was unclear - I'm new to OCaml too and it's a bit hard to me to explain the solution without the code :) – Konstantin Strukov Feb 20 '20 at 11:11
0

What you are trying is to take each element in the input and then insert it somewhere in the result list of lists. Let me propose a different approach.

You start with an 'a list of input and an empty 'a list list of the result you construct.

If the input is empty your result is complete and you return it.

Otherwise split the input into head and tail (the pattern matching does that already). Next partition the input list into items that are equivalent to the head and those that are not. Recurse with the second list as new input and (first list :: result) as new result.

Goswin von Brederlow
  • 11,875
  • 2
  • 24
  • 42
  • Note: you can use `let helper results = function [] -> results | (head::_) as input -> ....` – Goswin von Brederlow Feb 19 '20 at 14:05
  • Thank you for the response. I am trying to implement and make changes based on your recommendations. The part that I am stuck a bit on is partiioning the list. Do you think nested match statements is the best way to go about it?\ – LostinRecursion Feb 20 '20 at 00:17
  • I think I have a hang of the logic now, though I am getting hun up on the syntax for the higher order functions. I added my new code up above. – LostinRecursion Feb 20 '20 at 00:57
  • Normally I would say List.partition. But you can implement this yourself as helper function for the learning experience. – Goswin von Brederlow Feb 26 '20 at 10:19