First, the problem description (copied from the website):
Day 2: I Was Told There Would Be No Math
The elves are running low on wrapping paper, and so they need to submit an order for more. They have a list of the dimensions (length l, width w, and height h) of each present, and only want to order exactly as much as they need.
Fortunately, every present is a box (a perfect right rectangular prism), which makes calculating the required wrapping paper for each gift a little easier: find the surface area of the box, which is 2 x l x w + 2 x w x h + 2 x h x l. The elves also need a little extra paper for each present: the area of the smallest side.
- A present with dimensions 2x3x4 requires 2 x 6 + 2 x 12 + 2 x 8 = 52 square feet of wrapping paper plus 6 square feet of slack, for a total of 58 square feet.
- A present with dimensions 1x1x10 requires 2 x 1 + 2 x 10 + 2 x 10 = 42 square feet of wrapping paper plus 1 square foot of slack, for a total of 43 square feet.
All numbers in the elves’ list are in feet. How many total square feet of wrapping paper should they order?
Once again, we’ll be working in Scheme.
For this problem, I decided to create a “box” data type. In addition to the automatically generated accessors (thanks SRFI-9!), I wrote several procedures to perform calculations on boxes, namely:
SURFACE-AREA: Calculate the box’s surface area.
SMALLEST-SIDE: Determine which of the box’s sides has the smallest surface area (the extra material makes it easier to wrap).
WRAPPING-PAPER is just a “wrapper” (pun intended) around the first two.
SUM-BOXES are all about parsing the input file contents and shuffling them into the box data type that we use to do the actual calculation. The only part that required a bit of thought was the line with
LINE->BOX. In Perl I’d use
my @params = split /x/, $line without even thinking, but I was less familiar with Scheme’s facility for solving this problem, so it took a few minutes to puzzle out the right part of Scheme’s “API”. (
STRING-TOKENIZE was helpfully provided by SRFI-13.)
Abstract data types FTW! I’ll be using them more as the month’s challenges progress.
;; ,open srfi-9 srfi-13 sort (define-record-type box (make-box l w h) box? (l box-length set-box-length!) (w box-width set-box-width!) (h box-height set-box-height!)) (define (surface-area box) ;; Box -> Int (let ((l (box-length box)) (w (box-width box)) (h (box-height box))) (+ (* 2 l w) (* 2 w h) (* 2 h l)))) (define (smallest-side box) ;; Box -> Int (define (smallest-two xs) ;; List -> List (let ((sorted (sort-list xs <))) (list (first sorted) (second sorted)))) (let ((l (box-length box)) (w (box-width box)) (h (box-height box))) (apply * (smallest-two (list l w h))))) (define (wrapping-paper box) ;; Box -> Int (let ((minimum (surface-area box)) (extra (smallest-side box))) (+ minimum extra))) (define (line->box line) ;; String -> Box (define (line->lon s) ;; String -> List<Number> (let ((xs (string-tokenize s (char-set-complement (char-set #\x))))) (map string->number xs))) (let* ((dims (line->lon line))) (let ((l (first dims)) (w (second dims)) (h (third dims))) (make-box l w h)))) (define (read-boxes file) ;; Pathname -> List<Box> (with-input-from-file file (lambda () (let loop ((line (read-line)) (ys '())) (if (eof-object? line) ys (loop (read-line) (cons (line->box line) ys))))))) (define (sum-boxes boxes) ;; List<Box> -> Int (let ((xs (map wrapping-paper boxes))) (apply + xs))) ;; eof