Advertisement
Guest User

revenue.lisp

a guest
Jul 13th, 2018
352
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lisp 5.98 KB | None | 0 0
  1. ;;; -*- indent-tabs-mode:nil; -*-
  2.  
  3. ;;; [2018-07-11] Challenge #365 [Intermediate] Sales Commissions
  4. ;;; /r/dailyprogrammer https://redd.it/8xzwl6
  5. ;;; Why do I suck so bad at this? *sigh*
  6.  
  7. (defun p () (load #P"~/reddit/dailyprogrammer/revenue.lisp"))
  8.  
  9. (defconstant commission-rate (/ 6.2 100)) ;percent
  10.  
  11. (defparameter table
  12.   (format nil "~{~a~%~}~%"
  13.           '("Revenue"
  14.             ""
  15.             "            Johnver Vanston Danbree Vansey  Mundyke"
  16.             "Tea             190     140    1926     14      143"
  17.             "Coffee          325      19     293   1491      162"
  18.             "Water           682      14     852     56      659"
  19.             "Milk            829     140     609    120       87"
  20.             ""
  21.             "Expenses"
  22.             ""
  23.             "            Johnver Vanston Danbree Vansey  Mundyke"
  24.             "Tea             120      65     890     54      430"
  25.             "Coffee          300      10      23    802      235"
  26.             "Water            50     299    1290     12      145"
  27.             "Milk             67     254      89    129       76")))
  28.  
  29.  
  30. ;;instead of reading the string-stream within block return the stream itself
  31. (defparameter *table-input* (with-input-from-string ($_ table) $_))
  32.  
  33. (defparameter *salesmen* nil)
  34. (defparameter *inventory* nil)
  35. (defparameter *total-sales* nil)
  36. (defparameter *commissions* nil)
  37.  
  38. (defmacro include (elem collection &key (test eq))
  39.   `(unless (find ,elem ,collection :test ,test)
  40.      (setq ,collection (append ,collection (list ,elem)))))
  41.  
  42. (defun read-section (section in-stream)
  43.   (let ((multiplier (cond
  44.                       ((string= "Revenue" section)   1)
  45.                       ((string= "Expenses" section) -1)
  46.                       (t (error (format nil "read-section: unknown section ~s" section)))))
  47.         (roster nil)
  48.         (item nil)
  49.         (costs nil)
  50.         (record nil))
  51.  
  52.     (setq roster (read-fields))
  53.     ;;if it was an empty section... two+ employees required
  54.     (if (cdr roster)
  55.         (dolist (x roster) (include x *salesmen* :test #'string=))
  56.         (return-from read-section (car roster)))
  57.    
  58.     (setq item (read-fields))
  59.  
  60.     (loop
  61.        while (cdr item)
  62.        do
  63.          (setq costs (mapcar #'parse-integer (cdr item)))
  64.          (setq item (car item))
  65.          (include item *inventory* :test #'string=)
  66.  
  67.          (setq record
  68.                (mapcar #'(lambda (r c) (cons (cons r item) c))
  69.                        roster
  70.                        (mapcar #'(lambda (x) (* x multiplier)) costs)))
  71.  
  72.          (setq *total-sales* (append record *total-sales*))
  73.          (setq item (read-fields in-stream)))
  74.     (car item)))
  75.  
  76.  
  77. ;;#'split is forward reference, can't invoke until defun'd
  78. (defun read-fields (&optional (in-stream *table-input*))
  79.   (let ((line "")
  80.         (fields nil))
  81.     (loop
  82.        while (and line (not fields))
  83.        do
  84.          (setq line (read-line in-stream nil))
  85.          (setq fields (split line)))
  86.     fields))
  87.  
  88. ;;why is this not a standard function...
  89. (defun split (str &key (start 0) (end nil) (fs '(#\Space #\Tab)))
  90.   ;;helper local function
  91.   (labels ((split-point-p (x) (find x fs)))
  92.     ;;skip leading spacers, or return nil if no element found
  93.     (when (setq start (position-if-not #'split-point-p str :start start))
  94.       ;;this might be nil...
  95.       (setq end (position-if #'split-point-p str :start start))
  96.       ;;returns as a list
  97.       (cons (subseq str start end)
  98.             ;;nil if consing last element without extra spaces at end (bug fix)
  99.             (when end (split str :start end :fs fs))))))
  100.  
  101.  
  102. (defun find-all (item sequence
  103.                  &key
  104.                    (from-end nil)
  105.                    (test #'eq)
  106.                    (test-not nil)
  107.                    (start 0)
  108.                    (end nil)
  109.                    (key nil))
  110.   (let ((len (length sequence))
  111.         (pos (position item sequence
  112.                        :from-end from-end
  113.                        :test test
  114.                        :test-not test-not
  115.                        :start start
  116.                        :end end
  117.                        :key key)))
  118.    
  119.     (unless end (setq end len))
  120.    
  121.     (when pos
  122.       (cons (elt sequence pos)
  123.             (or (when (and from-end (< 0 pos))
  124.                   (find-all item sequence
  125.                             :from-end from-end :test test :test-not test-not :start start :end pos :key key))
  126.                 (when (< (+ 2 start) end)
  127.                   (find-all item sequence
  128.                             :from-end from-end :test test :test-not test-not :start (1+ pos) :end end :key key)))))))
  129.  
  130.  
  131.  
  132. ;;;----- MAIN -----
  133.  
  134. (setq f (read-fields))
  135. (setq section (car f))
  136.  
  137. ;;read the table...
  138. (loop
  139.    while section
  140.    do
  141.      (setq section (read-section section *table-input*)))
  142.      ;(format t "salesmen=~a~%inventory=~a~%records=~a~%~%" *salesmen* *inventory* *total-sales*))
  143.  
  144. ;;calculate commissions...
  145. (dolist (s *salesmen*)
  146.   (let ((sum 0)
  147.         (acc 0)
  148.         (record (find-all s *total-sales* :test #'string= :key #'caar)))
  149.    
  150.     (dolist (i *inventory*)
  151.       (let* ((tmp (find-all i record :test #'string= :key #'cdar))
  152.              (acc nil))
  153.         (setq acc (reduce #'(lambda (a b) (+ a (cdr b))) tmp :initial-value 0))
  154.         (when (> acc 0) (incf sum acc))))
  155.  
  156.     (setq *commissions* (cons (cons s (* sum commission-rate)) *commissions*))))
  157.  
  158. ;;prepare formatting...
  159.  
  160. (setq *commissions* (reverse *commissions*)) ;correct order
  161.  
  162. (defconstant *fmt*
  163.   (with-output-to-string (str)
  164.     (format str "~~{")
  165.     (let ((w 0)
  166.           (d 2))
  167.       (dolist (x *commissions*)
  168.         (setq w (1+ (length (car x))))
  169.         (format str "~~~d,~dF" w d)))
  170.     (format str "~~}~~%")))
  171.  
  172. ;;names row
  173. (format t (format nil "~?" "~~~dT " (list (length "Commissions"))))
  174. (format t
  175.         "~{~a~^ ~}~%"
  176.         (mapcar #'car *commissions*))
  177.  
  178. ;;commission row
  179. (format t "~a" "Commissions")
  180. (format t
  181.         *fmt*
  182.         (mapcar #'cdr *commissions*))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement