Reputation: 45
I'm dealing with this problem. I'm writing a function that returns true if the condition below are met. The input is a list and there are two valid operations: foo and baz
(foo a b c ... )
is valid, so the list starting with foo followed by words letters or numbers is valid, as many you want.
(baz a)
so baz
followed by words letters, numbers is valid.
I can also combine them:
(foo (baz a) b 42 hello) it is valid
(baz (foo a hello 42)) it is valid
also a list with just an element is valid: (a)
(hello)
(42)
if i use other operators it is not valid: like
(pizza a b c)
or (foo (dog a b) a)
so the first element of each bracket must be a valid operator
(defun ok-operation (x)
(cond ((eq (car x) 'foo) (ok-list (cdr x)) T)
((eq (car x) 'baz) (ok-list (cdr x) T))))
(defun ok-list (x)
(cond ((atom (car x)) (ok-list (cdr x)) T)
(t (ok-operation (cdr x) )))
The most common errors are:
Undefined function List called with arguments ()
and
variable atom is not bound
Upvotes: 1
Views: 460
Reputation:
I am not quite clear from the question what the actual semantics you are after is. However here is an implementation (almost certainly not suitable as a homework answer!) of a style of solution to this kind of recursive-descent problem which I'm quite fond of. It's kind of entertaining to work out how hard doing something like this would be in more fashionable object systems.
As a note to people who know CLOS: I tend to put methods which are either trivial or trivial default cases in the defgeneric
form: that's probably not the usual style, but I like it as it means you can see all the simple cases which you never change in the generic function definition and you're less likely to omit some critical fallback case.
For added value: what's the bug in this code and how do you fix it?
(defgeneric form-ok-p (form)
;; This is the entry point
(:documentation
"Is a form OK?")
(:method ((form number))
;; numbers are OK
t)
(:method ((form symbol))
;; symbols are OK
;;
;; This seems to be the case from the example but the text says
;; 'letters' which would more naturally mean strings or perhaps
;; characters. So, well, this might need to be made more complicated.
;; Note this means that (form-ok-p '()) is true.
t)
(:method ((form t))
;; defaultly forms are not OK
nil))
(defmethod form-ok-p ((form cons))
;; a cons is OK if it is OK as an operator with arguments
(destructuring-bind (operator . args) form
(and (listp args)
;; check the arguments are a proper list (this could also be
;; done in an around method on operator-ok-p and that might
;; be cooler). This considers that if ARGS is not a proper
;; list then the form is not OK, but isn't an error: it might
;; be appropriate to actually consider this case an error, so
;; (form-ok-p '(foo . a)) would raise an error. (The
;; deficiency in this code is here).
(loop for tail on args
unless (or (null tail) (consp tail))
do (return nil)
finally (return t))
(operator-ok-p operator args))))
(defgeneric operator-ok-p (op args)
;; Methods on this GF can assume that ARGS is a proper list
(:documentation
"Is an operator with some arguments OK?")
(:method (op args)
;; defaultly, no it's not
nil))
(defmethod operator-ok-p ((op (eql 'foo)) args)
;; (foo ...) is OK if all its args are OK
(every #'form-ok-p args))
(defmethod operator-ok-p ((op (eql 'bar)) args)
;; (bar ...) is OK if there is one arg and it's OK
(and (null (rest args))
(form-ok-p (first args))))
Upvotes: 1
Reputation: 139261
It would be great if you post an interaction which can be reproduced.
Your code - as you have posted - does not work. for example:
(defun ok-operation (x)
(cond ((eq (car x) 'foo)
(ok-list (cdr x)) ; this value is not used?
; thus the call is not having an effect
T)
((eq (car x) 'baz)
(ok-list (cdr x) T)) ; this value is not used?
) ; here a parenthesis is missing
Upvotes: 0
Reputation: 48745
(list)
in ok-operation
and ok-list
does not do anything with the argument list
since you are calling it. Instead you are calling the function list
with zero arguments. It returns the empty list ()
and (car ()) ; ==> ()
list
is part of the common-lisp
package so the only way you get that error is if you are creating this in a function package that doesn't import common-lisp
. Regardless I think you wanted it to be (car list)
which takes the car
of the variable list
.
You have 3 parts in the cond
clauses. Eg in ok-operations
if (eq (car list) 'foo)
it will first execute (ok-list (cdr list))
and since you don't use the returned value is just for effect and then always return T
. If you want two conditions to be true you can use (and expression-1 expression-2)
or you could just let the return of the second expression be the result by removing the final T
.
atom
is a standard function in common-lisp
package as well.
The error messages are strange. It makes me question if you're running your code in a standard Common Lisp implementation. All languages with the same syntax are called Lisp so make sure it's Common Lisp your implementation is compatible with and not one of the vast other kinds of lisp dialects out there.
Upvotes: 0