Why do crosslinked defstructs cause stack overflows? [duplicate] - data-structures

This question already has an answer here:
Print defstruct in Lisp
(1 answer)
Closed 4 years ago.
While playing around with graphs, I got a curious error I didn't quite understand. The code below reproduces the problem.
;; Define struct to store a node with links to other nodes.
(defstruct node properties links)
;; Make two nodes
(setf a (make-node :properties '(:name a))
b (make-node :properties '(:name b)))
;; Create link from b to a. This works fine...
(push b (node-links a))
;; ... but this crosslink makes lisp chase its own tail for a while and then crash with a stack overflow.
(push a (node-links b))
I got the same result with SBCL and Clozure. Setting *print-length* to a manageable value did not work.
So my questions is: Why doesn't this code create the same kind of infinite printing loop as a circular list (i.e., no stack overflow and stoppable with Ctrl-C). Any input is appreciated.
Thanks,
Paulo

*print-length* controls the number of elements in a list. You're looking for *print-level*. This works fine for me.
(let ((*print-level* 3))
(format t "~W~%" a))
;; Output: #S(NODE :PROPERTIES (:NAME A)
;; :LINKS (#S(NODE :PROPERTIES # :LINKS #)))
Alternatively, you could use *print-circle* to detect cycles and print them in an even nicer way.
(let ((*print-circle* t))
(format t "~W~%" a))
;; Output: #1=#S(NODE :PROPERTIES (:NAME A)
;; :LINKS (#S(NODE :PROPERTIES (:NAME B) :LINKS (#1#))))
Here, it actually detects the cycle and prints #1#, a reference to the #1= to show that it's the same object.

Related

Scheme box and pointer cyclic list

I am trying to understand what is happening when writing this code by drawing it with the box and pointer method but cant quite get it. Anybody that could help?
(define bar (list 'a 'b 'c 'd 'e))
(set-cdr! (cdddr bar) (cdr bar))
My idea:
Also I don't know how to start to draw a diagram for the code under, I cant see where the points and boxes for (car) need to be.
(define bah (list 'bring 'a 'towel))
(set-car! bah (cdr bah))
This is the initial situation:
And this is the final situation:
If you try to print bah, it is printed as a three element list, whose first element is a two element list:
((a towel) a towel)
But in fact the two structures are shared, so that, for instance, if you do:
(set-car! (cddr bah) 'handkerchief)
bah will be printed as:
((a handkerchief) a handkerchief)

How does one make symbols that refer to structure slots in lisp?

I am self learning lisp and thought a nice non-trivial program would be to write a set of standard tree insertion and manipulation routines. I figured it could be done with CONS but wanted to try it with a structure.
I put together one version that worked:
(defstruct treenode data left right)
(defun tree-insert ( value tree )
"Insert data into tree"
(if tree
(if (< value (treenode-data tree))
(setf (treenode-left tree) (tree-insert value (treenode-left tree)))
(setf (treenode-right tree) (tree-insert value (treenode-right tree))))
(setf tree (make-treenode :data value)))
tree)
which rebuilt the tree at every step which seemed computationally inefficient. By inefficient, I mean that I have to use setf every time I do another level of recursion. So I wanted to try a scheme that passed the tree by reference rather than by value so I could make assignments in the subroutine that inserts into a tree.
I cobbled the following together, which does not work (but give me credit for having comments):
(defstruct treenode data left right)
(defun tree-insert ( value tree )
"Insert data value into tree, using pass by reference.
value A datum to insert, in this version has to be a number.
tree The tree passed as a symbol."
(setq tval (symbol-value tree))
(if (eq tval nil)
(set tree (make-treenode :data value)) ; Empty tree. Place data here.
(if (< value (treenode-data tval)) ; Non-empty node. Decide which subtree for insert.
(tree-insert value (treenode-left tval)) ; Left side
(tree-insert value (treenode-right tval)))) ; Right side. This is a stable sort.
nil)
? (setf tr nil)
NIL
? (tree-insert 10 'tr)
NIL
? tr
#S(TREENODE :DATA 10 :LEFT NIL :RIGHT NIL)
?
The initial insert works fine. Passing a symbol the (set tree ...) correctly inserts the structure with left and right porinters nil.
Of course, the problem that follows is that on the recursive call to tree-insert I am not passing a symbol.
That is the hangup. I haven't found a way to refer to a structure slot as a symbol that I can then pass to tree-insert.
I've been looking around for a couple of days and found this interesting comment about the defstruct macro: "defstruct not only defines an access function for each slot, but also arranges for setf to work properly on such access functions, defines a predicate named name-p, defines a constructor function named make-name, and defines a copier function named copy-name. All names of automatically created functions are interned in whatever package is current at the time the defstruct form is processed (see package). Also, all such functions may be declared inline at the discretion of the implementation to improve efficiency; if you do not want some function declared inline, follow the defstruct form with a notinline declaration to override any automatic inline declaration."
So, what could I do to do the magic that setf does? I know I can do assignments to slots with setf, but I haven't gotten setf to work in a function due to lexical scope rules. Maybe like adding automatic functions to allow symbol generating, like (treenode-data-symbol tr)?
Of course, lisp programmers have dealt with binary trees since before my first PDP-8/L. What's the lispy way to do this?
This is an edited question. User Rainer Joswig gave a very fast and concise response. I learned a lot from the example he gave. I was interested in the issue of modifying the tree directly rather than using a return value from a function.
From the comments I've seen here, and the one answer by Rainer Joswig, should I draw the conclusion that the pointer manipulation is computationally low in cost, and that the best lisp approach is to use a function that returns a tree rather than relying on an approach of modifying the argument?
simple version for your inspiration:
(defstruct node a b v)
(defun insert-tree (tree value)
(cond ((null tree)
(setf tree (make-node :v value)))
((> (node-v tree)
value)
(setf (node-a tree)
(insert-tree (node-a tree) value)))
(t
(setf (node-b tree)
(insert-tree (node-b tree) value))))
tree)
using it:
CL-USER 171 > (let ((tree nil))
(loop for i in '(4 7 3 5 9 10 11 8)
do (setf tree (insert-tree tree i)))
(pprint tree)
(values))
#S(NODE :A #S(NODE :A NIL :B NIL :V 3)
:B #S(NODE :A #S(NODE :A NIL :B NIL :V 5)
:B #S(NODE :A #S(NODE :A NIL :B NIL :V 8)
:B #S(NODE :A NIL
:B #S(NODE :A NIL
:B NIL
:V 11)
:V 10)
:V 9)
:V 7)
:V 4)
Now, if wanted to do less setf operations, we could check whether the returned subtree is the same which we passed. This will only not be the case when we create a new node.
(defun insert-tree (tree value)
(cond ((null tree)
(setf tree (make-node :v value)))
((> (node-v tree)
value)
(let ((new-tree (insert-tree (node-a tree) value)))
(unless (eql new-tree (node-a tree))
(setf (node-a tree) new-tree))))
(t
(setf (node-b tree)
(insert-tree (node-b tree) value))))
tree)
or with a local macro hiding part of the code:
(defun insert-tree (tree value)
(macrolet ((insert (place call &aux (new-value-sym (gensym "new-value")))
`(let ((,new-value-sym ,call))
(unless (eql ,place ,new-value-sym)
(setf ,place ,new-value-sym)))))
(cond ((null tree)
(setf tree (make-node :v value)))
((> (node-v tree)
value)
(insert (node-a tree) (insert-tree (node-a tree) value)))
(t
(insert (node-b tree) (insert-tree (node-b tree) value))))
tree))
Trying to add an answer from another angle.
In standard Common Lisp structures have a bunch of limitations to make them low-level and efficient to use. Among those limitations:
access to structure slots via slot names is undefined. some implementations do it, others not.
redefining a structure definition has undefined consequences. This means that in some cases, one best restarts Lisp to do that...
The idea behind that: all operations to structures should be able to be inlined and an executing program should not need any further information about structure slots (their names, their memory locations, ...). There would be no dynamic lookup at runtime.
Then Common Lisp in general has this further limitation: it has no first class pointers. There is no mechanism to provide a pointer only pointing directly to the slot of a structure. In some older Lisp dialects this might be possible via a concept of locatives - pointers in those languages. Common Lisp does not support that.
This means practically: to update a slot of a structure, one needs access to the structure and a setter operation.
How do update the slot of a structure?
I can think of two simple ways:
pass the structure, a new value and an indicator what to update -> then dispatch on the indicator and call the right updater
Example
(defun update (s indicator value)
(case indicator
(:a (setf (node-a s) value))
(:b (setf (node-b s) value))))
(update tree :a (make-node :v 100))
Pass a closure, which does the update
Example:
(let ((tree ...))
(flet ((do-something (updater)
(funcall updater (make-node :v 100))))
(do-something (lambda (value) (setf (node-a tree) value) ...)))
With much thanks to Rainer and Will, I understand Common Lisp better, now. The point about not having first class pointers is huge. I don't have to keep looking for that anymore, though I did see a package that implemented refs in my searches.
The key problem in my approach was that I defined an empty tree as nil. Since passing nil doesn't allow any manipulation of the argument, nil being immutable, the algorithm was bound to fail.
Redefining the empty tree as '(nil) allows the program to work.
;; Make list of 5 random numbers.
(setf r5 (loop for i from 1 to 5 collect (random 100)))
;; Initialize tr to empty tree.
;; Empty tree is '(nil). Tree with data is '(data left right),
;; where left and right are either empty tree or tree with data.
(setf tr '(nil))
(defun tree-insert ( value tree )
"Insert data into tree. tree is modified with an insertion."
(if (equal tree '(nil))
(progn ; Empty (sub)tree. Insert value.
(setf (car tree) value)
(setf (cdr tree) (list (list nil)(list nil))))
(progn ; Non-empty subtree.
(if (< value (car tree))
(tree-insert value (second tree)) ; Insert on left.
(tree-insert value (third tree))))) ; Insert on right.
nil)
;; Load tree with the list of random numbers defined above.
(mapc (lambda (val) (tree-insert val tr)) r5)
(defun tree-walk (tree)
"Retrieve keys in sorted order."
(if (car tree)
(progn
(tree-walk (second tree)) ; Left subtree.
(format t " ~d" (car tree))
(tree-walk (third tree))))) ; Right subtree.
;; Walk the tree.
(tree-walk tr)
Example in use:
? (setf r5 (loop for i from 1 to 5 collect (random 100)))
(22 50 76 20 49)
? (setf tr '(nil))
(NIL)
? (mapc (lambda (val) (tree-insert val tr)) r5)
;Compiler warnings :
; In an anonymous lambda form at position 37: Undeclared free variable TR
(22 50 76 20 49)
? tr
(22 (20 (NIL) (NIL)) (50 (49 (NIL) (NIL)) (76 (NIL) (NIL))))
? (tree-walk tr)
20 22 49 50 76
NIL
?
So, several things to make this work. A mutable object has to be passed to the procedure. In this case I redesigned the structure to be a list, either '(nil) for empty, or '(data left right), where left and right are either '(nil) or '(data left right). A list containing nil can be manipulated. However, I had to use car and cdr to access the structure so as to preserve Lisp's pointer that was passed to the procedure.
Another thing I had to do was not use a list constant in the functon definitions. I'm sure knowledgeable people will be in the know about this, and chucle a bit about the opaque error that follow until the issue is understood, but if I had used '((nil)(nil)) rather than (list (list nil)(list nil)) in tree-insert it would not work. Looks like Lisp compiles list shorthand notation to a pointer to an abject in memory which is used on all subsequent calls of the function.
Oh, there is a leftover progn function call in tree-insert. That was from when I wrapped everything with progn to let me add print statements during debugging.
Running timing on the functions was interesting. It's fast! I'll run some timing comparisons to compare the functional reassignment approach vs the search and insert algorithms.
Thanks, again, to the expert comments. Since last contributing I've learned a little about map, loop/collect, and that variables leak out of functions into global space when let isn't used in function definitions. Also wrapping a funciton with a lot of output with (progn ... nil) saves screen space after large data structures are used. I have learned a lot with just this exercise.

How to store user input into a list in Scheme?

I need to be able to take input from a user (store it in a list) and and print it to the screen to prove it was stored in a list or print #f if the list contains an element that is not a number. The idea is to then use the result of that function in another that will give me the sum (I've already made that function). I have been looking all over and can not find any information on how to do this in Scheme. I know let has to be used, but I am not sure how I would implement it.
(read-user-ints)
=>1
=>2
=>3
=>4
=>5
=>e
(1 2 3 4 5)
start with defining e to be the empty list.
(define e '())
then you can use a recursive loop with READ to get ints, each time you get one you can append it onto the end of your list like this:
(set! e (append e (list number)))
If you were struggling with the LET part, you can do something like this
(let loop ((number (read)))
;; check if number is actually a number or if it's 'e'
;; either append it or exit the loop (by not calling loop)
)
Answering your follow up comment.
You can use BEGIN to put multiple statements in one branch of an IF expression, like this:
(define read-int-list
(lambda ()
(let loop ((number (read)))
(if (number? number)
(begin (set! e (append e (list number)))
(loop))
'done
))))

Walking through a binary tree

I'm having issues writing a function to walk through a binary tree, the function takes in a search_term, list and returns true or false. Here is what I have and it's essentially the same thing I found googling how to implement binary search in Scheme.
(define (tree-lookup val tree)
(if (empty-tree? tree)
#f
(let ((curr-val (node-value tree))
(left (node-left tree))
(right (node-right tree)))
(cond ((equal? val curr-val) #t)
((< val curr-val))
(tree-lookup val left)
(else
(tree-lookup val right))))))
(define tree-test '(((1 2) 3)(4 (5 6)) 7 (8 9 10))) ; Test tree
The problem comes when it tries to compare the "val" variable with the node. This means I am comparing a real number to a list, like (< 2 '((1 2) 3)). I tried testing for atom values only but then I am stuck on how to go back up the tree when I reach a leaf.
Here's the error message:
<: contract violation
expected: real?
given: '{{1 2} 3}
argument position: 2nd
other arguments...:
8
At first glance the procedure looks fine. I suspect that the problem is in the node-value, node-left and node-right procedures, or in the way you're building the tree - for starters, the sample tree provided in the question doesn't seem right to me.
Think about it, the error message signals that the < operator was applied to the list '{{1 2} 3}, meaning that curr-val is a list, but it should be a value.

Environment not part of a continuation?

Is the environment not part of a continuation in scheme?
I have tested this with Chicken, Gauche, Racket and Gambit, and they all behave similarly:
(define kont #f)
(let ((a 1)
(b 2))
(call-with-current-continuation
(lambda (k)
(set! kont k)
(display 'mutating)
(newline)
(set! a -1)
(set! b -2)))
(display (+ a b))
(newline))
I would expect -3 when the LET is evaluated, but +3 in the calls to kont (since I thought the program would remember the bindings of a and b before mutation):
(let ... ) ; <-- evaluating the LET above
; prints "mutating"
=> -3
(kont 100)
=> -3
(kont 100)
=> -3
So the continuation only affects control, and not the environment? In this case, why is it said that one of the ways to implement continuations is to "copy the stack" (are bindings are not on the stack?)
The continuation captures the bindings. However, as you surmise, these bindings are mutable.
You've been somewhat misled, here, by the "copies the stack" slogan. While this is a reasonable way to think about call/cc, it's not the whole story. For one thing, you really really wouldn't want a language feature that exposed whether or not local bindings were stack-allocated or not.
Instead, call/cc is defined using the notion of "program contexts". For a nice treatment of this, you might want to take a look at Shriram Krishnamurthi's (free, online) textbook PLAI, or at the (not-free, much more in-depth) book "Semantics Engineering with PLT Redex".
As an aside; your program doesn't really check what you wanted it to check, because you never invoked the captured continuation. I think you wanted to write something like this:
#lang racket
(define kont #f)
(let ([a 3])
(let/cc k
(set! kont k)
(set! a 4))
(printf "~s\n" a))
(kont)
... which shows pretty much the same behavior that you mention above.
You change the values of a and b in the environment with set!. So a and b is -1 and -2 in the continuation environment. You can not unroll side effects. There are no differences between a, b and kont in your continuation.

Resources