I am trying to make a node object in Common Lisp using SBCL that is initialized with its text element and then has links to other nodes. My function link is supposed to take node "from_node", get its member links (which should be a mutable/extendable vector) and push in the node "to_node".
I compile say.lisp, create 2 global variables that represent nodes and then try to link the two nodes. I am getting an error
Here is say.lisp
(defclass node ()
((text
:initarg :text)
(links
:initform (make-array 1 :adjustable t))))
(defun link (from_node to_node)
(vector-push-extend to_node (slot-value from_node 'links)))
And then in the REPL
* (load "say.lisp")
T
* (defvar *x* (make-instance 'node :text "hello world"))
*X*
* (defvar *y* (make-instance 'node :text "bye world"))
*Y*
* (link *x* *y*)
debugger invoked on a TYPE-ERROR in thread
#<THREAD "main thread" RUNNING {1003016593}>:
The value #() is not of type (AND VECTOR (NOT SIMPLE-ARRAY)).
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT] Exit debugger, returning to top level.
(VECTOR-PUSH-EXTEND #<NODE {10031D3983}> #() NIL)
0]
Originally I thought that I was making an immutable vector, but ":adjustable t" should allow this to work.
What is wrong?
VECTOR-PUSH-EXTEND requires that the vector argument is a "vector with a fill pointer." Passing :adjustable t to the make-array makes it adjustable, but doesn't give it a fill pointer. For instance, without a fill pointer:
CL-USER> (defparameter *x* (make-array 1 :adjustable t))
*X*
CL-USER> *x*
#(0)
CL-USER> (vector-push-extend 3 *x*)
; Evaluation aborted on #<SIMPLE-TYPE-ERROR expected-type:
(AND VECTOR (SATISFIES ARRAY-HAS-FILL-POINTER-P))
datum: #<(VECTOR T 1) {100464C57F}>>.
With a fill pointer:
CL-USER> (defparameter *x* (make-array 1 :adjustable t :fill-pointer 0))
*X*
CL-USER> *x*
#()
CL-USER> (vector-push-extend 3 *x*)
0
CL-USER> (vector-push-extend 4 *x*)
1
CL-USER> (vector-push-extend 5 *x*)
2
CL-USER> *x*
#(3 4 5)
It's an important difference because you can have adjustable arrays without fill pointers, as you've seen. These can be resized, but always appear to have as many elements as there is space. (E.g., in the first case, *x* has length one. You can also have arrays will fill pointers that are not adjustable. These would still allow you to use vector-push and vector-push-extend until they were filled up, but couldn't be resized afterward.
Related
So I am going through Practical Common Lisp once again and I cannot really understand how to create a vector with constant size.
According to the book:
However, even a vector with a fill pointer isn't completely resizable. The vector *x* can hold at most five elements. To make an arbitrarily resizable vector, you need to pass MAKE-ARRAY another keyword argument: :adjustable.
However when I use (vector-push-extend) I can extend my initial vector even when I set :adjustable nil (or leave it default).
(vector-push 'a *x*) ;; 0
(vector-push 'b *x*) ;; 1
(vector-push 'c *x*) ;; 2
(vector-push 'd *x*) ;; 3
(vector-push 'e *x*) ;; 4
*x* ;; #(A B C D E)
(vector-push 'f *x*) ;; NIL
*x* ;; #(A B C D E)
(vector-push-extend 'f *x*) ;; 5
*x* ;; #(A B C D E F)
I assumed that (vector-push-extend) cannot resize array which is not :adjustable? What is the correct way of creating non-dynamic (non-adjustable) array?
The behavior is implementation specific.
The Common Lisp specification says:
There is no specified way to create an array for which adjustable-array-p definitely returns false.
An implementation may make vectors adjustable, even though the :adjustable argument to make-array is given as nil.
To see if an array object is actually adjustable, one needs to call adjustable-array-p.
The Common Lisp standard says that a vector is expressly adjustable (and thus also actually adjustable if it was requested to be so. If it wasn't request, the vector can still be adjustable, actually adjustable.
So the :adjustable nil arg is just telling Lisp, that it can make the vector non-adjustable, if possible.
Here in SBCL:
1) A normal vector is not adjustable:
* (make-array 5)
#(0 0 0 0 0)
* (adjustable-array-p *)
NIL
2) A vector with a fill-pointer is actually adjustable in SBCL:
* (make-array 5 :fill-pointer 0)
#()
* (adjustable-array-p *)
T
3) A vector with a fill-pointer is actually adjustable in SBCL, even though the :adjustable arg was given as nil:
* (make-array 5 :fill-pointer 0 :adjustable nil)
#()
* (adjustable-array-p *)
T
That's in SBCL. In LispWorks 2) and 3) would not be actually adjustable.
The specification of make-array says that it is implementation dependent whether an array with :adjustable nil is actually adjustable. In the notes, it goes on to say:
There is no specified way to create an array for which adjustable-array-p definitely returns false.
So, it really depends on the implementation.
The correct way is to use vector-push if you do not want to extend.
I feel as though I'm in the same ballpark as Basic Lisp Macro error
but when I imagine how the code should look when expanded I don't see a problem and macroexpand isn't helping because it just doesn't want to output anything I can print; macroexpand just runs the code for me.
(setf my-array (make-array 4 :initial-element 3))
(print my-array)
(setf (aref my-array 2) 5)
(print my-array)
(defmacro set3To5 (arrnum)
(print (arrayp arrnum))
(print arrnum)
(setf (aref arrnum 3) 5)
)
(set3To5 my-array)
Running this gives me the output
argument MY-ARRAY is not an array
but if 'arrnum' is replaced by 'my-array' then it should be fine?
To quote from the linked question
Now on macro expansion, the macro ADD-TEST is called with the parameter VAR getting the value G, a symbol.
Certainly my-array is a symbol and it is the symbol I wish to manipulate so why is there a problem?
Let’s write down what would happen if a lisp were to evaluate your file line by line:
(setf my-array (make-array 4 :initial-element 3))
At this point we have MY-ARRAY bound to #(3 3 3 3)
(print my-array)
Prints #(3 3 3 3)
(setf (aref my-array 2) 5)
(print my-array)
Modifies an element and prints #(3 3 5 3)
(defmacro set3To5 (arrnum)
(print (arrayp arrnum))
(print arrnum)
(setf (aref arrnum 3) 5))
Now the macro SET3TO5 has been defined.
(set3To5 my-array)
The first step here (which we haven’t mentioned before, even though it was happening) is macroexpansion. The compiler knows that SET3TO5 is a macro so it calls the macro function with MY-ARRAY (the symbol) as arguments. Let’s look at what happens inside that macro:
(print (arrayp arrnum))
Well ARRNUM is the symbol MY-ARRAY so this prints NIL, although possibly not to the stream you expect.
(print arrnum)
This prints MY-ARRAY.
(setf (aref arrnum 3) 5)
Well ARRNUM is not an array so you have an error here.
So we have failed to evaluate this expression because expanding the macro has failed.
Here are some other things you could do:
(defun set1 (arrnum)
(print (arrayp arrnum))
(print arrnum)
(setf (aref arrnum 3) 5))
(defun set2 (arrnum)
(list 'setf (list 'aref arrnum 3) 5))
(defmacro set3 (arrnum)
(list 'setf (list 'aref arrnum 3) 5))
And now evaluate:
CL-USER> (set1 my-array)
T
#(3 3 5 3)
5
CL-USER> my-array
#(3 3 5 5)
CL-USER> (set2 my-array)
(SETF (AREF #(3 3 5 5) 3) 5)
CL-USER> (set2 'foo)
(SETF (AREF FOO 3) 5)
CL-USER> (setf (aref my-array 3) 1)
1
CL-USER> (set3 my-array)
5
CL-USER> my-array
#(3 3 5 5)
Long story short, the code should look like this to leave you with an array of (3 3 5 5).
(defmacro set3To5 (arrnum)
`(print (type-of ,arrnum))
`(setf (aref ,arrnum 3) 5)
)
GNU Common Lisp, at least, cares a lot about the difference between ` and ' (those are backtick/backquote and quote/apostrophe characters). What does backtick mean in LISP?
The comma operator undoes the quotation for an element in the list, allowing you to plug in parameters or local variables, do ",#" for splicing in a list.
Lisp macros execute the code within their bodies and the return result should be a form that can then be executed, that is macro expansion.
It seems to be they just decided on magic to define how the two backquoted lines are both turned into code because it seems like you would have to put them both within a list and backquote the entire list and then return that list, but it does look neater this way.
I have a problem with the behaviour of objects (class instances).
A code sample:
(defclass game-cards ()
((card-symbol :initarg :card-symbol :accessor card-symbol)
(colour :initarg :colour :accessor colour)))
(defvar *king-hearts* (make-instance 'game-cards
:card-symbol 'King
:colour 'hearts))
(defvar *ace-spades* (make-instance 'game-cards
:card-symbol 'Ace
:colour 'spades))
(defclass game-states ()
((my-cards :initarg :my-cards :accessor my-cards)
(other-cards :initarg :other-cards :accessor other-cards)))
(defparameter *state-1*
(make-instance 'game-states
:my-cards '(*king-hearts* *ace-spades*)
:other-cards ()))
(defmethod play-game ((state game-states))
(some-job (first (my-cards state))))
(defmethod some-job ((card game-cards))
(colour card))
When some-job is used with a game-cards object in the parameter list, it works like I expected.
CL-USER> (some-job *king-hearts*)
HEARTS
CL-USER>
Also this works:
CL-USER> (first (my-cards *state-1*))
*KING-HEARTS*
CL-USER>
When I try this:
(some-job (first (my-cards *state-1*)))
I get the following error message:
There is no applicable method for the generic function
#<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::SOME-JOB (1)>
when called with arguments
(*KING-HEARTS*).
[Condition of type SIMPLE-ERROR]
When I define some-job as a function:
(defun some-job-1 (card)
(colour card))
the same behaviour occurs.
The error message is now:
There is no applicable method for the generic function
#<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::COLOUR (1)>
when called with arguments
(*KING-HEARTS*).
[Condition of type SIMPLE-ERROR]
It seems that *king-hearts* is now not distinguished as an instance of game-cards by some-job and colour.
What is the reason? Tank you for your answers.
Quoted data is not evaluated. That's a basic Lisp evaluation rule:
CL-USER 1 > pi
3.141592653589793D0
CL-USER 2 > 'pi
PI
CL-USER 3 > '(pi pi)
(PI PI)
CL-USER 4 > (list pi pi)
(3.141592653589793D0 3.141592653589793D0)
CL-USER 5 > (list 'pi 'pi)
(PI PI)
Here PI is a symbol and not a number:
CL-USER 6 > (type-of 'pi)
SYMBOL
CL-USER 7 > (type-of pi)
DOUBLE-FLOAT
Thus we can define a method for a number:
CL-USER 8 > (defmethod square ((n number)) (* n n))
#<STANDARD-METHOD SQUARE NIL (NUMBER) 402005F60B>
CL-USER 9 > (square pi)
9.869604401089358D0
But the call for a symbol does not work, since there is only a method for a number:
CL-USER 10 > (square 'pi)
Error: No applicable methods for #<STANDARD-GENERIC-FUNCTION SQUARE 4060010C1C> with args (PI)
1 (continue) Call #<STANDARD-GENERIC-FUNCTION SQUARE 4060010C1C> again
2 (abort) Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
We can define a method for a symbol in the debugger:
CL-USER 11 : 1 > (defmethod square ((n symbol))
(let ((n (symbol-value n)))
(* n n)))
#<STANDARD-METHOD SQUARE NIL (SYMBOL) 4020285ED3>
And then we re-invoke the call:
CL-USER 12 : 1 > :c 1
9.869604401089358D0
How to solve your problem:
Either create a list of CLOS objects using LIST
or retrieve the CLOS objects from the global variable using SYMBOL-VALUE.
The latter usually makes less sense.
I'm excise example from book: ANSI Common Lisp, charpter 14.6 conditions.
But the check-type and assert example doesn't work in sbcl.
And how can I input new value? it is always failed no matter whatever new value I input.
However, it works in clisp.
example code:
(let ((x '(a b c))) (check-type (car x) integer "an integer") x)
(let ((sandwich '(ham on rye)))
(assert (eql (car sandwich) 'chicken)
((car sandwich))
"I wanted a ~a sandwich." 'chicken)
sandwich)
In sbcl:
example 1:
* (let ((x '(a b c))) (check-type (car x) integer "an integer") x)
; in: LET ((X '(A B C)))
; (CHECK-TYPE (CAR X) INTEGER "an integer")
; --> DO BLOCK LET TAGBODY TAGBODY SETF
; ==>
; (SB-KERNEL:%RPLACA X
; (SB-KERNEL:CHECK-TYPE-ERROR '(CAR X) #:G0 'INTEGER
; "an integer"))
;
; caught WARNING:
; Destructive function SB-KERNEL:%RPLACA called on constant data.
; See also:
; The ANSI Standard, Special Operator QUOTE
; The ANSI Standard, Section 3.2.2.3
;
; compilation unit finished
; caught 1 WARNING condition
debugger invoked on a SIMPLE-TYPE-ERROR in thread
#<THREAD "main thread" RUNNING {B3E2341}>:
The value of (CAR X) is A, which is not an integer.
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [STORE-VALUE] Supply a new value for (CAR X).
1: [ABORT ] Exit debugger, returning to top level.
(SB-KERNEL:CHECK-TYPE-ERROR (CAR X) A INTEGER "an integer")
0] 0
Type a form to be evaluated: 0
debugger invoked on a SIMPLE-TYPE-ERROR in thread
#<THREAD "main thread" RUNNING {B3E2341}>:
The value of (CAR X) is A, which is not an integer.
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [STORE-VALUE] Supply a new value for (CAR X).
1: [ABORT ] Exit debugger, returning to top level.
(SB-KERNEL:CHECK-TYPE-ERROR (CAR X) A INTEGER "an integer")
0]
example2:
* (let ((sandwich '(ham on rye)))
(assert (eql (car sandwich) 'chicken)
((car sandwich))
"I wanted a ~a sandwich." 'chicken)
sandwich)
; in: LET ((SANDWICH '(HAM ON RYE)))
; (ASSERT (EQL (CAR SANDWICH) 'CHICKEN) ((CAR SANDWICH))
; "I wanted a ~a sandwich." 'CHICKEN)
; --> TAGBODY SETF
; ==>
; (SB-KERNEL:%RPLACA SANDWICH
; (SB-IMPL::ASSERT-PROMPT '(CAR SANDWICH) (CAR SANDWICH)))
;
; caught WARNING:
; Destructive function SB-KERNEL:%RPLACA called on constant data.
; See also:
; The ANSI Standard, Special Operator QUOTE
; The ANSI Standard, Section 3.2.2.3
;
; compilation unit finished
; caught 1 WARNING condition
debugger invoked on a SIMPLE-ERROR in thread
#<THREAD "main thread" RUNNING {B3E2341}>:
I wanted a CHICKEN sandwich.
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [CONTINUE] Retry assertion with new value for (CAR SANDWICH).
1: [ABORT ] Exit debugger, returning to top level.
(SB-KERNEL:ASSERT-ERROR (EQL (CAR SANDWICH) (QUOTE CHICKEN)) (((CAR SANDWICH) HAM)) ((CAR SANDWICH)) "I wanted a ~a sandwich." CHICKEN)
0] 0
The old value of (CAR SANDWICH) is HAM.
Do you want to supply a new value? (y or n) y
Type a form to be evaluated:
'chicken
debugger invoked on a SIMPLE-ERROR in thread
#<THREAD "main thread" RUNNING {B3E2341}>:
I wanted a CHICKEN sandwich.
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [CONTINUE] Retry assertion with new value for (CAR SANDWICH).
1: [ABORT ] Exit debugger, returning to top level.
(SB-KERNEL:ASSERT-ERROR (EQL (CAR SANDWICH) (QUOTE CHICKEN)) (((CAR SANDWICH) HAM)) ((CAR SANDWICH)) "I wanted a ~a sandwich." CHICKEN)
0]
But in clisp, it works well.
[1]> (let ((x '(a b c))) (check-type (car x) integer "an integer") x)
*** - The value of (CAR X) should be an integer.
The value is: A
The following restarts are available:
STORE-VALUE :R1 Input a new value for (CAR X).
ABORT :R2 Abort main loop
Break 1 [2]> :R1
New (CAR X)> 99
(99 B C)
[3]> (let ((sandwich '(ham on rye)))
(assert (eql (car sandwich) 'chicken)
You are in the top-level Read-Eval-Print loop.
Help (abbreviated :h) = this list
Use the usual editing capabilities.
(quit) or (exit) leaves CLISP.
((car sandwich))
You are in the top-level Read-Eval-Print loop.
Help (abbreviated :h) = this list
Use the usual editing capabilities.
(quit) or (exit) leaves CLISP.
"I wanted a ~a sandwich." 'chicken)
sandwich)
** - Continuable Error
I wanted a CHICKEN sandwich.
If you continue (by typing 'continue'): Input a new value for (CAR SANDWICH).
The following restarts are also available:
ABORT :R1 Abort main loop
Break 1 [4]> continue
New (CAR SANDWICH)> 'chicken
(CHICKEN ON RYE)
[5]>
You are not supposed to modify literal data. The consequences are undefined in the Common Lisp standard.
This is undefined:
(let ((x '(a b c)))
(setf (car x) 10))
This works:
(let ((x (list 'a 'b 'c)))
(setf (car x) 10))
This is my second proper attempt at a Lisp program, as a dice-roller for Mythender (a freely distributed tabletop RPG). It has a couple of problems though:
When it's loaded I get a prompt to confirm creation of the package. Surely this file should be creating it?
When I try to build it standalone with the LispWorks application builder it gives an error saying that I am trying to invoke a CAPI function at compile-time, but I don't see where that is.
I've gotten negative comments from some lisp folks I spoke to about the (the null ()) sections which are meant to indicate a function has no return so no point leaving anything on the stack - is this proper or not? Is there a better way to do it?
Any general suggestions would also be welcome.
(defpackage :mythender (:add-use-defaults t) (:use "CAPI"))
(in-package :mythender)
(defun d6 () (the fixnum (+ 1 (random 6))))
(defun d6s (count)
(declare (type fixnum count))
(the list (loop for x from 1 to count collecting (d6))))
(defun d6over (count threshold)
(declare (type fixnum count threshold))
(the fixnum (count-if
(lambda (x) (> threshold x))
(d6s count))))
(defvar *storm* 3)
(defvar *thunder* 3)
(defvar *lightning* 0)
(declare (ftype (function) printstate))
(defun printstate ()
(print *storm*)
(print *thunder*)
(print *lightning*)
(the null ()))
(defun roll ()
(incf *lightning* (d6over *thunder* 3))
(incf *thunder* (d6over *storm* 3))
(the null ()))
(defun damage (threshold)
(setf *thunder* (d6over *thunder* threshold))
(the null ()))
(defun doroll (&rest args)
(roll)
(update-interface)
(the null ()))
(define-interface mythender-interface () ()
(:panes
(roll-button push-button :data "Roll" :callback #'doroll)
(damage-button push-button :data "Damage")
(storm-pane display-pane :title "Storm:" :title-position :left)
(thunder-pane display-pane :title "Thunder:" :title-position :Left)
(lightning-pane display-pane :title "Lightning:" :title-position :left))
(:layouts
(main-layout column-layout '(storm-pane thunder-pane lightning-pane buttonlayout))
(buttonlayout row-layout '(roll-button damage-button))))
(defvar *interface*)
(defun update-interface-slot (slotname value)
(declare (type string slotname) (type fixnum value))
(setf (display-pane-text (slot-value *interface* slotname)) (write-to-string value))
(the null ()))
(defun update-interface ()
(update-interface-slot 'storm-pane *storm*)
(update-interface-slot 'thunder-pane *thunder*)
(update-interface-slot 'lightning-pane *lightning*)
(the null ()))
(defun start ()
(setf *interface* (make-instance 'mythender-interface))
(display *interface*)
(the null (update-interface)))
An answer to your build problem has to wait until you tell us the build statement and the error message.
Your last question:
(declare (ftype (function) printstate))
(defun printstate ()
(print *storm*)
(print *thunder*)
(print *lightning*)
(the null ()))
It's known that it is a function. No need to declare that. Declaring types like that, have in plain Common Lisp only the purpose of optimization hints to the compiler, which the compiler may ignore. Only CMUCL (and derived compilers like SBCL and SCL) actually does more with declared types.
Nobody writes such code in Lisp. Better omit the types. Remember: Lisp is not a statically typed language.
(defun printstate ()
(print *storm*)
(print *thunder*)
(print *lightning*)
(values))
Using (values) causes the function to not return a value. That's usually preferred, not returning NIL.
If you want to actually check types in a meaningful way at runtime, then make use of ASSERT, CHECK-TYPE and/or DEFMETHOD.
(defun d6s (count)
(declare (type fixnum count))
(the list (loop for x from 1 to count collecting (d6))))
Is just:
(defmethod d6s ((n integer))
"Returns a list of n dice rolls."
(loop repeat n collect (d6)))
Don't forget to describe the semantics of your function in human readable form.