Reputation: 125
As a result of musings around an exercism problem, I am trying to write a function that takes an input number and an arbitrary length list of divisors to test, along with the expected divisibility (i.e. remainder 0) as a boolean, returning true if all expectations are met (defaulting to true if unspecified).
example input:
(divisible-by 10 (5 t) (4 f) 2) => t
My reading has lead to this attempt at creating the input for the function:
(defun divisible-by (numerator &rest args (&key divisors (divisorp t)))
(loop...))
My simple test cases for such an input type error out in various ways, and my searching via Google and directly here on Stack Overflow have not proved fruitful, leading me to believe my understanding is insufficient to generate the right keywords.
Pointers on how to implement such a function, where my attempts fall down or why such a function cannot be implemented as I have outlined would be gratefully received.
Upvotes: 0
Views: 718
Reputation: 60064
You don't need anything beyond &rest
:
(defun divisible-p (number &rest divisors)
"Check whether number is divisible by divisors."
(dolist (spec divisors t) ; For each 'divisor' in divisors
(etypecase spec
;; If divisor is a list, test if modulus of first value is 0
;; then compare to second (boolean)
(cons (unless (eq (zerop (mod number (first spec)))
(second spec))
(return nil)))
;; If divisor is an integer, return t if modulus == 0
(integer (unless (zerop (mod number spec))
(return nil))))))
(divisible-p 10 '(5 t) '(4 nil) 2)
==> T
(divisible-p 10 '(5 t) '(4 nil) 2 3)
==> NIL
Note that you need to quote list arguments, otherwise you will get an error that there is no function 5
.
I am not sure what you were trying to accomplish by using &key
, but they cannot be repeated in CL, i.e., if you write
(defun foo (&key a) (print a))
(foo :a 1 :a 2 :a 3)
only 3
will be printed, 1 & 2 will be ignored.
Upvotes: 1
Reputation: 51531
Defun
takes an ordinary lambda list. There is no destructuring in that.
&Rest
takes exactly one symbol, which is bound to the rest of the arguments after all required and optional parameters are filled. If you want to destructure it, use destructuring-bind
inside the function body.
Sometimes it may be worthwhile to use a macro, which takes a destructuring lambda list, to preprocess a function calling form.
Upvotes: 0
Reputation: 1446
I don't see a productive way to mix &rest
and &key
for solving this problem. Here's an example using only &key
:
(defun divisible-by (numerator &key divisors not-divisors)
(flet ((dividesp (denom) (= 0 (mod numerator denom))))
(and (every #'dividesp divisors)
(notany #'dividesp not-divisors))))
;; Call like:
(divisible-by 10 :divisors (list 2 5) :not-divisors (list 4 6))
=> t
Your lambda list has a syntax error, something close to what you wrote but valid would look and be called like this:
(defun divisible-by (numerator &rest args &key divisors (divisorp t))
(print args)
(print divisors)
(print divisorp))
;; Calling would look like this
(divisible-by 10 :divisors (list 11 2) :divisorp nil)
-> (:DIVISORS (11 2) :DIVISORP NIL)
-> (11 2)
-> NIL
Your desired input is not exactly possible for a function. Function calls don't alter the syntax of their arguments: (5 t)
would be the function 5
called with the argument t
, but 5
isn't a function so you get an error regardless of the lambda list.
Upvotes: 1