Why are circle center coordinates (key 10) not relative to the origin in DXF data? - lisp

I need this piece of information for a filter that I'm creating.
So let's say I set my grid and snap to 1 for example and then I place the origin with UCS.
And then I draw a circle with center 5, 0.
Here is what I get:
(
(-1 . <Entity name: 1f3dbb9d580>)
(0 . "CIRCLE")
(330 . <Entity name: 1f3dbba51f0>)
(5 . "270")
(100 . "AcDbEntity")
(67 . 0)
(410 . "Model")
(8 . "0")
(100 . "AcDbCircle")
(10 2495.0 1180.0 0.0)
(40 . 3.16228)
(210 0.0 0.0 1.0)
)
Why at number 10 I have those numbers?
Shouldn't it be like (10 5.0 0.0 0.0)?

The coordinates defining the geometry of the majority of planar entities (such as arcs, circles, 2D polylines etc.) are defined relative to a coordinate system known as the Object Coordinate System (OCS).
The OCS shares its origin with the World Coordinate System (WCS), with its Z-axis corresponding to the normal vector (aka extrusion vector) associated with the entity (represented by DXF group 210), and its X & Y axes defined by the Arbitary Axis Algorithm applied to the normal vector.
The Arbitrary Axis Algorithm is implemented in the standard AutoLISP trans function, which facilitates easy transformation of points from one coordinate system to another.
In your particular example, the normal vector is (0.0 0.0 1.0), which is equal to the normal vector of the WCS plane, and so for this particular example, the OCS is equal to the WCS.
However, in general, to translate points from an arbitrary OCS to either the WCS or the active User Coordinate System (UCS), you would supply the trans function with either the OCS normal vector or the entity name of the entity in question.
For example, translating from OCS to the active UCS using the OCS normal vector:
(trans (cdr (assoc 10 <dxf-data>)) (cdr (assoc 210 <dxf-data>)) 1)
Or, translating from OCS to the active UCS using the entity name:
(trans (cdr (assoc 10 <dxf-data>)) (cdr (assoc -1 <dxf-data>)) 1)
Implemented in a sample program, this might be:
(defun c:test ( / ent enx )
(cond
( (not (setq ent (car (entsel "\nSelect circle: "))))
(princ "\nNothing selected.")
)
( (/= "CIRCLE" (cdr (assoc 0 (setq enx (entget ent)))))
(princ "\nThe selected object is not a circle.")
)
( (princ "\nThe circle center relative to the UCS is: ")
(princ (trans (cdr (assoc 10 enx)) ent 1))
)
)
(princ)
)
Addressing the issues you are encountering as described in your comments, you'll need to transform the coordinates from/to the OCS & UCS to achieve the desired result, for example:
(defun c:test ( / ent enx new old xco )
(cond
( (not (setq ent (car (entsel "\nSelect circle: "))))
(princ "\nNothing selected.")
)
( (/= "CIRCLE" (cdr (assoc 0 (setq enx (entget ent)))))
(princ "\nThe selected object is not a circle.")
)
( (setq old (assoc 10 enx)
xco (car (trans (cdr old) ent 1))
new (cons 10 (trans (list xco 0.0 0.0) 1 ent))
enx (subst new old enx)
)
(entmod enx)
)
)
(princ)
)
The operation could also be condensed to a single expression, e.g.:
(setq old (assoc 10 enx)
enx (subst (cons 10 (trans (list (car (trans (cdr old) ent 1)) 0) 1 ent)) old enx)
)
(entmod enx)
However, this is less readable.

In this list coordinates are in WCS. So if You draw circle using command and active UCS is differend than WCS, everything is OK the result is as expected.
To translate coordinates between coordinate systems You can use
(trans (assoc 10 YourList) 0 1 nil)

Related

Troubleshooting a LISP routine that inserts a block with attributes at coordinates defined in a text file

