AutoLisp - Two basic functions: Determining the number and value ​of block attributes - lisp

I have to write the function that determines the number and values ​​of block attributes in AutoLisp.
I have function which count the atributes:
(defun c:Test (/ s ss)
(if (and (princ "\n Select FIRST Attributed Block :")
(setq s (ssget "_+.:S:E" '((0 . "INSERT") (66 . 1))))
(princ "\n Select the SECOND Attributed Block :")
(setq ss (ssget "_+.:S:E" '((0 . "INSERT") (66 . 1))))
)
(mapcar
'length
(mapcar
'(lambda (a)
(mapcar
'(lambda (x) (vla-get-textstring x))
(vlax-invoke (vlax-ename->vla-object a) 'getattributes)
)
)
(list (ssname s 0) (ssname ss 0))
)
)
)
)**
A function that returns the values ​​of attributes:
(defun c:Test (/ ss n e x)
(while (progn (princ "\n Select single attributed block :")
(setq ss (ssget "_+.:S" '((0 . "INSERT") (66 . 1))))
)
(setq n (entnext (ssname ss 0)))
(while (not (eq (cdr (assoc 0 (setq e (entget n)))) "SEQEND" ))
(if (eq (cdr (assoc 0 e)) "ATTRIB")
(print (cdr (assoc 1 e)))
)
(setq n (entnext n))
)
)
(princ)
)
Could you help me to combine this to functions into one?

Here is a lisp program that will loop over all blocks from a user selection set and:
1.) print block name
2.) print the association list of AttributeTag.AttributeValue
3.) print the list of AttributeTags
4.) print the list of AttributeValues
5.) print the number of AttributeValues
I also attached what the command line output should look like.
Lisp command line output
;;www.cadwiki.net
(defun c:test (/ SSINPUT)
(setq ssInput (ssget (list '(0 . "insert"))))
(PRINT-BLOCK-ATTRIBUTE-INFO ssInput)
(princ)
)
(defun PRINT-BLOCK-ATTRIBUTE-INFO (ssInput / ATTRIBUTETAGS ATTRIBUTETAGSTOVALUES ATTRIBUTEVALUES BLOCKENTITY BLOCKVLAOBJECT I NUMBEROFBLOCKATTRIBUTES
)
(setq i 0)
(if (= ssInput nil)
(progn
(princ "ssInput was nothing, exiting.")
(exit)
)
)
(princ (strcat "\nItems in selection set: " (itoa (sslength ssInput))))
(while (< i (sslength ssInput))
(setq blockEntity (ssname ssInput i))
(setq blockVlaObject (vlax-ename->vla-object blockEntity))
(setq attributeTagsToValues (GET-BLOCK-ATTRIBUTE-NAME-TO-VALUE-ASSOC blockEntity))
(princ (strcat "\nBlock name: " (vla-get-name blockVlaObject)))
(princ "\nBlock attributes tag to values association list: ")
(princ attributeTagsToValues)
(setq attributeTags (GET-NTHS-FROM-LISTS 0 attributeTagsToValues nil))
(princ "\nBlock attribute tags list: ")
(princ attributeTags)
(setq attributeValues (GET-LAST-ITEM-FROM-EACH-LIST attributeTagsToValues))
(princ "\nBlock attributes values list: ")
(princ attributeValues)
(princ "\nNumber of block attributes: ")
(setq numberOfBlockAttributes (itoa (length attributeValues)))
(princ numberOfBlockAttributes)
(setq i (+ i 1))
)
)
(defun GET-NTHS-FROM-LISTS (N LSTs removeDuplicates / CT LST2 LST IT)
(setq LST2 nil)
(foreach LST LSTs
(setq IT (nth N LST))
(if removeDuplicates
(if (not (member IT LST2))
(setq LST2 (append LST2 (list IT)))
)
(setq LST2 (append LST2 (list IT)))
)
)
LST2
)
(defun GET-LAST-ITEM-FROM-EACH-LIST (LSTs / CDRs FAIL LST)
(setq CDRs nil
FAIL nil
)
(if (not (= (type LSTs) 'LIST))
(setq FAIL "not a list")
)
(if (not FAIL)
(foreach LST LSTs
(setq FAIL (cond
((not (= (type LST) 'LIST)) "non-list member")
((not (cdr LST)) "no CDR")
(T nil)
)
)
(if (not FAIL)
(setq CDRs (append CDRs (list (cdr LST))))
)
)
)
CDRs
)
(defun GET-BLOCK-ATTRIBUTE-NAME-TO-VALUE-ASSOC (entity / COUNTER COUNTER2 COUNTERMAX COUNTERMAX2 DXFCODE0 DXFCODE2 DXFCODE66 DXFCODE8 DXFCODECODE-1 ENTITIESTORETURN ENTITYDXFCODES *ERROR* RETURNLIST
SUPPLIEDTRUENAME TRUENAME ATTRIBUTETAG ATTRIBUTEVALUE DXFCODE-1 ENTITYNAMEFORDRILLING SUBLIST TAGSANDVALUES THECALLINGFUNCTIONSNAME
)
(setq counter 0) ;initialize counter to 0 for while loop
(if ;if
(/= entity nil) ;entity is not nil
(progn ;progn wrap
(setq entityDxfCodes (entget entity)) ;set the varaible entityDxfCodes to the list of entities from the en varaible
;; you can use the method here to find any value from a dxfCodecode
(setq dxfCode-1 (cdr (assoc -1 entityDxfCodes))) ;set dxfCode-1 to the second element of the item that has -1 as it's first element, this is the entity name
(setq dxfCode0 (cdr (assoc 0 entityDxfCodes))) ;set dxfCode0 to the element of the item that has 0 as it's first element, this is the entity type
(setq dxfCode2 (cdr (assoc 2 entityDxfCodes))) ;set dxfCode8 to the second element of the item that has 8 as it's first element, this is the name, or block name
(setq dxfCode8 (cdr (assoc 8 entityDxfCodes))) ;set dxfCode8 to the second element of the item that has 8 as it's first element, this is the layer
(setq dxfCode66 (cdr (assoc 66 entityDxfCodes))) ;set dxfCode66 to the second element of the item that has 66 as it's first element, this is the attribute flag
(setq entityNameForDrilling entity)
(if ;if start
(= dxfCode66 1) ;entity attribute flag is 1
(progn ;progn wrap
(while (/= dxfCode0 "SEQEND") ;while loop to drill to each sub entity in a block
(setq attributeTag (cdr (assoc 2 entityDxfCodes))) ;set attributeTag to the second element of the second Dxf code (assoc 2) of the entityDxfCodes variable
(setq attributeValue (cdr (assoc 1 entityDxfCodes))) ;set attributeValue to the second element of the first Dxf code (assoc 1) of the entityDxfCodes variable
(if
(/= attributeValue nil)
(progn
(setq sublist (cons attributeTag attributeValue))
(setq tagsAndValues (cons sublist TagsAndValues))
)
)
(setq entityNameForDrilling (entnext entityNameForDrilling))
(setq entityDxfCodes (entget entityNameForDrilling))
(setq dxfCode0 (cdr (assoc 0 entityDxfCodes)))
)
) ;progn wrap end
) ;if end
) ;progn wrap end
) ;if end
(setq returnList tagsAndValues)
)

Related

AutoLISP, How to export my selected polylines to a CSV with a name

I have this code below. It exports the selected polylines lenght to a CSV but it does not give it a name so i cant make a difference between two(or more) types of polyline.
My question is how to modify this code in order to be able to export the lenghts with the name of the linetype.
For example: I loaded ZIGZAG and TRACKS linetype, next I run my function and select all of the drawn polylines and I want to see in my CSV that which linetype is how long by name.
(defun c:Polyline_számoló (/ s i e l fn)
(if (and(setq s (ssget '((0 . "LWPOLYLINE"))))
(setq fn (getfiled "Create Output File" "" "csv" 1)))
(progn
(setq s (_SortSSByXValue s))
(setq i (sslength s))
(while (setq e(ssname s (setq i (1- i))))
(setq l (cons (vla-get-length (vlax-ename->vla-object e)) l))
(ssdel e s)
)
)
)
(setq l (list (cd:CON_All2Str l nil)))
(if (LM:WriteCSV l fn)
(startapp "explorer" fn)
)
(princ)
)
(defun cd:CON_All2Str (Lst Mode)
(mapcar
(function
(lambda (%)
(if Mode
(vl-prin1-to-string %)
(vl-princ-to-string %)
)
)
)
Lst
)
)
(defun _SortSSByXValue (ss / lst i e add)
(if (eq (type ss) 'PICKSET)
(progn
(repeat (setq i (sslength ss))
(setq lst (cons (cons (setq e (ssname ss (setq i (1- i))))
(cadr (assoc 10 (entget e)))
)
lst
)
)
)
(setq add (ssadd))
(foreach e (vl-sort lst (function (lambda (a b) (< (cdr a) (cdr b))))) (ssadd (car e) add))
(if (> (sslength add) 0)
add
)
)
)
)
(defun LM:writecsv ( lst csv / des sep )
(if (setq des (open csv "w"))
(progn
(setq sep (cond ((vl-registry-read "HKEY_CURRENT_USER\\Control Panel\\International" "sList")) (",")))
(foreach row lst (write-line (LM:lst->csv row sep) des))
(close des)
t
)
)
)
(defun LM:lst->csv ( lst sep )
(if (cdr lst)
(strcat (LM:csv-addquotes (car lst) sep) sep (LM:lst->csv (cdr lst) sep))
(LM:csv-addquotes (car lst) sep)
)
)
(defun LM:csv-addquotes ( str sep / pos )
(cond
( (wcmatch str (strcat "*[`" sep "\"]*"))
(setq pos 0)
(while (setq pos (vl-string-position 34 str pos))
(setq str (vl-string-subst "\"\"" "\"" str pos)
pos (+ pos 2)
)
)
(strcat "\"" str "\"")
)
( str )
)
)
Here's a lisp function that will export a csv file.
The csv file contains two sections:
1.) a length summary by linetype name
2.) an individual line summary with length and linetype
csv example:
--Length Summary By LineType--
LineType,Length
CENTER,739.97
HIDDEN,1858.61
--Length Breakdown By Individual Line--
LineType,Length
CENTER,246.656
HIDDEN,309.768
HIDDEN,309.768
CENTER,246.656
HIDDEN,309.768
HIDDEN,309.768
CENTER,246.656
HIDDEN,309.768
HIDDEN,309.768
Lisp code
;;www.cadwiki.net
(defun c:test (/ s i e l fn CSVSTRING CSVSTRINGLIST DATAITEM individualLineDataList LINELENGTH LINETYPE VLAOBJECT NEWASSOC NEWLENGTH PREVIOUSLENGTH lineTypeToLengthAssoc SUMMARYENTRY
)
(if (and (setq s (ssget '((0 . "LWPOLYLINE"))))
(setq fn (getfiled "Create Output File" "" "csv" 1))
)
(progn
(setq s (_SortSSByXValue s))
(setq i (sslength s))
(setq individualLineDataList (list))
(while (setq e (ssname s (setq i (1- i))))
(setq vlaObject (vlax-ename->vla-object e))
(setq lineType (vla-get-linetype vlaObject))
(setq lineLength (vla-get-length vlaObject))
(setq dataItem (list lineType lineLength))
(setq individualLineDataList (cons dataItem individualLineDataList))
(setq summaryEntry (assoc lineType lineTypeToLengthAssoc))
(if (/= summaryEntry nil)
(progn
(setq previousLength (cdr summaryEntry))
(setq newLength (+ previousLength lineLength))
(setq newAssoc (cons lineType newLength))
(setq lineTypeToLengthAssoc (REMOVE-ASSOC-BY-KEY lineType lineTypeToLengthAssoc))
(setq lineTypeToLengthAssoc (cons newAssoc lineTypeToLengthAssoc))
)
(progn
(setq newAssoc (cons lineType lineLength))
(setq lineTypeToLengthAssoc (cons newAssoc lineTypeToLengthAssoc))
)
)
(ssdel e s)
)
)
)
(setq csvStringList (list (list "--Length Summary By LineType--")))
(setq csvStringList (cons (list "LineType" "Length") csvStringList))
(foreach assocItem lineTypeToLengthAssoc
(setq csvString (summaryAssocToStringList assocItem))
(setq csvStringList (cons csvString csvStringList))
)
(setq csvStringList (cons (list "--Length Breakdown By Individual Line--") csvStringList))
(setq csvStringList (cons (list "LineType" "Length") csvStringList))
(foreach item individualLineDataList
(setq csvString (cd:CON_All2Str item nil))
(setq csvStringList (cons csvString csvStringList))
)
(setq csvStringList (reverse csvStringList))
(if (LM:WriteCSV csvStringList fn)
(startapp "explorer" fn)
)
(princ)
)
(defun REMOVE-ASSOC-BY-KEY (assocKey assocList / newAssocList item)
(setq newAssocList nil)
(foreach item assocList
(if (not (= (car item) assocKey))
(setq newAssocList (append newAssocList (list item)))
)
)
newAssocList
)
(defun summaryAssocToStringList (assocItem / LINELENGTH LINETYPE STRINGLIST)
(setq lineType (car assocItem))
(setq lineLength (cdr assocItem))
(setq stringList (list lineType (rtos lineLength 2 2)))
)
(defun cd:CON_All2Str (Lst Mode)
(mapcar
(function
(lambda (%)
(if Mode
(vl-prin1-to-string %)
(vl-princ-to-string %)
)
)
)
Lst
)
)
(defun _SortSSByXValue (ss / lst i e add)
(if (eq (type ss) 'PICKSET)
(progn
(repeat (setq i (sslength ss))
(setq lst (cons (cons (setq e (ssname ss (setq i (1- i))))
(cadr (assoc 10 (entget e)))
)
lst
)
)
)
(setq add (ssadd))
(foreach e (vl-sort lst (function (lambda (a b) (< (cdr a) (cdr b))))) (ssadd (car e) add))
(if (> (sslength add) 0)
add
)
)
)
)
(defun LM:writecsv (lst csv / des sep)
(if (setq des (open csv "w"))
(progn
(setq sep (cond ((vl-registry-read "HKEY_CURRENT_USER\\Control Panel\\International" "sList"))
(",")
)
)
(foreach row lst (write-line (LM:lst->csv row sep) des))
(close des)
t
)
)
)
(defun LM:lst->csv (lst sep)
(if (cdr lst)
(strcat (LM:csv-addquotes (car lst) sep) sep (LM:lst->csv (cdr lst) sep))
(LM:csv-addquotes (car lst) sep)
)
)
(defun LM:csv-addquotes (str sep / pos)
(cond
((wcmatch str (strcat "*[`" sep "\"]*"))
(setq pos 0)
(while (setq pos (vl-string-position 34 str pos))
(setq str (vl-string-subst "\"\"" "\"" str pos)
pos (+ pos 2)
)
)
(strcat "\"" str "\"")
)
(str)
)
)

Error Bad argument type : fixnump : nil when calling second function

I'm trying to create an array of lists with its elements being: Surname , Name and Age.
Here is my code in AutoLISP:
(defun C:DP_ADINREG ( / prenume nume varsta inreg)
(initget 1)
(setq prenume (getstring "\nIntroduceti prenumele: "))
(initget 1)
(setq nume (getstring "\nIntroduceti numele: "))
(initget 7)
(setq varsta (getint "\nIntroduceti varsta: "))
(setq inreg (list (cons 'pn prenume) (cons 'nf nume)
(cons 'v varsta))
DP_DATA (append DP_DATA (list inreg))
)
(princ)
)
(defun C:DP_LISTARE ( / curent inreg n)
(setq curent DP_DATA
n 1)
(while curent
(setq inreg (car curent))
(princ (strcat "\nInregistrarea #" (itoa n)
": " (cdr (assoc 'pn inreg))
", " (cdr (assoc 'nf inreg))
". Varsta " (itoa (cdr (assoc 'v inreg)))
)
)
(setq curent (cdr curent)
n (1+ n)
)
)
(princ)
)
Problem is, when trying to access the second function, it gives me an error called:
Bad argument type : fixnump : nil.
Now I have no idea where the problem actually is.
Any ideas?
The error:
; error: bad argument type: fixnump: nil
Arises when a function requiring an integer argument is supplied with a value of nil. Hence, this error is arising from the evaluation of the second of your two itoa expressions:
(itoa (cdr (assoc 'v inreg)))
(Since the first itoa expression is being supplied with the variable n, which cannot be nil).
This implies that the following expression returns nil:
(cdr (assoc 'v inreg))
Which implies that one of the association lists within the list held by your global variable DP_DATA does not contain a dotted pair with key v. I would therefore suggest checking the value held by your global variable DP_DATA.
Aside: note that initget has no effect on a getstring prompt - you can achieve the same effect using a basic while loop, e.g.:
(while (= "" (setq prenume (getstring "\nIntroduceti prenumele: ")))
(princ "\nPlease enter a first name.")
)
(while (= "" (setq nume (getstring "\nIntroduceti numele: ")))
(princ "\nPlease enter a surname.")
)
You can account for null values in your association list using some basic error checking:
(defun C:DP_ADINREG ( / prenume nume varsta )
(while (= "" (setq prenume (getstring "\nIntroduceti prenumele: ")))
(princ "\nPlease enter a first name.")
)
(while (= "" (setq nume (getstring "\nIntroduceti numele: ")))
(princ "\nPlease enter a surname.")
)
(initget 7)
(setq varsta (getint "\nIntroduceti varsta: ")
DP_DATA (cons (list (cons 'pn prenume) (cons 'nf nume) (cons 'v varsta)) DP_DATA)
)
(princ)
)
(defun C:DP_LISTARE ( / n )
(setq n 0)
(foreach lst (reverse DP_DATA)
(princ
(strcat
"\nInregistrarea #" (itoa (setq n (1+ n)))
": " (cond ((cdr (assoc 'pn lst))) (""))
", " (cond ((cdr (assoc 'nf lst))) (""))
". Varsta " (itoa (cond ((cdr (assoc 'v lst))) (0)))
)
)
)
(princ)
)
The above will return a blank first name/surname where not present, and an age of 0 where not present; you could alternatively return an error if these values are not present, e.g.:
(defun C:DP_LISTARE ( / n nf pn v )
(setq n 1)
(foreach lst (reverse DP_DATA)
(if
(and
(setq pn (cdr (assoc 'pn lst)))
(setq nf (cdr (assoc 'nf lst)))
(setq v (cdr (assoc 'v lst)))
)
(princ (strcat "\nInregistrarea #" (itoa n) ": " pn ", " nf ". Varsta " (itoa v)))
(princ (strcat "\nMissing data for item " (itoa n)))
)
(setq n (1+ n))
)
(princ)
)

Object reactor "copied" and "modified" in one entity going wrong - Help needed

My intent is to create some lines having two texts each with short info (made as a group), and letting the reactor assigned to the line making sure that:
When copied, the handles of each entity will get updated with the info of the others and
In case I stretch the line using the end grip, the line reactor will update the length of the line as stored xdata and updating the text value that corresponds to the length.
So far and with the valuable help of this forum I was able to make it with the copy part of the story. When I try to incorporate the 'modified' option in the reactor, it does not work. Below is the code I use.
The first function assigns the reactor to the line during its creation through an other function, not shown here. The rest are callbacks and so on. Any suggestions appreciated.
In continuation to this question:
How to update xdata information of an entity when it is copied
(defun assign_LLreactor (LL_ename LL_Length LL_Size / LL_ename LL_hLength LL_hSize)
(if (= 'vlr-object-reactor (type lineReactor))
(vlr-owner-add lineReactor (vlax-ename->vla-object LL_ename))
(vlr-set-notification
(setq lineReactor
(vlr-object-reactor (list (vlax-ename->vla-object LL_ename)) "Line Reactor"
'(
(:vlr-modified . LL_callback_mod)
(:vlr-copied . LL_callback)
)
)
)
'active-document-only
)
)
(makeagroup (ssadd (handent LL_Length) (ssadd (handent LL_Size) (ssadd LL_ename (ssadd)))))
)
(defun LL_callback (notifierobj reactorobj paramls)
(if (/= 0 (car paramls))
(progn
(setq LL_owner (append LL_owner (list (car paramls))))
(vlr-command-reactor "LL_copied_re"
'(
(:vlr-commandended . LL_copyended)
(:vlr-commandcancelled . LL_copycancelled)
(:vlr-commandfailed . LL_copycancelled)
)
)
)
)
)
(defun LL_callback_mod (owner reactorobj paramls)
(setq LL_owner (append LL_owner (list owner)))
(vlr-command-reactor "LL_modified_re"
'(
(:vlr-commandended . LL_modifiedended)
(:vlr-commandcancelled . LL_modifiedcancelled)
(:vlr-commandfailed . LL_modifiedcancelled)
)
)
)
(defun LL_copyended ( reactor params / enametxt txt1 txt2 hSize hLength groupedentities txthandles grtxtdata)
(vlr-remove reactor)
(if
(and
(setq LL_ent (car LL_owner))
(setq LL_enx (entget LL_ent '("LL_U")))
(= (cdr (assoc 0 LL_enx)) "LINE")
(setq LL_3data (assoc -3 LL_enx))
)
(progn
(foreach n (setq groupedentities (_groupedenames LL_ent))
(if (= (cdr (assoc 0 (setq grtxtdata (entget n '("LL_U"))))) "TEXT")
(progn
(setq txthandles (append txthandles (list (cdr (assoc 5 grtxtdata)))))
(entmod (subst (list -3 (list "LL_U" (cons 1005 (cdr (assoc 5 LL_enx))))) (assoc -3 grtxtdata) grtxtdata))
)
)
)
(setq txthandles (vl-sort txthandles (function (lambda (a b) (< a b)))))
(entmod (subst (list -3 (list "LL_U" (cons 1005 (cadr txthandles)) (cons 1005 (car txthandles)))) (assoc -3 LL_enx) LL_enx))
)
)
(if (and LL_enx (= 'vlr-object-reactor (type lineReactor)))
(vlr-owner-add lineReactor (vlax-ename->vla-object LL_ent))
)
(setq LL_owner (cdr LL_owner))
(princ)
)
(defun LL_modifiedended ( reactor params / LL_ent LL_enx llpt10 llpt11 LL_3data LL_3data_n)
(if
(and
(setq LL_ent (car LL_owner))
(setq LL_enx (entget LL_ent '("Pdata")))
(= (cdr (assoc 0 LL_enx)) "LINE")
(setq LL_3data (assoc -3 LL_enx))
)
(progn
(if (= 'vlr-object-reactor (type lineReactor))
(vlr-remove lineReactor)
)
(setq llpt10 (cdr (assoc 10 LL_enx)))
(setq llpt11 (cdr (assoc 11 LL_enx)))
(setq LL_3data_n (list -3 (subst (cons 1000 (rtos (distance llpt10 llpt11) 2 3)) (nth 2 (cadr LL_3data)) (cadr LL_3data))))
(ht_ss (ssadd (cdr (assoc -1 (entmod (subst LL_3data_n (assoc -3 LL_enx) LL_enx)))) (ssadd)))
(if (= 'vlr-object-reactor (type lineReactor))
(vlr-add lineReactor)
)
)
)
(princ)
)
(defun LL_copycancelled ( reactor params )
(vlr-remove reactor)
(setq LL_owner nil)
(princ)
)
(defun LL_modifiedcancelled ( reactor params )
(vlr-remove reactor)
(setq LL_owner nil)
(princ)
)
I expect the object reactor works well for both 'modified' and 'copied' action for lines being either stretched or copied with updated xdata and texts.
So I managed to solve the issue by utilizing a separate list for the owners of the specific reactor type (e.g. 'copied' or 'modified'). So 'LL_owner' list became two, 'LL_copy_owner' and 'LL_mod_owner'. Now it works fine (until further notice :).

lisp simplify polynomials

I'm writting a program that simplifies polynimials, only addition and multiplication for now.
I've been slamming my head agains the keyboard for hours now and figure it was time to ask for some help.
(defun simplify (lis)
(if (eq (car lis) '+)
(cons '+ (simplify-addition (cdr lis)))
(if (eq (car lis) '*)
(cons '* (simplify-multiplication (cdr lis)))
)
)
)
(defun simplify-addition (lis)
(if (not (null lis))
(if (listp (car lis))
(list (simplify (car lis)) (simplify-addition (cdr lis)))
(if (numberp (car lis))
(if (eq (car lis) 0)
(simplify-addition (cdr lis))
(if (null (cdr lis))
lis
(cons (car lis) (simplify-addition (cdr lis)))
)
)
(if (eq (car lis) '+)
(list (car lis) (simplify-addition (cdr lis)))
(if (eq (car lis) '*)
(list (car lis) (simplify-addition (cdr lis)))
lis
)
)
)
)
)
)
(defun simplify-multiplication (lis)
(if (not (null lis))
(if (listp (car lis))
(if (find 0 (car lis))
0
(list (simplify (car lis)) (simplify-multiplication (cdr lis)))
)
(if (numberp (car lis))
(if (null (cdr lis))
lis
(cons (car lis) (simplify-multiplication (cdr lis)))
)
(if (eq (car lis) '+)
(list (car lis) (simplify-multiplication (cdr lis)))
(if (eq (car lis) '*)
(list (car lis) (simplify-multiplication (cdr lis)))
lis
)
)
)
)
)
)
This is what should happen:
(simplify ‘(+ x ( + 0 3 ) ( * 1 5 ) ( * ( * x y z ) 0 ) )) --> ( + x 3 5 )
(simplify ‘(* (+ 6 0) (* 1 6 2))) --------------------------------> (* 6 (* 6 2))
but instead i either get the same polynomial i sent in, or something completely off
EDIT:
The simplification that i need is to remove 0 from additions, so that:
(+ 3 0) --> 3
(+ 4 0 6) --> (+ 4 6)
and the multiplication with zero are removed
(* 6 0 7) --> 0
First you might want to improve your coding style a bit to make it readable.
don't put parentheses on their own lines. This just wastes space and doesn't help at all.
don't use CAR and CDR in domain specific code. The domain is mathematics. You use expressions (operator arg1 arg2). Instead using CAR and CDR define functions OPERATOR and ARGUMENTS and use them.
use CASE, COND and other multiway conditional expressions, instead of nested IF - where useful.
try to extract the traversal of data structures from domain code. Use higher order functions instead of recursion (MAP, REDUCE, ...).
Example:
Some basic domain functions:
(defun operator (expression)
(first expression))
(defun arguments (expression)
(rest expression))
(defun make-expression (operator arguments)
(if (= (length arguments) 1)
(first arguments)
(cons operator arguments)))
(defun is-zero? (expression)
(and (numberp expression)
(= expression 0)))
Now the simplifications:
(defun simplify (expression)
(if (atom expression)
expression
(case (operator expression)
(+ (make-expression '+ (simplify-addition (arguments expression))))
(* (make-expression '* (simplify-multiplication (arguments expression)))))))
(defun simplify-addition (expressions)
(remove-if #'is-zero?
(mapcar #'simplify
(remove-if #'is-zero? expressions))))
(defun simplify-multiplication (expressions)
(if (member-if #'is-zero? expressions)
(list 0)
(let ((expressions1 (mapcar #'simplify expressions)))
(if (member-if #'is-zero? expressions1)
(list 0)
expressions1))))
See, how much more readable the code is? No more CAR, LIS, CDR. The intention of the recursive invocations is also much clearer to understand.
It still not optimal, but it should get you going.
I've only looked at simplify-multiplication but there are a number of issues here.
On a general note, you want to recursively simplify first, then check for specific constants afterwards. (A post-order traversal, I guess.)
Second, I don't see you checking for 1 anywhere so I don't see how (* 1 5) ==> 5 is supposed to work.
Third, let's step through (simplify '(* (+ 2 0) 3)) for a bit:
(defun simplify-multiplication (lis)
; lis = '((+ 2 0) 3)
(if (not (null lis))
; ==> t
(if (listp (car lis))
; (car lis) = '(+ 2 0), so (listp '(+ 2 0)) ==> t
(if (find 0 (car lis))
; succeeds because '(+ 2 0) contains 0
; this is completely wrong! you're not supposed to check sublists of lis
0
; ... yeah, you just returned 0 just because there was a 0 *somewhere*
(list (simplify (car lis)) (simplify-multiplication (cdr lis)))
)
...
Or (simplify '(* 0 2)):
(defun simplify-multiplication (lis)
; lis = '(0 2)
(if (not (null lis))
; ==> t
(if (listp (car lis))
; (car lis) = 0, so (listp 0) ==> nil
(if (find 0 (car lis))
0
(list (simplify (car lis)) (simplify-multiplication (cdr lis)))
)
(if (numberp (car lis))
; (numberp 0) ==> t
(if (null (cdr lis))
; (cdr lis) = '(2), so (null '(2)) ==> nil
lis
(cons (car lis) (simplify-multiplication (cdr lis)))
; ... wait, what?
; you're just recursively walking through the list without
; checking what numbers you actually got. this won't simplify
; anything.
)
(if (eq (car lis) '+)
; what is this branch for? it can only succeed if you have code of the form
; (* 1 + 2)
; which is a syntax error
(list (car lis) (simplify-multiplication (cdr lis)))
(if (eq (car lis) '*)
; this branch is for code like (* * *). huh???
(list (car lis) (simplify-multiplication (cdr lis)))
lis
)
)
)
)
)
)

How do I format a list of strings

I have a list of strings that I need to format using emacs lisp. This was the only way I could think of going about it:
(setq slist '("is there" "any" "way" "directed iteration"))
(format "%s _%s_ %s to do %S in elisp?"
(elt slist 0)
(elt slist 1)
(elt slist 2)
(elt slist 3)
(elt slist 4))
Giving me what I want.
is there _any_ way to do "directed iteration" in elisp?
There must be a more elegant way, but after much thought, I'm not seeing it. I'm very new to emacs lisp and I might be missing something obvious.
Use apply:
(apply 'format "%s _%s_ %s to do %S in elisp?" slist)
The apply function takes a function (or symbol) as its first argument, then a number of individual arguments, finishing with a list of arguments.
I've decided to make it into a standalone project by adding some more features, fixing some bugs and adding more bugs! yey :)
You can find the project here: http://code.google.com/p/formatting-el/source/browse/trunk/formatting.el
Not sure how much buggy this is, but at the first sight it seems to work:
(defun directive-end (c)
(member c "csdoxXeg%"))
(defun pp-if-nil (spec)
(position ?\% spec))
(defun pp-list (spec args)
(let ((pos 0) (last 0) (fstring "% ") current seen-^)
(catch 't
(while t
(setq pos (1+ (or (position ?% spec :start pos) -1))
current (aref spec pos))
(unless (and seen-^ (char-equal current ?\}) (null args))
(princ (substring spec last (1- pos))))
(setq last pos pos (1+ pos))
(cond
((char-equal current ?^)
(incf last)
(setq seen-^ t))
((char-equal current ?\{)
(setq pos (+ pos (pp-list (substring spec pos) (car args)))
args (cdr args)
last pos
seen-^ nil ))
((char-equal current ?\})
(if args (setq pos 0 last 0)
(throw 't nil)))
((char-equal current ?%)
(setq seen-^ nil last (1+ last))
(write-char ?%))
(t (unless args (error "Not enough argumens for list iteration"))
(setf (aref fstring 1) current)
(princ (format fstring (car args)))
(setq args (cdr args)
seen-^ nil
last
(or (position-if #'directive-end spec :start pos)
pos)))))) pos))
(defun cl-format (spec &rest args)
(with-output-to-string
(let ((pos 0) (last 0) (fstring "% ") current)
(catch 't
(while t
(setq pos (1+ (or (position ?\% spec :start pos) -1))
current (aref spec pos))
(when (= pos 0) (throw 't nil))
(princ (substring spec last (1- pos)))
(setq last pos pos (1+ pos))
(cond
((char-equal current ?^)
(unless args
(setq last (pp-if-nil spec)
pos last)))
((char-equal current ?\{)
(setq pos (+ pos (pp-list (substring spec pos) (car args)))
args (cdr args)
last pos))
((char-equal current ?\})
(error "Unmatched list iteration termination directive"))
((char-equal current ?%)
(write-char ?%)
(incf last))
(t (unless args (error "Not enough argumens"))
(setf (aref fstring 1) current)
(princ (format fstring (car args)))
(setq args (cdr args)
last
(or (position-if #'directive-end spec :start pos)
pos))))
(incf pos))))))
(cl-format "begin: %{%s = %d%^,%}; %% %c %% %{%{%s -> %d%^.%},%}"
'(a 1 b 2 c 3) ?\X '((a 2 b 4 c 6) (a 1 b 3 c 5)))
"begin: a = 1,b = 2,c = 3; % X % a -> 2.b -> 4.c -> 6,a -> 1.b -> 3.c -> 5,"
This tries to replicate some (very simplistic) Common Lisp-like printing behaviour of the ~{ ... ~} directives.