Search code examples
functional-programmingschemelisp

Taking groups of elements randomly in scheme


How do I implement a program in Scheme taking the elements of a given list and returning a new list where the elements are random gatherings of the previous list? I would like it to work for any length. For example:

Input: '(a e i o u), output: '((a e) (i o) (u)) for length 2.

My attempts (making use of for/list) are being clumsy and based on recursion. I have divided the tasks as suggested by Óscar:

  1. Select n elements randomly from a list l:

    (define (pick-n-random l n)
      (take (shuffle l) n))
    
  2. Remove a list l2 from a list l1:

    (define (cut l1 l2)
      (cond ((null? l1)
             '())
            ((not (member (car l1) l2))
             (cons (car l1) (cut (cdr l1) l2)))
            (else
             (cut (cdr l1) l2))))
    

Then, and that is my problem: how do I recurse over this process to get the intended program? Should I use for/list to paste all sublists got by this process 1. and 2.?


Solution

  • It's easier if we split the problem into chunks. First, let's write a couple of procedures that will allow us to take or drop n elements from a list, with appropriate results if there are not enough elements left in the list (if not for this, we could have used the built-in take and drop):

    (define (take-up-to lst n)
      (if (or (<= n 0) (null? lst))
          '()
          (cons (car lst) (take-up-to (cdr lst) (sub1 n)))))
    
    (define (drop-up-to lst n)
      (if (or (<= n 0) (null? lst))
          lst
          (drop-up-to (cdr lst) (sub1 n))))
    

    With the above two procedures in place, it's easy to create another procedure to group the elements in a list into n-sized sublists:

    (define (group lst n)
      (if (null? lst)
          '()
          (cons (take-up-to lst n)
                (group (drop-up-to lst n) n))))
    

    Finally, we combine our grouping procedure with shuffle, which randomizes the contents of the list:

    (define (random-groups lst n)
      (group (shuffle lst) n))
    

    It works as expected:

    (random-groups '(a e i o u) 2)
    => '((e a) (u i) (o))