Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ;;; -*- indent-tabs-mode:nil; -*-
- ;;; [2018-07-11] Challenge #365 [Intermediate] Sales Commissions
- ;;; /r/dailyprogrammer https://redd.it/8xzwl6
- ;;; Why do I suck so bad at this? *sigh*
- (defun p () (load #P"~/reddit/dailyprogrammer/revenue.lisp"))
- (defconstant commission-rate (/ 6.2 100)) ;percent
- (defparameter table
- (format nil "~{~a~%~}~%"
- '("Revenue"
- ""
- " Johnver Vanston Danbree Vansey Mundyke"
- "Tea 190 140 1926 14 143"
- "Coffee 325 19 293 1491 162"
- "Water 682 14 852 56 659"
- "Milk 829 140 609 120 87"
- ""
- "Expenses"
- ""
- " Johnver Vanston Danbree Vansey Mundyke"
- "Tea 120 65 890 54 430"
- "Coffee 300 10 23 802 235"
- "Water 50 299 1290 12 145"
- "Milk 67 254 89 129 76")))
- ;;instead of reading the string-stream within block return the stream itself
- (defparameter *table-input* (with-input-from-string ($_ table) $_))
- (defparameter *salesmen* nil)
- (defparameter *inventory* nil)
- (defparameter *total-sales* nil)
- (defparameter *commissions* nil)
- (defmacro include (elem collection &key (test eq))
- `(unless (find ,elem ,collection :test ,test)
- (setq ,collection (append ,collection (list ,elem)))))
- (defun read-section (section in-stream)
- (let ((multiplier (cond
- ((string= "Revenue" section) 1)
- ((string= "Expenses" section) -1)
- (t (error (format nil "read-section: unknown section ~s" section)))))
- (roster nil)
- (item nil)
- (costs nil)
- (record nil))
- (setq roster (read-fields))
- ;;if it was an empty section... two+ employees required
- (if (cdr roster)
- (dolist (x roster) (include x *salesmen* :test #'string=))
- (return-from read-section (car roster)))
- (setq item (read-fields))
- (loop
- while (cdr item)
- do
- (setq costs (mapcar #'parse-integer (cdr item)))
- (setq item (car item))
- (include item *inventory* :test #'string=)
- (setq record
- (mapcar #'(lambda (r c) (cons (cons r item) c))
- roster
- (mapcar #'(lambda (x) (* x multiplier)) costs)))
- (setq *total-sales* (append record *total-sales*))
- (setq item (read-fields in-stream)))
- (car item)))
- ;;#'split is forward reference, can't invoke until defun'd
- (defun read-fields (&optional (in-stream *table-input*))
- (let ((line "")
- (fields nil))
- (loop
- while (and line (not fields))
- do
- (setq line (read-line in-stream nil))
- (setq fields (split line)))
- fields))
- ;;why is this not a standard function...
- (defun split (str &key (start 0) (end nil) (fs '(#\Space #\Tab)))
- ;;helper local function
- (labels ((split-point-p (x) (find x fs)))
- ;;skip leading spacers, or return nil if no element found
- (when (setq start (position-if-not #'split-point-p str :start start))
- ;;this might be nil...
- (setq end (position-if #'split-point-p str :start start))
- ;;returns as a list
- (cons (subseq str start end)
- ;;nil if consing last element without extra spaces at end (bug fix)
- (when end (split str :start end :fs fs))))))
- (defun find-all (item sequence
- &key
- (from-end nil)
- (test #'eq)
- (test-not nil)
- (start 0)
- (end nil)
- (key nil))
- (let ((len (length sequence))
- (pos (position item sequence
- :from-end from-end
- :test test
- :test-not test-not
- :start start
- :end end
- :key key)))
- (unless end (setq end len))
- (when pos
- (cons (elt sequence pos)
- (or (when (and from-end (< 0 pos))
- (find-all item sequence
- :from-end from-end :test test :test-not test-not :start start :end pos :key key))
- (when (< (+ 2 start) end)
- (find-all item sequence
- :from-end from-end :test test :test-not test-not :start (1+ pos) :end end :key key)))))))
- ;;;----- MAIN -----
- (setq f (read-fields))
- (setq section (car f))
- ;;read the table...
- (loop
- while section
- do
- (setq section (read-section section *table-input*)))
- ;(format t "salesmen=~a~%inventory=~a~%records=~a~%~%" *salesmen* *inventory* *total-sales*))
- ;;calculate commissions...
- (dolist (s *salesmen*)
- (let ((sum 0)
- (acc 0)
- (record (find-all s *total-sales* :test #'string= :key #'caar)))
- (dolist (i *inventory*)
- (let* ((tmp (find-all i record :test #'string= :key #'cdar))
- (acc nil))
- (setq acc (reduce #'(lambda (a b) (+ a (cdr b))) tmp :initial-value 0))
- (when (> acc 0) (incf sum acc))))
- (setq *commissions* (cons (cons s (* sum commission-rate)) *commissions*))))
- ;;prepare formatting...
- (setq *commissions* (reverse *commissions*)) ;correct order
- (defconstant *fmt*
- (with-output-to-string (str)
- (format str "~~{")
- (let ((w 0)
- (d 2))
- (dolist (x *commissions*)
- (setq w (1+ (length (car x))))
- (format str "~~~d,~dF" w d)))
- (format str "~~}~~%")))
- ;;names row
- (format t (format nil "~?" "~~~dT " (list (length "Commissions"))))
- (format t
- "~{~a~^ ~}~%"
- (mapcar #'car *commissions*))
- ;;commission row
- (format t "~a" "Commissions")
- (format t
- *fmt*
- (mapcar #'cdr *commissions*))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement