3

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.?

Óscar López
  • 232,561
  • 37
  • 312
  • 386
gibarian
  • 133
  • 6
  • Which variant of Scheme are you using? Is it R6RS-compatible, R5RS-compatible, Racket, or something else? – Alex Knauth Nov 16 '18 at 22:15
  • I am running "mzscheme" in a terminal as suggested here: https://stackoverflow.com/questions/1175977/running-scheme-from-the-command-line – gibarian Nov 16 '18 at 22:18
  • Wait, I just realized that your sample output is _not_ randomized. What do you want to randomize, the contents (as I did in my answer) or the length of the sublists? – Óscar López Nov 16 '18 at 22:53
  • 1
    The content, as you did. Thank you. – gibarian Nov 16 '18 at 22:57
  • Ok then, it's fine :) . You're welcome! – Óscar López Nov 16 '18 at 22:59
  • I see what you attempted to do. It'll be hard to get it right, how would you make sure that the right elements were removed from the list after randomly selecting them? what if there were duplicates? and `for/list` doesn't seem appropriate for it, an explicit recursion would be simpler to use. – Óscar López Nov 17 '18 at 11:20
  • If there were no duplicates, the algorithm would be similar as my `group`: cons the removed elements as a new sublist, and keep recursing over the rest of the elements, handling the case when the list has less than `n` elements left. Also, why would you randomize the list more than once? it's a one-time operation. – Óscar López Nov 17 '18 at 11:22

1 Answers1

5

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))
Óscar López
  • 232,561
  • 37
  • 312
  • 386
  • Thank you. My approach was different and entailed a sort of recursion I couldn't solve: 1. take a random group of elements 2. cut the list removing this first group and 3. recurse over this process. I had 1. and 2. working correctly but failed on 3. – gibarian Nov 16 '18 at 22:59
  • Any suggestion to do it recursively with my idea? – gibarian Nov 16 '18 at 23:20
  • Well, you should edit your post and add the code as part of the question. But it's basically the same as my approach: randomize the list first, then take chunks of the right size. The trick is to split the problem in smaller parts, and in each one focus on a single problem. – Óscar López Nov 16 '18 at 23:23
  • Ok, I will try on my own and see. – gibarian Nov 16 '18 at 23:25