-1

In Scheme (extempore version) I sometimes use expressions similar to the following to choose alternative defined callback nodes.

(random (cons 0.1 'node1) (cons 0.2 'node2) (cons 0.7 'node3))

This will select one of the quoted symbols => node3 or node2 or node1.

In the example above, the chances of choosing node3 is greatest. https://gist.github.com/georgejwright/c480886dc85bb6b8e8d104675aa8da41#file-callback_nodes-xtm-L98

Running a test using this code:

(let pick-a-node ((i 100))
  (if (= i 0)
      (println "Done")
      (begin
        (print (random (cons 0.1 'node1) (cons 0.2 'node2) (cons 0.7 'node3)) " ")
        (pick-a-node (- i 1)))))

Evaluating 100 times I got: => 12 occurrences of node1, 22 of node2 and 66 of node3.

I understand 'random' will choose one of the 3 bracketed choices. I see that somehow the 'node3 bracket has an 70% chance of being selected. But there's stuff I don't understand. Evaluating just one of these brackets on its own doesn't pick the cdr:

(cons 0.1 'node1) => (cons 0.100000 . 'node1)

nor does printing:

(print (cons 0.1 'node1)) =>  (cons 0.100000 quote node1)

Also notice that if the numbers such as 0.1, 0.2 and 0.7 add up to less than 1 you get an error! And if the numbers include some greater than 1.0 - such as 0.1, 4.1 and 5.7 - the first of the greater-than-ones is chosen and others ignored.

This all appears quite wierd to me.

Why does random return just the quoted element of one of them? Is there something about "random" that will do this? Are there other key words that will do the same?

I'm hoping someone is able to explain the how and why of it.

1
  • I've rolled back your edit. On Stack Overflow, we don't put responses to Answers in the Question. Comments and/or voting are the appropriate way to acknowledge a response. Commented Jun 15 at 3:32

1 Answer 1

1

There's no standard Scheme random function, but I think I tracked down the one you're looking at. Extempore appears to be some audio manipulation/creation software with a Scheme interface. Its documentation seems pretty lacking, so I dug around in the source code to find the function in question.

From this source file:

(define (random . args)
  (cond ((< (length args) 1)
         (random-real))
        ((list? (car args))
         (list-ref (car args) (random (length (car args)))))
        ((pair? (car args))
         (apply weighted-selection args))
        (else (let ((lower (if (> (length args) 1) (real->integer (car args)) 0))
                    (upper (if (> (length args) 1) (cadr args) (car args))))
                (+ lower (random-int (- upper lower)))))))

This defines a function named random that takes any number of arguments.

  • If called with no arguments, it returns a floating point value (Probably in the range [0,1) as that's how such things typically work).
  • If called with at least one argument where the first argument is a proper list, it randomly picks one of those elements to return. Other arguments are ignored.
  • If called with at least one argument where the first argument is a pair (cons cell), it calls weighted-selection with the same arguments. This is what you're doing.
  • Otherwise, it assumes the first two (Or one if only one argument) arguments are numbers, and returns a random integer in a specified range.

The weighted-selection function:

(define (weighted-selection . args)
   (let ((rand (random)))
      (let loop ((lst args)
     (count 0))
   (if (< rand (+ count (caar lst)))
       (cdar lst)
       (loop (cdr lst) (+ count (caar lst)))))))

It takes zero or more arguments, each of which is a pair whose car is a probability expressed as a real number (Ideally between 0.0 and 1.0) and whose cdr is a potential value to return. It picks which one by comparing against a random number in that [0,1) range, and if the probability plus the value of count is less than that random number, returns the corresponding value. Otherwise, it adds the current probability to count and repeats. Thus in

(random (cons 0.1 'node1) (cons 0.2 'node2) (cons 0.7 'node3))
;;; I'd write it as
;;; (random '(0.1 . node1) '(0.2 . node2) '(0.7 . node3))

it'll return the symbol node1 roughly 10% of the time, node2 roughly 20% of the time, and node3 roughly 70% of the time.

The function is prone to failure - because it doesn't check to see if the list of options is empty, if the total probabilities doesn't add up to at least 1.0, you'll get a cryptic error if it gets to the end of the arguments without picking one yet, which you've run into. No issue if you use it as expected, but give it invalid input and it blows up.

If you have a probability greater than 1.0, and that gets checked (Because none of the earlier options were selected), it'll always be picked and anything after it ignored, which you also ran into.

Just (cons 0.1 'node1) creates a new pair. It doesn't select anything. You'd have to use car or cdr with it to do that.

Sign up to request clarification or add additional context in comments.

3 Comments

There's also issues with the whole design of random - you can't pass it a set of probabilities where the first one has a cdr that's a proper list, as that'll get snagged by the earlier check for a list. Better to split its behavior up into a couple of different functions.
Thank you @Shawn. Well tracked down! I had not found that version of random definition. Your explanation is clear. As you say the function is prone to failure. Extempore has been designed for live-coding musical performance. I find that having code in the form: (random (cons 0.1 'node1) (cons 0.2 'node2) (cons 0.7 'node3)) allows for quick on-the-fly adjustment of probabilities of selection simply by changing the numbers - careful of course that they add to 1.0. An alternative: (random '('node1 'node2 'node2 'node3 'node3 'node3 'node3 'node3 'node3 'node3 )) is more cumbersome.
Many functions in Extempore are not well documented but there is are excellent guides on the website. extemporelang.github.io/docs/overview/quickstart

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.