3
\$\begingroup\$

I have recently started learning lisp by reading ANSI Common Lisp, and I thought advent of code would be good for practice. Any feedback on my code for Day 1 would be appreciated.

(require :uiop)
(defparameter *input-file* "01-input")
(defparameter *input-lines* (uiop:read-file-lines *input-file*))
(defun solve ()
 (multiple-value-bind (xs ys) (parse-input-lines *input-lines*)
 (format t "Part 1: ~a~%" (part-1 xs ys))
 (format t "Part 2: ~a~%" (part-2 xs ys))))
(defun parse-input-lines (lines)
 (let (xs ys)
 (loop
 for line in lines
 do (with-input-from-string (s line)
 (push (read s) xs)
 (push (read s) ys)))
 (values (sort xs #'<) (sort ys #'<))))
(defun part-1 (xs ys)
 (reduce #'+ (mapcar (lambda (x y) (abs (- x y))) xs ys)))
(defun part-2 (xs ys)
 (let ((counts (make-hash-table)))
 (loop for y in ys
 do (setf (gethash y counts) (1+ (or (gethash y counts) 0))))
 (loop for x in xs
 sum (* x (or (gethash x counts) 0)) into similarity-score
 finally (return similarity-score))))
toolic
14.5k5 gold badges29 silver badges203 bronze badges
asked Dec 1, 2024 at 10:48
\$\endgroup\$
2
  • 2
    \$\begingroup\$ Welcome to Code Review! The programming-challenge has been added since the code is a solution to a challenge on Advent of Code. Per the excerpt of that tag: "Always include a sufficient description of the problem to be solved - while a link to the challenge is welcome, the review request needs to be complete when the challenge site is unavailable". Can you please edit the post to have sufficient details? \$\endgroup\$ Commented Dec 4, 2024 at 18:27
  • \$\begingroup\$ Assigning a global to *input-file*, with earmuffs, looks great. But the *input-lines* global feels unnatural. Prefer to just leave it as an anonymous expression within that multiple-value-bind. \$\endgroup\$ Commented Dec 8, 2024 at 17:20

1 Answer 1

3
\$\begingroup\$

Background: Input data is a series of lines, where each line has two integers separated by multiple spaces.

(loop
 for line in lines
 do (with-input-from-string (s line)
 (push (read s) xs)
 (push (read s) ys)))

Not an issue if you're just using the problem's input, but read on untrusted input data can cause arbitrary code execution via reader macros. Safer to disable them (And ensure other sane defaults) with

(uiop:with-safe-io-syntax ()
 (loop
 for line in lines
 do (with-input-from-string (s line)
 (push (read s) xs)
 (push (read s) ys))))

I'd take advantage of loop's collect ... into ... form and parse-integer, though, and skip string streams and read completely:

(defun parse-input-lines (lines)
 (loop
 for line in lines
 for first-space = (position #\Space line)
 collect (parse-integer line :end first-space) into xs
 collect (parse-integer line :start first-space) into ys
 finally (return (values (sort xs #'<) (sort ys #'<)))))

(Well, my actual Common Lisp solution for this problem used cl-ppcre regular expressions to parse the lines.)


(loop for y in ys
 do (setf (gethash y counts) (1+ (or (gethash y counts) 0))))

gethash takes an optional third argument that's used as the default value if the given key isn't present in the hash table, so this could be simplified to

(loop for y in ys
 do (setf (gethash y counts) (1+ (gethash y counts 0))))

but it's even better using incf:

(loop for y in ys
 do (incf (gethash y counts 0)))
answered Dec 7, 2024 at 23:44
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.