So I asked a similar question here about a month ago (Lisp - Extracting info from a list of comma separated values) and managed to put something together that almost meets my needs, but I am hitting a roadblock with a few things. I'll start with the code:
(defun c:poleid ( / fn fp lst l)
;; String to list convertor. This will separate coordinates and values by comma and store them in a variable as a list
(defun LM:str->lst ( str del / len lst pos )
(setq len (1+ (strlen del)))
(while (setq pos (vl-string-search del str))
(setq lst (cons (substr str 1 pos) lst)
str (substr str (+ pos len))
)
)
(reverse (cons str lst))
)
;; Prompt the user to select a .TXT file.
(setq fn (getfiled "Select UTM GPS file" "" "txt" 4))
;; Open the file and create an empty list
(setq fp (open fn "r") lst '())
;; Iterate the file, writing each line to the list (as a string)
(while (setq l (read-line fp))
(setq lst (cons l lst))
)
;; Close the file.
(close fp)
;; Reverse the list
(setq lst (reverse lst))
;; At this point, the data is stored in a variable (lst) and the file is closed.
;; Save current OSNAP MODE and turn off
(setq os (getvar 'osmode))
(setvar "osmode" 0)
;;Set pcount to 0
(setq pcount 0)
;; Iterate the list and draw a point
;; entity at each coordinate
(foreach item lst ;; For each line in lst
(setq items (LM:str->lst item ",")) ;;set variable items as a list of item, separated by commas. Set the las
(setq ptx (nth 2 items) pty (nth 1 items) ptz (nth 3 items) idn (nth 4 items)) ;; Set the pole (pt) x, y and z values from the 2nd, 3rd and 4th values of each line. Set notes to idn (as a string). UTM values are provided to this program as y,x,z
(setq idr (LM:str->lst idn " ") idn (nth 0 idr) idr (nth 1 idr)) ;;Set idr (Pole ID) as a list of idn, then set idn as the first half of the note (HP#) and idr as the second half
(cond ((wcmatch idn "HP") ;; Only process lines that have HP in the 5th value
(
(printc idn)
(setq ptxyz (strcat ptx "," pty "," ptz)) ;;Make the pole x, y, and z value into a single string, separated by commas
(setq idx (atof ptx) idx (- idx 5.0) idx (rtos idx)) ;;set the idx as real number version of ptx, subtract 5 from it, then convert back to a string
(setq idxyz (strcat idx "," pty "," ptz)) ;;Make the ID x, y, and z value into a single string, separated by commas
;;Insert pole and ID block at xyz coords, with idn as the HP number and idr as the pole ID
(command "insert" "G:\\Shared drives\\Project Tools\\Customized Tools\\CAD\\prog\\CWood_Pole_D.dwg" ptxyz "508" "508" "0") ;; Pole symbol set to an x/y scale of 20
(command "insert" "G:\\Shared drives\\Project Tools\\Customized Tools\\CAD\\prog\\POLENA.dwg" idxyz "25.4" "25.4" "0" idn idr) ;; Pole ID block set to an x/y scale of 1, with the top half showing the HP# and the bottom half showing the pole ID
(setq pcount (+ pcount 1)) ;;Add 1 to counter
))
)
)
;; Restore OSNAP MODE and close with count of poles inserted
(setvar 'osmode os)
(setq pcount (write-to-string pcount))
(princ pcount)
(princ " pole(s) have been successively added")
(princ)
)
This is fed a .txt file that contains GPS points. The test example I have been feeding the script is:
1000,1,2,3,HP
1001,10.000,2.000,3.000,HP21 blah
1002,15.000,2.000,3.000,HP22 2gt3
1003,20.000,2.000,3.000,CU
#,Easting,Northing,Elevation,Notes
What I want the code to do is insert a block (CWood_Pole_D.dwg) in at the Easting/Northing/Elevation, and then insert a second block (POLENA.dwg) 5 units to the left of that point. The second block contains two attributes, which I would like to pull from the notes (with the two attributes separated by a space). All of this should only happen when the notes begin with "HP" (which may be followed bynumbers and a letter, ex. HP22A). The last little bit just counts up each time a pair of blocks is successfully added, but even that is spitting out a .
The problem I am having is dealing with the notes part, and conversely the loop activating when the notes are anything but JUST "HP". I'm also sure there is a bunch of redundancy or useless code, but boy oh boy is it difficult to find good information that breaks down all the individual actions (like, what is happening with the Lee Mac string to list convertor?)
I think the match does not work:
(wcmatch idn "HP")
You can try this instead, you may need a wildcard to match e.g. "HP22":
(wcmatch idn "HP*")
The rest is fine, I'd encourage to split your setq into distinct lines for readability.

Creating a rectangle from center point with existing height and width size

Im trying to create a 10 x 10 rectangle from its center point. I found existing code that creates a rectangle by its center point, but the user has to give size by picking the opposite corner. I want to replace the manual part with known dimensions of 10 x 10. The user picks a point and a 10 x 10 rectangle is created off of that center point.
Here is the existing code that I found:
(defun C:CENRECT ( / pt1 ptc vec)
(setq pt1 (getpoint "\nSpecify the center point: "))
(setq ptc (getpoint pt1 "\nSpecify the corner point: "))
(setq vec (mapcar '- ptc pt1))
(entmake
(list
'(000 . "LWPOLYLINE")
'(100 . "AcDbEntity")
'(100 . "AcDbPolyline")
'(090 . 4)
'(070 . 1)
(cons 010 (trans (mapcar '+ pt1 (list (-(car vec))(+(cadr vec))(caddr vec))) 1 0))
(cons 010 (trans (mapcar '+ pt1 (list (+(car vec))(cadr vec)(caddr vec))) 1 0))
(cons 010 (trans (mapcar '+ pt1 (list (+(car vec))(-(cadr vec))(caddr vec))) 1 0))
(cons 010 (trans (mapcar '+ pt1 (list (-(car vec))(-(cadr vec))(caddr vec))) 1 0))
(cons 210 (trans '(0.0 0.0 1.0) 1 0 T))
)
)
(redraw)
(princ)
)
Here I am trying to add the known dimensions of 10 x 10 instead of having the user pick the size manually.
(defun C:test ( / pt1 ptc vec len wid)
(setq pt1 (getpoint "\nSpecify the center point: "))
**(setq len 10)
(setq wid 10)**
(setq ptc (getpoint pt1 **len wid**))
(setq vec (mapcar '- ptc pt1))
(entmake
(list
'(000 . "LWPOLYLINE")
'(100 . "AcDbEntity")
'(100 . "AcDbPolyline")
'(090 . 4)
'(070 . 1)
(cons 010 (trans (mapcar '+ pt1 (list (-(car vec))(+(cadr vec))(caddr vec))) 1 0))
(cons 010 (trans (mapcar '+ pt1 (list (+(car vec))(cadr vec)(caddr vec))) 1 0))
(cons 010 (trans (mapcar '+ pt1 (list (+(car vec))(-(cadr vec))(caddr vec))) 1 0))
(cons 010 (trans (mapcar '+ pt1 (list (-(car vec))(-(cadr vec))(caddr vec))) 1 0))
(cons 210 (trans '(0.0 0.0 1.0) 1 0 T))
)
)
(redraw)
(princ)
)
I get an error of too many arguments. Need to figure how to give the opposite corner of 10 x 10 instead of the user doing.
Since you know the fixed dimensions of the resulting rectangle ahead of time, the code can be reduced to the following:
(defun c:cenrect ( / c z )
(setq z (trans '(0 0 1) 1 0 t))
(if (setq c (getpoint "\nSpecify center: "))
(entmake
(list
'(000 . "LWPOLYLINE")
'(100 . "AcDbEntity")
'(100 . "AcDbPolyline")
'(090 . 4)
'(070 . 1)
(cons 010 (trans (mapcar '+ c '(-5 -5)) 1 z))
(cons 010 (trans (mapcar '+ c '( 5 -5)) 1 z))
(cons 010 (trans (mapcar '+ c '( 5 5)) 1 z))
(cons 010 (trans (mapcar '+ c '(-5 5)) 1 z))
(cons 210 z)
)
)
)
(princ)
)
Here, the polyline vertices are calculated relative to the supplied center point (with respect to the active UCS), and such vertices are then transformed relative to the Object Coordinate System (OCS).
(setq pt1 (getpoint "\nSpecify the center point: "))
(setq len 10)
(setq wid 10)
(setq vec (list len wid 0 ))
(entmake......
)
Should be OK.

Getting the measurements for stretching

I am creating an algorithm to help me expand boxes to the correct size, as such:
I made a code that asks two points that should be the new depth and measures them. Then subtracts to the original depth of the block (0.51) and then asks the side to stretch.
(defun mystretchh ( dis / pt1 pt2 sel )
(while
(and
(setq pt1 (getpoint "\nFirst point of selection window: "))
(setq pt2 (getcorner pt1 "\nSecond point of selection window: "))
(not (setq sel (ssget "_C" pt1 pt2)))
)
(princ "\nNo objects where found in the selection window.")
)
(if sel
(progn
(command "_.stretch" sel "" "_non" '(0 0) "_non" (list dis 0))
t
)
)
)
(defun c:test (/ a x c p)
;ungroup
(command "pickstyle" 0)
;variables
(initget (+ 1 2 4))
(setq p (getpoint "\nSelect first point: "))
(setq c (getpoint "\nSelect second point: "))
(command "_.dist" p c)
(setq x (rtos (getvar 'distance) 2 3))
;calculate distance to stretch
(setq a (- x 0.51))
;stretch
(mystretchh a)
;regroup
(command "pickstyle" 1)
(print "Module modified.")
(princ)
)
I have two problems:
The stretch is working backwards, I tried using negative values to no avail.
It reports a syntax error, but I cannot find it.
I haven't touched AutoLISP for half a year - maybe some of you can find the problems in a blink of an eye.
You can calculate the distance between two points using the standard distance function, rather than calling the DIST command and then retrieving the value of the DISTANCE system variable.
Hence, this:
(command "_.dist" p c)
(setq x (rtos (getvar 'distance) 2 3))
Can become:
(setq x (rtos (distance p c) 2 3))
However, you are receiving a syntax error because you have converted the distance to a string using rtos, and then you are attempting to perform arithmetic on the string here:
(setq a (- x 0.51))
There is no need to convert the distance to a string, and so these expressions can become:
(setq a (- (distance p c) 0.51))
You may also want to check whether (distance p c) is greater than 0.51 before performing this subtraction to avoid unexpected results.
To determine the appropriate direction, since your current code can only stretch along the x-axis, you'll need to check whether or not the x-coordinate of point p is greater than that of point c.

How do I access Quoted Variable Data in AutoLisp?

I'm having trouble accessing the information stored in the lists STPT1 and ENDPT1 which are x(0), y(1), and z(2) coordinates.
For instance, after getting a point: (45.4529 21.6384 0.0) when I inspect with Visual LISP (-(NTH 1 STPT1) 0.5) I get a REAL 21.1384, but the following:
(SETQ STPTP2 '((NTH 0 STPT1) (- (NTH 1 STPT1) 0.5) 0))
creates the list:
((NTH 0 STPT1) (- (NTH 1 STPT1) 0.5) 0)
instead of:
(45.4529 21.1384 0.0)
My goal is to simultaneously create two parallel lines that are 0.5 units apart from each other.
How can I access the information in different positions of the lists STPT1 and ENDPT1 and then assign them in lists STPT2 and ENDPT2?
(VL-LOAD-COM)
(DEFUN C:CURBYOURENTHUSIASM ( / STPT1 ENDPT1 STPT2 ENDPT2)
(SETQ STPT1 (GETPOINT "\nSpecify start point: "))
(SETQ ENDPT1 (GETPOINT STPT1 "\nSpecify end point: "))
(SETQ STPT2 '((NTH 0 STPT1) (-(NTH 1 STPT1) 0.5) 0))
(SETQ ENDPT2 '((NTH 0 ENDPT1) (-(NTH 1 ENDPT1) 0.5) 0))
(SETQ TOP (ENTMAKE (LIST (CONS 0 "LINE")(CONS 10 STPT1)(CONS 11 ENDPT1)(CONS 8 "CONCRETE"))))
(SETQ BOTTOM (ENTMAKE (LIST (CONS 0 "LINE")(CONS 10 STPT2)(CONS 11 ENDPT2)(CONS 8 "CONCRETE"))))
(PRINC)
)
Current Issues
There are a number of issues with your current code:
1. Unbalanced Parentheses
You have one too many closing parentheses on line 5 of your code:
(SETQ STPT2 '((NTH 0 STPT1) (-(NTH 1 STPT1) 0.5) 0)))
The final closing parenthesis at the end of the above expression is closing the defun expression, resulting in the remaining expressions being evaluated on load, rather than when the function is evaluated.
2. Quoted Variable Data
You are incorrectly quoting the following expressions as literal expressions:
(SETQ STPT2 '((NTH 0 STPT1) (-(NTH 1 STPT1) 0.5) 0))
(SETQ ENDPT2 '((NTH 0 ENDPT1) (-(NTH 1 ENDPT1) 0.5) 0))
Expressions which follow the single quote will not be evaluated by the AutoLISP interpreter, but will instead be taken at 'face-value'.
This means that the nth and - functions will not be evaluated, but will instead be interpreted simply as symbols within a nested list structure. For more information on literal expressions, you may wish to refer to my tutorial describing the Apostrophe & Quote Function.
To construct a list of variable (i.e. non-literal) data, you should use the list function, e.g.:
(setq stpt2 (list (nth 0 stpt1) (- (nth 1 stpt1) 0.5) 0))
3. Unnecessary ActiveX
You are unnecessarily loading the Visual LISP ActiveX extensions (using (vl-load-com)), but are not using any functions from this library in your code. This is a relatively minor issue, but worth mentioning nonetheless.
Correcting the above issues and formatting your code with appropriate indentation, we have the following:
(defun c:curbyourenthusiasm ( / stpt1 endpt1 stpt2 endpt2 )
(setq stpt1 (getpoint "\nSpecify start point: "))
(setq endpt1 (getpoint stpt1 "\nSpecify end point: "))
(setq stpt2 (list (nth 0 stpt1) (- (nth 1 stpt1) 0.5) 0))
(setq endpt2 (list (nth 0 endpt1) (- (nth 1 endpt1) 0.5) 0))
(setq top (entmake (list (cons 0 "line") (cons 10 stpt1) (cons 11 endpt1) (cons 8 "concrete"))))
(setq bottom (entmake (list (cons 0 "line") (cons 10 stpt2) (cons 11 endpt2) (cons 8 "concrete"))))
(princ)
)
This code will now run successfully, but there are a number of possible improvements:
Possible Improvements
1. User Input Validation
You should test for valid user input before proceeding to operate on data obtained from the user: if the user dismisses the prompts without supplying a point, any arithmetic operations on the list values will error, as such values will be nil.
You can avoid such errors by simply using an if statement:
(defun c:curbyourenthusiasm ( / ep1 sp1 )
(if
(and
(setq sp1 (getpoint "\nSpecify start point: "))
(setq ep1 (getpoint "\nSpecify end point: " sp1))
)
(progn
;; Continue with program operations
)
)
(princ)
)
2. Line Angle Variation
Currently your code will always offset the second line in the negative y-direction which will result in a variation in the line spacing as the angle of the line changes - when the line angle is vertical, the two lines will overlap.
To avoid this, you can use the polar function to calculate a point offset a predetermined distance from the specified start & end points, in a direction perpendicular to the line angle, which you can calculate using the angle function:
(defun c:curbyourenthusiasm ( / ang ep1 ep2 sp1 sp2 )
(if
(and
(setq sp1 (getpoint "\nSpecify start point: "))
(setq ep1 (getpoint "\nSpecify end point: " sp1))
)
(progn
(setq ang (- (angle sp1 ep1) (/ pi 2))
sp2 (polar sp1 ang 0.5)
ep2 (polar ep1 ang 0.5)
)
;; Continue with program operations
)
)
(princ)
)
3. Accounting for UCS
The getpoint function will return points whose coordinates are expressed relative to the current UCS (User Coordinate System) active at the time that the program is evaluated.
However, the points associated with DXF groups 10 & 11 for a LINE entity in the drawing database are expected to be expressed relative to the WCS (World Coordinate System).
We can transform points between the two coordinate systems using the AutoLISP trans function:
(defun c:curbyourenthusiasm ( / ang ep1 ep2 sp1 sp2 )
(if
(and
(setq sp1 (getpoint "\nSpecify start point: "))
(setq ep1 (getpoint "\nSpecify end point: " sp1))
)
(progn
(setq ang (- (angle sp1 ep1) (/ pi 2))
sp2 (trans (polar sp1 ang 0.5) 1 0)
ep2 (trans (polar ep1 ang 0.5) 1 0)
sp1 (trans sp1 1 0)
ep1 (trans ep1 1 0)
)
;; Continue with program operations
)
)
(princ)
)
4. Quote Constant Data
Where you have constant data (e.g. explicit numerical data or strings), you can quote such data as literal data in the code, avoiding the need for the interpreter to evaluate the list and cons functions to construct the data structures:
For example:
(cons 0 "line")
Can become:
'(0 . "line")
Since 0 and "line" are both constant data and may therefore be marked as literals.
Implementing all of the above, we have the following:
(defun c:curbyourenthusiasm ( / ang ep1 ep2 sp1 sp2 )
(if
(and
(setq sp1 (getpoint "\nSpecify start point: "))
(setq ep1 (getpoint "\nSpecify end point: " sp1))
)
(progn
(setq ang (- (angle sp1 ep1) (/ pi 2))
sp2 (trans (polar sp1 ang 0.5) 1 0)
ep2 (trans (polar ep1 ang 0.5) 1 0)
sp1 (trans sp1 1 0)
ep1 (trans ep1 1 0)
)
(entmake (list '(0 . "LINE") '(8 . "concrete") (cons 10 sp1) (cons 11 ep1)))
(entmake (list '(0 . "LINE") '(8 . "concrete") (cons 10 sp2) (cons 11 ep2)))
)
)
(princ)
)

Calculating Grade Point Average and Class Average in Lisp

I am working on a problem that calls for me to compute the grade point average for each student in a class.
The input is a lisp file with the following format:
( ((name studentname) (class hour grade) (class hour grade) ...)
((name studentname) (class hour grade) (class hour grade) ...) ...)
For the output: I need to print the students name and their GPA (average of the grades for that student) sorted by average grade as well as the class average (the average of the grades for each unique class).
So far this is what I have
(setq class '(((name Seymore) (eng 3 4.0) (mat 3 3.0) (his 3 4.0) (bio 3 2.0) (biol 1 4.0))
((name Ichahbod) (cs 3 3.0) (mat 3 4.0) (spe 2 4.0) (che 3 4.0) (chel 1 3.0) (lit 3 3.0))
((name Zackery) (mat 5 3.0) (eng 3 3.0) (jou 2 3.0) (phy 3 3.0) (phyl 1 4.0) (lit 2 4.0))
((name Tukerville) (soc 4 3.0) (mus 2 4.0) (jou 3 4.0) (geo 4 4.0) (geol 1 3.0) (eng 3 3.0))
((name Simonsays) (css 3 3.0) (ast 3 4.0) (spe 3 4.0) (cs 3 4.0) (spe 2 3.0) (dan 4 4.0))
((name Snicker) (eng 3 4.0) (phy 4 4.0) (css 3 2.0) (csl 1 4.0) (ped 2 3.0) (mat 3 3.0))
((name Glass) (mat 3 1.0) (eng 3 1.0) (ped 1 1.0) (bio 3 1.0) (biol 1 0.0) (che 3 1.0) (chel 1 1.0))))
;this function multiplies the hours * the grades
(defun product (hours grades)
(* hours grades)
)
;this function multiplies a set of grades
(defun sumofGrades (L)
(cond
((null L) 0) ;check if it is first
(t (+ (product (cdr (cdadar L)) (caddar L)))) ;first val then the second val
(sumofGrades (cdr L)) ;the rest of one
)
)
;to get the total , same as sum of grades but sum the second variables
(defun totalHours (L)
(cond
((null L) 0) ;check if it is first
(t (+ (product (caddar L) (caddar L)))) ;first val then the second val
(totalHours() (cdr L)) ;the rest of one
)
)
(defun gradepoint (L)
( / (sumofGrades L) (totalHours L))
)
I attempted to start with the auxiliary methods because I thought that would be the best approach, it might not have been. When I run sumofGrades, I get back the 4.0 like I need from the first entry but it says it is not a number. I wrote these methods going off of the basic math that i need to do with the numbers but at this point I am confused on what to do next.
If I need to rewind and go a different routine I am down, any help would be appreciated.
First define some generic average function:
(defun average (lst &key (key #'identity))
(when lst
(/ (reduce #'+ lst :key key) (length lst))))
Define also a grade function to retrieve the grade of a given student in a given class (not necessary but will make it more clear):
(defun grade (class) (caddr class))
and a grades function to retrieve the grades of a student:
(defun grades (student)
(cdr (find student class :key #'cadar)))
Now you can find the average of the grades of a student by calling
(average (grades 'seymore ) :key #'grade)
=> 3.4
Following this example, you should be able to write the average of all the class by yourself.
Your code
(defun sumofGrades (L)
(cond
((null L) 0) ;check if it is first
(t (+ (product (cdr (cdadar L)) (caddar L)))) ;first val then the second val
(sumofGrades (cdr L)) ;the rest of one
)
)
Let's look at it:
(defun sumofGrades (L) ; please no camelCase in Lisp
(cond
((null L) 0) ;check if it is first <- what does this comment mean???
(t (+ (product (cdr (cdadar L)) (caddar L))))
; what is (+ (product (cdr (cdadar L)) (caddar L))) ?
; you are calling + with one argument. Why?
; what does a function like caddar mean?
; what is it supposed to do?
; no one reading your code will have an idea why
; caddar and not cdaadar, cdadaadr, or cdddddr...
; write better documented, or self-documenting code.
(sumofGrades (cdr L)) ;the rest of one <- what does this comment mean?
; what is (sumofGrades (cdr L)) ?
; is sumofGrades a variable checked in COND?
; should it be a function call?
; just as it is alone here, it does not make any sense.
; since T is always true, this clause is also never reached...
) ; <- please no dangling parentheses in Lisp
)
When compiling the above function, LispWorks says:
; (TOP-LEVEL-FORM 0)
;;;*** Warning in SUMOFGRADES: The following cond clause
;;; will never be processed: ((SUMOFGRADES (CDR L)))
Summary: sumofGrades won't work. A Lisp compiler already complains about it.
More about style
Global variables: they are defined by DEFPARAMETER or DEFVAR. Don't use SETQ.
Don't write
(setq class ...)
instead write:
(defparameter *class* ...
"the global variable *class* is a list of ...")
You probably want to try reduce:
(mapcar (lambda (l)
(cons (second (first l))
(/ (reduce #'+ (rest l) :key #'third)
(1- (length l)))))
class)
==>
((SEYMORE . 3.4) (ICHAHBOD . 3.5) (ZACKERY . 3.3333333) (TUKERVILLE . 3.5)
(SIMONSAYS . 3.6666667) (SNICKER . 3.3333333) (GLASS . 0.85714287))
then you can sort this using sort:
(sort * #'< :key #'cdr)
==>
((GLASS . 0.85714287) (ZACKERY . 3.3333333) (SNICKER . 3.3333333) (SEYMORE . 3.4)
(ICHAHBOD . 3.5) (TUKERVILLE . 3.5) (SIMONSAYS . 3.6666667))
here * is the value of the previous expression.
PS. Since this is probably h/w, I am giving a code sample rather than a complete solution, I suggest that you play with my code and then ask another very specific question if something is not unclear.
PPS. A few stylistic remarks:
do not define functions like your product, it's just confusing noise
do not use the CamelCase, use normal-lisp-dashes instead
do not use hanging parens
use Emacs to indent your code, it is unreadable now.