Test Scores
May 24, 2016
We begin by creating test input:
(define scores '(("Alice" 84) ("Bob" 92) ("Alice" 93)
("Bob" 47) ("Charlie" 73) ("Dennis" 59) ("Eve" 91)
("Alice" 78) ("Charlie" 85) ("Eve" 68) ("Alice" 100)
("Charlie" 84) ("Dennis" 92) ("Eve" 85) ("Dennis" 83)
("Alice" 92) ("Bob" 68) ("Bob" 79) ("Dennis" 79)
("Alice" 84) ("Bob" 89) ("Charlie" 69) ("Charlie" 67)
("Charlie" 92) ("Dennis" 73) ("Eve" 79) ("Eve" 84)))
Processing occurs in two steps: First, we accumulate lists of scores for each student in an R6RS hash table. Second, we sort the scores, then take the average of the top five:
(define (score scores)
(let ((ht (make-hashtable string-hash string=?)))
(do ((scores scores (cdr scores)))
((null? scores))
(hashtable-set! ht (caar scores)
(cons (cadar scores)
(hashtable-ref ht (caar scores) (list)))))
(call-with-values
(lambda () (hashtable-entries ht))
(lambda (key value)
(map (lambda (key value)
(list key (/ (sum (take 5 (sort > value))) 5.)))
(vector->list key) (vector->list value))))))
Here’s the output for our sample data:
> (score scores)
(("Dennis" 77.2)
("Eve" 81.4)
("Alice" 90.6)
("Charlie" 80.6)
("Bob" 75.0))
We used take and sum from the Standard Prelude. You can run the program at http://ideone.com/ux59n2, where you will see that Guile Scheme uses a different set of hashtable functions than standard R6RS, and takes the arguments to sort in a different order.
In SQL for DB2:
with students (id,name) as (values (1, 'A'),(2, 'B'),(3, 'C')), scores (id,score,cnt) as (select id, int(rand() * 100), 1 from students union all select id, int(rand() * 100), cnt + 1 from scores where cnt < 10), list (id,cnt,arr) as (select id,cnt,cast(char(score) as varchar(50)) from scores where cnt = 1 union all select s.id,s.cnt,trim(l.arr) concat ',' concat char(s.score) from list l inner join scores s on l.id = s.id and l.cnt + 1 = s.cnt), hits (id,score,rn) as (select id,score,row_number() over (partition by id order by score desc) from scores), average (id,val) as (select id, avg(score) from hits where rn <= 5 group by id) select a.id as student_id, a.val as average_score, l.arr as scores_list from average a inner join list l on a.id = l.id and l.cnt = 10 ==> STUDENT_ID AVERAGE_SCORE SCORES_LIST 1 55 49,49,18,35,15,70,45,7,63,33 2 80 79,59,30,4,79,90,21,61,61,92 3 68 54,42,30,65,59,39,82,18,84,39