It seems that when I program in Lisp my brain goes on auto pilot and I end up solving the problem somehow. I don't even think I just do and it works out.

That said, this is some horrible Lisp code that was hacked together in about 20 mins, just to see if I could do it.

The C++ program that this is based off was about 150 lines long, so doing it in about 59 lines of terrible code is neat.

The date is 50 lines and is structured like 2 dates and 48 doubles 1 for each hour:


(defun read-nth-line (file n &aux (line-number 0))
  "Read the nth line from a text file. The first line has the number 1"
  (assert (> n 0) (n))
  (with-open-file (stream file)
    (loop for line = (read-line stream nil nil)
          if (and (null line) (< line-number n))
            do (error "file ~a is too short, just ~a, not ~a lines long"
                      file line-number n)
          do (incf line-number)
          if (and line (= line-number n))
       do (return line))))

(defun arithmetic-average (samples)
  (/ (reduce #'+ samples)
     (length samples)))

(defun get-file (filename)
  (with-open-file (stream filename)
    (loop for line = (read-line stream nil)
          while line
       collect line)))

   ;; compute sDev
(defun standard-dev (colc)
  (let ((mean (arithmetic-average colc)))
    (sqrt (* (/ 1.0d0 (length colc))
         (reduce #'+ colc
             :key (lambda (x)
                (expt (- x mean) 2)))))))

(defun split-in-half (sequence)
  (let ((mid (ceiling (length sequence) 2)))
    (list (subseq sequence 0 mid)
            (subseq sequence mid nil))))

(defun parse-string-to-float (line)
  (with-input-from-string (s line)
      :for num := (read s nil nil)
      :while num
      :collect num)))

(defun extract-float (line)
 (first (parse-string-to-float line)))

(defun process-file ()
  "print mean and standard deviation to terminal"
  (let* ((l1 (get-file "~/ClionProjects/project5withTemplates/twoday.txt"))
     (date1 (first l1))
     (date2 (second l1))
     (day1temps (mapcar #'extract-float (first (split-in-half (rest (rest l1))))))
     (day2temps (mapcar #'extract-float (second (split-in-half (rest (rest l1))))))
     (s-dev-day-1 (standard-dev day1temps))
     (s-dev-day-2 (standard-dev day2temps)))
    (print "date:")(print date1) (print "standard deviation:") (print s-dev-day-1)
    (print "date:")(print date2) (print "standard deviation:") (print s-dev-day-2)) nil)

2 Answers 2


You are doing fine, other than a few simple nitpicks.

You are not using read-nth-line (which is a crazy thing to do anyway), please drop it.

You should fix your indentation, it would make your code much easier to read.

You probably want to divide by N-1, not N, in standard-dev and you probably want to simplify the code there by dividing by N-1 instead of multiplying by the reciprocal; also expt is relatively expensive, so, if you were not i/o bound anyway, you might want to replace it with multiplication.

Function split-in-half should probably return multiple values instead of a list.

Global variable l1 should be local to process-file.

Function parse-string-to-float should probably be called parse-string-to-floats (it read the whole list).

  • \$\begingroup\$ OK! Thanks for the input. The indentation is a problem. I don't actually know what good indentation looks like. I just let slime indent for me by pressing tab. \$\endgroup\$
    – Cody
    Jul 21, 2015 at 12:27
  • \$\begingroup\$ please don't tell me that Emacs indented standard-dev that way. \$\endgroup\$
    – sds
    Jul 21, 2015 at 12:30
  • \$\begingroup\$ Is standard-dev indentation better now? \$\endgroup\$
    – Cody
    Jul 21, 2015 at 12:50
  • \$\begingroup\$ Better, but still no cigar (and many other functions are mis-indented too). Your problem is that you are using TABs. Set indent-tabs-mode to nil and use untabify on your code. \$\endgroup\$
    – sds
    Jul 21, 2015 at 12:51
  • \$\begingroup\$ Ok then I guess going to each line and pressing tab is a bad idea. Is there a better way to auto indent a region with slime? Or better yet indent an entire file. \$\endgroup\$
    – Cody
    Jul 21, 2015 at 12:53
;;; File Processing

(defun get-file (filename)
  (with-open-file (stream filename)
    (loop for line = (read-line stream nil)
          while line
          collect line)))

(defun parse-file (filename)
  (let* ((lines (get-file filename))
         (date1 (car lines))
         (date2 (cadr lines))
         (hourly-data (cddr lines))
         (mid (/ (length hourly-data) 
         (day1-data (subseq hourly-data 0 mid))
         (day2-data (subseq hourly-data mid)))
    (values date1
            (mapcar #'read-from-string day1-data)
            (mapcar #'read-from-string day2-data))))

;;; Compute deviations

(defun arithmetic-average (samples l)
  (/ (reduce #'+ samples)

(defun standard-dev (samples)
  (let* ((l (length samples))
         (mean (arithmetic-average samples l)))
    (sqrt (* (/ 1.0d0 l)
      (reduce #'+ samples
              :key (lambda (x)
                     (expt (- x mean) 2)))))))

;;; Process deviations in file

(defun process-std-deviations (filename)
  (multiple-value-bind (date1 date2 day1-data day2-data)
       (parse-file filename)
    (let ((s1 (standard-dev day1-data))
          (s2 (standard-dev day2-data)))
      (format t "Date: ~A Deviation: ~,2f ~%Date: ~A Deviation: ~,2f"
              date1 s1 date2 s2))))

The following are the changes compared to your solution:

  • A function to parse the input file into date1, date2 and values for days 1, 2
  • Using read-from-string to convert from string to float
  • The deviations computation does not recompute the samples length
  • Using the format function to print the results

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.