patternMinor
Advent of Code: "Not Quite Lisp"
Viewed 0 times
adventlispquitecodenot
Problem
I have solved this simple challenge on Advent of Code:
Santa is trying to deliver presents in a large apartment building, but
he can't find the right floor - the directions he got are a little
confusing. He starts on the ground floor (floor
the instructions one character at a time.
An opening parenthesis,
closing parenthesis,
The apartment building is very tall, and the basement is very deep; he
will never find the top or bottom floors.
This is my first time trying out Lisp (and functional programming at all) and would like to see if there is anything that I could do better, or more "functionally".
I wrote the function in a manner to be quite generic, i.e., it can take any input string, and any two characters. It returns a list containing the amount of up and down characters along with the difference between the two (which is the answer to the challenge). Demo on Coding Ground
Example generic usage:
```
(setf night-before-xmas "'Twas the night before Christmas, when all through the house, Not a creature was stirring, not even a mouse; The stockings were hung by the chimney with care, In hopes that St. Nicholas soon would be there;")
(time (print (count-up-down-characters-with-difference night-before-xmas #\e #\a)))
;; prints:
;(21 -10 11)
Santa is trying to deliver presents in a large apartment building, but
he can't find the right floor - the directions he got are a little
confusing. He starts on the ground floor (floor
0) and then followsthe instructions one character at a time.
An opening parenthesis,
(, means he should go up one floor, and aclosing parenthesis,
), means he should go down one floor.The apartment building is very tall, and the basement is very deep; he
will never find the top or bottom floors.
This is my first time trying out Lisp (and functional programming at all) and would like to see if there is anything that I could do better, or more "functionally".
I wrote the function in a manner to be quite generic, i.e., it can take any input string, and any two characters. It returns a list containing the amount of up and down characters along with the difference between the two (which is the answer to the challenge). Demo on Coding Ground
(defun count-up-down-characters-with-difference (input-string up-char down-char)
"Given a string of any length, iterate each character of the string looking for up- and down-characters
provided by the caller, and return the number of each, as well as the difference between them."
(setf count-up 0)
(setf count-down 0)
(loop for c across input-string do
(if (char-equal c up-char)
(incf count-up))
(if (char-equal c down-char)
(decf count-down)))
(list count-up count-down (+ count-up count-down)))Example generic usage:
```
(setf night-before-xmas "'Twas the night before Christmas, when all through the house, Not a creature was stirring, not even a mouse; The stockings were hung by the chimney with care, In hopes that St. Nicholas soon would be there;")
(time (print (count-up-down-characters-with-difference night-before-xmas #\e #\a)))
;; prints:
;(21 -10 11)
Solution
The IDE you linked uses CLISP, which is a bit lenient; when I evaluate
that definition, I immediately get two warnings from SBCL:
That is because
It's implementation-defined what happens in this case.
So firstly let's define them with
(Constants usually have
usually a bit different as well, I'm only going to show the
Emacs-formatted code without more explanation though.)
Then again, if you want a functional solution, don't use
is, assignment) or globals at all. Instead (and in general) prefer
A few other things to make it more idiomatic (for some value of
idiomatic) are the use of the most strict equality operator possible,
which here would be
characters), and not using
or
fine for now. Also consider using
Or also
that definition, I immediately get two warnings from SBCL:
; in: DEFUN COUNT-UP-DOWN-CHARACTERS-WITH-DIFFERENCE
; (SETF COUNT-DOWN 0)
; ==>
; (SETQ COUNT-DOWN 0)
;
; caught WARNING:
; undefined variable: COUNT-DOWN
; (SETF COUNT-UP 0)
; ==>
; (SETQ COUNT-UP 0)
;
; caught WARNING:
; undefined variable: COUNT-UP
;
; compilation unit finished
; Undefined variables:
; COUNT-DOWN COUNT-UP
; caught 2 WARNING conditions
That is because
count-up and count-down weren't defined anywhere.It's implementation-defined what happens in this case.
So firstly let's define them with
defvar:(defvar count-up)
(defvar count-down)
...
(defvar +night-before-xmas+ "...")
...
(Constants usually have
+ as markers, globals *. Indentation isusually a bit different as well, I'm only going to show the
Emacs-formatted code without more explanation though.)
Then again, if you want a functional solution, don't use
setf (thatis, assignment) or globals at all. Instead (and in general) prefer
let.A few other things to make it more idiomatic (for some value of
idiomatic) are the use of the most strict equality operator possible,
which here would be
eql (since you check for exact equality betweencharacters), and not using
if if there's only one case, instead whenor
unless would be preferable.(defun count-up-down-characters-with-difference (input-string up-char down-char)
"..."
(let ((count-up 0)
(count-down 0))
(loop
for c across input-string
do (when (eql c up-char)
(incf count-up))
(when (eql c down-char)
(decf count-down)))
(list count-up count-down (+ count-up count-down))))
loop also has more grammar to compress it more, but I think this isfine for now. Also consider using
count instead of a manual loop, i.e.:(defun count-up-down-characters-with-difference (input-string up-char down-char)
"..."
(let ((count-up (count up-char input-string))
(count-down (- (count down-char input-string))))
(list count-up count-down (+ count-up count-down))))
Or also
(count up-char input-string :test #'char-equal) (see COUNT).Context
StackExchange Code Review Q#114931, answer score: 8
Revisions (0)
No revisions yet.