|20-CS-4003-001||Organization of Programming Languages||Fall 2017|
Support declarative programming style
(define call/cc call-with-current-continuation) (define amb-fail (lambda () (error 'no-solution))) (define-syntax amb (syntax-rules () ((amb x ...) (let ((prev-level amb-fail)) (call/cc (lambda (sk) (call/cc (lambda (fk) (set! amb-fail (lambda () (fk 'fail))) (sk x))) ... (prev-level))))))) (define assert (lambda (p) (if (not p) (amb)))) ;; --------------------------------------------- ;; code above this line is applicable to ;; many other problems (define number-between (lambda (lo hi) (if (> lo hi) (error "all possible numbers exhausted") (amb lo (number-between (+ lo 1) hi))))) (define gen-multiple (lambda (num lo hi) (let ((i (number-between lo hi))) (assert (= (modulo i num) 0)) i)))
Procedure gen-multiple finds a multiple of num that is
at least lo and no greater than hi, if one exists, or
it returns no-solution otherwise. An instance of the
problem is solved by exhaustive search through all possible numbers.
The code supporting the search is above the dotted line. This code
may be used for many other problems and its operation is described
below. For now, note that the supporting code supplies two functions
that are used to solve the problem: amb and assert.
Procedure number-between generates all numbers consecutively from lo to hi via repeated calls to itself, each time increasing lo. Due to the call/cc mechanism of amb, it returns on each recursion one of the numbers to i of procedure gen-multiple.
In procedure gen-multiple the line
(assert (= (modulo i num) 0))acts as a gate: control does not pass to the next line unless the condition is satisfied. If the condition is not satisfied, control is passed back to number-between which continues from where it left off to generate the next number for i. This continues until either control passes to the line i))) in gen-multiple, in which case an answer has been found and displayed, or until number-between runs out of numbers to try. In the latter case the
no-solutionerror message is displayed. Observe that although a search takes place, control flow in gen-multiple appears to be straight down. In future examples there will be many assert statements. If the last line is reached in the "main" procedure it means all the constraints that the assert statements signify have been satisfied.
The co-routining between the assert statement of gen-multiple and the amb statement of number-between is made possible by manipulating continuations in the macro definition of amb at the top of the page. That code explores a search space from the top down. Typically, each level of search corresponds to an amb statement which lays out all its possible values in a list. However, in this case the amb in procedure number-between is responsible for producing values but contains a recursive call to number-between - hence the number of levels is the number of recursive calls which is hi-lo+1. Observe it is possible that not all levels need be explored to satisfy the assert. The breadth of a level is proportional to the number of possible values that can be given to the variable that is bound to the corresponding amb: in this case the number of values is hi-lo+1.
There are two continuation types in the macro expansion for amb: these are identified as sk and fk. For information about these see this slide.