HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

"Skewed" average in Lisp

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
skewedaveragelisp

Problem

I set myself the task to calculate the average of a list, but with two conditions:

  • negative numbers are ignored



  • numbers greater than 100 are counted as if they were 100



So the "skewed" average of the list '(1 -3 42 297 14) should be (1 + 42 + 100 + 14) / 4 (157/4).

I wrote 2 functions that do that. Please review and comment.

(defun skewed-average1 (list)
"calculate average by summing and dividing"
(let ((sum 0) (n 0))
(dolist (x list)
(if (>= x 0)
(progn
(if (> x 100) (setf x 100))
(setf sum (+ x sum))
(setf n (+ 1 n)))))
(/ sum n)))

(defun skewed-average2 (list)
"calculate average by building 'fixed' list"
(let (newlist)
(dolist (x list)
(if (>= x 0)
(progn
(if (> x 100) (setf x 100))
(setf newlist (cons x newlist)))))
(/ (apply #'+ newlist) (length newlist))))

(let ((numbers '(1 -3 42 297 14)))
(print (skewed-average1 numbers))
(print (skewed-average2 numbers)))


Also, how should errors be treated? Imagine passing an empty list to the functions; or a list with all negative numbers.

Solution

(defun skewed-average1 (list)
  "calculate average by summing and dividing"
  (let ((sum 0) (n 0))
    (dolist (x list)
      (if (>= x 0)
          (progn
            (if (> x 100) (setf x 100))
            (setf sum (+ x sum))
            (setf n (+ 1 n)))))
    (/ sum n)))


IF ... PROGN is WHEN.

>= 0 is plusp.

(setf sum (+ ... is INCF.

(defun skewed-average1 (list &aux (sum 0) (n 0))
  "calculate average by summing and dividing"
  (dolist (x list (when (plusp n)
                     (/ sum n)))
    (when (plusp x)
      (incf sum (min x 100))
      (incf n))))


Next function:

(setf newlist (cons x newlist)) is (push x newlist).

Don't use APPLY, use REDUCE. APPLY has a list length limit.

Alternative implementations:

(defun skewed-average3 (list)
  (loop for x in list
        when (plusp x)
        sum (min x 100) into sum1
        and count t into count1
        finally (return (when (plusp count1)
                          (/ sum1 count1)))))

(defun skewed-average4 (list)
  (let ((new-list (remove-if #'minusp 
                             (substitute-if 100
                                            (lambda (item) (> item 100))
                                            list))))
    (when new-list
      (/ (reduce #'+ new-list)
         (length new-list)))))

Code Snippets

(defun skewed-average1 (list)
  "calculate average by summing and dividing"
  (let ((sum 0) (n 0))
    (dolist (x list)
      (if (>= x 0)
          (progn
            (if (> x 100) (setf x 100))
            (setf sum (+ x sum))
            (setf n (+ 1 n)))))
    (/ sum n)))
(defun skewed-average1 (list &aux (sum 0) (n 0))
  "calculate average by summing and dividing"
  (dolist (x list (when (plusp n)
                     (/ sum n)))
    (when (plusp x)
      (incf sum (min x 100))
      (incf n))))
(defun skewed-average3 (list)
  (loop for x in list
        when (plusp x)
        sum (min x 100) into sum1
        and count t into count1
        finally (return (when (plusp count1)
                          (/ sum1 count1)))))


(defun skewed-average4 (list)
  (let ((new-list (remove-if #'minusp 
                             (substitute-if 100
                                            (lambda (item) (> item 100))
                                            list))))
    (when new-list
      (/ (reduce #'+ new-list)
         (length new-list)))))

Context

StackExchange Code Review Q#98035, answer score: 4

Revisions (0)

No revisions yet.