I am trying to figure out the proper usage of funcall
. I have this function:
(defun frame-add-slot (frame slot)
(push (list slot) (rest (assoc frame *frames*))))
and I’m trying to get this other function to call it.
(defun frame-add-subframe (superframe subframe)
(let ((res (push (list subframe) (rest *frames*))))
(funcall (frame-add-slot) subframe 'ako))))
However, when I try to pass it two arguments in this fashion, clisp tells me the called function receives too few arguments. What am I doing wrong? *Frames*
is my knowledge base. It looks like this:
(setf *frames* '((mammal
(eats
(:value meat)
(:if-needed (food))))
(4-legged-animal
(ako
(:type mammal)
(:default beings))
(blood
(:type warm-blooded)))
(husky
(ako
(:type dog))
(origin
(:value alaska)
(:default north-america))
(roots
(:value unknown)))
(dog
(ako
(:type 4-legged-animal))
(exterior
(:value furry)
(:default skin)))
(abner
(isa
(:type husky)
(:default dog))
(shape
(:weight 40-lbs)
(:height 20-inches)
(:color brown))
(acts
(:demeanor friendly)
(:sometimes rough)))
(gypsy
(isa
(:default dog))
(acts
(:demeanor unpredictable))
(shapes
(:weight 45-lbs)
(:color black-and-brown)))))
Rainer Joswig
137k10 gold badges221 silver badges346 bronze badges
asked Sep 13, 2011 at 7:12
1
Can you explain why you need FUNCALL
?
FRAME-ADD-SLOT
is a normal named function and you can call it as such — without FUNCALL
.
(defun frame-add-subframe (superframe subframe)
(let ((res (push (list subframe) (rest *frames*))))
(frame-add-slot subframe 'ako))))
Literal data
Later in your code you set *frames*
to constant literal data. In your functions you are modifying this constant data. In standard Common Lisp the effect of these modifications is undefined. You need freshly allocated data structures — these can be modified without problems. See for example the function COPY-TREE
to recursively make a fresh copy of a deeply nested list. The result of COPY-TREE
can be modified.
answered Sep 13, 2011 at 8:08
Rainer JoswigRainer Joswig
137k10 gold badges221 silver badges346 bronze badges
There’s several problems with your frame-add-subframe
function:
- Why are you using
funcall
in this instance? You should be able to directly callframe-add-slot
:(frame-add-slot subframe 'ako)
- If the
funcall
usage is merited, then you’d use it like this:(funcall #'frame-add-slot subframe 'ako)
- I presume that instead of specifying
'ako
hardcoded, you meant to useres
somehow? That variable is unused.
answered Sep 13, 2011 at 7:46
C. K. YoungC. K. Young
220k46 gold badges383 silver badges436 bronze badges
In (funcall (frame-add-slot) subframe 'ako))))
, you are calling frame-add-slot by putting parens around it.
Try (funcall #'frame-add-slot subframe 'ako)
.
answered Sep 13, 2011 at 7:42
Paul NathanPaul Nathan
39.6k29 gold badges112 silver badges213 bronze badges
0
We have installed the latest Autodesk 2021 and one of our scripts (a modified @Lee Mac) which strips and formats some input text now fails. This script runs perfectly on 2019 and below. I can’t seem to work out why there is a difference.
I have substituted the original «vl-catch-all-apply» to «apply» so I could catch the error. The error is:
Too few arguments
This occurs as it hits the ‘(lambda function call. The code is below with the call:
(defun GDD:removeinfo (rgx str)
(if
(null
(vl-catch-all-error-p
(setq str
(apply
'(lambda nil
(vlax-put-property rgx 'global actrue)
(vlax-put-property rgx 'multiline actrue)
(vlax-put-property rgx 'ignorecase acfalse)
(foreach pair
'(
("\032" . "\\\\\\\\")
("\n" . "\\\\P")
("$1" . "\\\\(\\\\[ACcFfHKkLlOopQTW])|\\\\[ACcFfHKkLlOopQTW][^\\\\;]*;|\\\\[ACcFfKkHLlOopQTW]")
("$1$2/$3" . "([^\\\\])\\\\S([^;]*)[/#\\^]([^;]*);")
("$1$2" . "\\\\(\\\\S)|[\\\\](})|}")
("$1" . "[\\\\]({)|{")
("\\$1$2$3" . "(\\\\[ACcFfHKkLlOoPpQSTW])|({)|(})")
("\\\\" . "\032")
("" . "(?:.*\\n)*Const\\s+.*\\n")
("" . "\\w\\w\\d?\\s+\\d+\\s\\d+-\\d+-\\d+")
("" . "^\\s+\\n")
)
(vlax-put-property rgx 'pattern (cdr pair))
(setq str (vlax-invoke rgx 'replace str (car pair)))
)
)
)
)
)
)
str
)
)
The call to this function is below. I’ve checked the «input» and it is identical to the 2019 version that works and the str is populated properly using the vlisp (vscode) debugger. I’ve run the same code and input through both and only the 2021 version fails?
(setq input (GDD:removeinfo (vlax-get-or-create-object "VBScript.RegExp") input))
I’m not that familiar with LISP and I’m stuck. Thanks for your help.
asked May 22, 2020 at 6:37
Rob MascaroRob Mascaro
8018 silver badges16 bronze badges
Assuming <FUN>
stands for:
'(lambda nil
(vlax-put-property rgx 'global actrue)
(vlax-put-property rgx 'multiline actrue)
(vlax-put-property rgx 'ignorecase acfalse)
(foreach pair
'(("\032" . "\\\\\\\\")
("\n" . "\\\\P")
("$1" . "\\\\(\\\\[ACcFfHKkLlOopQTW])|\\\\[ACcFfHKkLlOopQTW][^\\\\;]*;|\\\\[ACcFfKkHLlOopQTW]")
("$1$2/$3" . "([^\\\\])\\\\S([^;]*)[/#\\^]([^;]*);")
("$1$2" . "\\\\(\\\\S)|[\\\\](})|}")
("$1" . "[\\\\]({)|{")
("\\$1$2$3" . "(\\\\[ACcFfHKkLlOoPpQSTW])|({)|(})")
("\\\\" . "\032")
("" . "(?:.*\\n)*Const\\s+.*\\n")
("" . "\\w\\w\\d?\\s+\\d+\\s\\d+-\\d+-\\d+")
("" . "^\\s+\\n"))
(vlax-put-property rgx 'pattern (cdr pair))
(setq str (vlax-invoke rgx 'replace str (car pair)))))
The code you posted, rewritten more compactly, looks as follows:
(defun GDD:removeinfo (rgx str)
(if (null (vl-catch-all-error-p
(setq str (apply <FUN>))))
str))
In particular, the call to APPLY
has only one argument:
(apply <FUN>)
APPLY
in Autolisp is a function of 2 parameters: a function, and a list of arguments.
The intent is that:
(apply f '(0 1))
… is evaluated as-if you called (f 0 1)
, but with possibility of building the list of arguments at runtime.
You only gave one argument to apply
in your code, so you need to also pass a list of arguments.
In your case, that would be the empty list:
(apply <FUN> nil)
answered May 22, 2020 at 13:10
coredumpcoredump
37.8k5 gold badges43 silver badges77 bronze badges
3
Instead of changing vl-catch-all-apply
to apply
in order to see the error, simply output the error as part of the else branch of the if
statement, for example:
(if
(null
(vl-catch-all-error-p
(setq str
(vl-catch-all-apply
...
)
)
)
)
str
(prompt (strcat "\nError: " (vl-catch-all-error-message str))) ;; Output error and return nil
)
Aside, whilst this code is relatively trivial, I’m not sure whether I agree with you appropriating 95% of the code for my function and stripping off my heading and author prefix.
answered May 22, 2020 at 15:50
Lee MacLee Mac
15.7k6 gold badges33 silver badges80 bronze badges
4
Whoever wrote that code seemed not to be aware of the progn
operator.
So that is to say, if we want to evaluate multiple expressions e1
, e2
, … for the sake of a side effect that they produce, we do not have to do this:
;; wrap expressions in a dummy lambda and apply empty arg list to it
(apply (lambda () e1 e2 ...) nil)
We can just write this:
(progn e1 e2 ...)
That still leaves us with a strange code smell which looks like this:
(setq str (progn .... (setq str value)))
The code is assigning the variable str
twice with the same value. The deeply nested (setq str value)
puts value
into str
and then yields that value as a result. It’s the last expression of the progn
(originall, of the lambda
) and so that value is also returned. Then the outer setq
wastefully stores it in str
again. We just need one or the other:
;; set str as the side effect of the last form in the
;; progn; also yield that value.
(progn e1 e2 ...(setq str value))
;; assign value yielded from progn to str, then also
;; yield that value.
(setq str (progn e1 e2 ... value))
answered May 28, 2020 at 14:44
KazKaz
56.2k9 gold badges102 silver badges149 bronze badges
1
@Shneuph wrote:
Kent — I still don’t fully understand the differences between C: functions and ones w/o the «C:» and how they are used differently. I do know that some built-in autolisp functions have «optional» arguements which you cannot have with a C: function. To get around this I have something like C:Symbol (P1 P2 P3 Poptional1 Poptional2 / etc etc)
Then in the lisp I just test to see if Poptional1 or Poptional2 are nil and if so adjust appropritely. Then call the function like
(C:Symbol «p1» «p2» «p3» «po1» nil)
In the lisp it will have:
(if Poptional1
(do this)
);if(if Poptional2
(do this)
);if
Things defined with the C:, e.g. (defun C:Whatever… are commands [I assume that’s what the C stands for], though they are also sometimes referred to as routines, and their command names can be typed at the Command: prompt line without parentheses, as with other commands, or put in menu items, scripts, etc. But since they’re not native AutoCAD commands, they can’t be used in a Lisp (command) function if they need to be called from inside other Lisp routines, but need to be in parentheses with the C: included. They’re not supposed to have arguments, though that doesn’t seem to be absolute [see Message 4]. But they can have localized variables.
Things defined without the C:, e.g. (defun Whatever… are usually called routines, or sub-routines, or maybe some other things. [Sometimes someone will refer to one as a function, but that has the potential to be confused with what are properly called functions: those listed in the AutoLISP Reference, including (defun) itself.] You can’t type their bare names at the Command: prompt line, though you can type them there included in parentheses, but usually they are used from within other routines, similarly to the way AutoLISP functions are used. They can have arguments [though they don’t always need to], as well as localized variables.
In your example, if you’re going to use it by calling it from inside another Lisp function/routine, I would just define it as a routine/sub-routine, not as a command, since the C: isn’t doing anything for you:(defun Symbol (P1 P2 P3 Poptional1 Poptional2 / etc etc)
and use it the way functions/routines/sub-routines are used:
(Symbol p1 p2 p3 po1 nil)
[Note no quotation marks around the arguments — I’m assuming those are variables holding some kind of value, and not actually text strings.]
Kent Cooper, AIA
Please, help me with this program to toggle multiple wire numbers.
Error: too few arguments…
Private Sub ToggleWireNum()
Dim x As AcadEntity
Dim blk As AcadBlock
On Error Resume Next
For Each x In ThisDrawing.ModelSpace
If TypeName(x) = «IAcadLine» Then
ThisDrawing.SetVariable «USERS1», x.Handle
WDCommand («(setq return (c:ace_get_wnum (handent (getvar «»USERS1″»))))» & vbCr)
WDCommand («(setvar «»USERS2″» (car return))» & vbCr)
If Len(ThisDrawing.GetVariable(«USERS2»)) > 0 Then
WDCommand («(setq en (cadr return))» & vbCr)
WDCommand («(c:ace_toggle_inline en)» & vbCr)
End If
End If
Next x
End Sub
Public Sub WDCommand(Command As String)
‘ This function calls lisp function
ThisDrawing.SendCommand (Command)
End Sub
Public Sub WDForceStart()
Call WDCommand(«(if(not wd_load)(if(setq x(findfile «»wd_load.lsp»»))(load x)))(wd_load)» & vbCr)
End Sub
Кто бы ни писал этот код, похоже, не знал о progn
оператор.
То есть, если мы хотим оценить несколько выражений e1
, e2
,… из-за побочного эффекта, который они производят, нам не нужно этого делать:
;; wrap expressions in a dummy lambda and apply empty arg list to it
(apply (lambda () e1 e2 ...) nil)
Мы можем просто написать это:
(progn e1 e2 ...)
Это по-прежнему оставляет нам странный запах кода, который выглядит так:
(setq str (progn .... (setq str value)))
Код присваивает переменную str
дважды с одинаковым значением. Глубоко вложенный(setq str value)
ставит value
в str
а затем возвращает это значение в качестве результата. Это последнее выражениеprogn
(первоначально из lambda
), и это значение также возвращается. Тогда внешнийsetq
расточительно хранит это в str
еще раз. Нам просто нужно одно или другое:
;; set str as the side effect of the last form in the
;; progn; also yield that value.
(progn e1 e2 ...(setq str value))
;; assign value yielded from progn to str, then also
;; yield that value.
(setq str (progn e1 e2 ... value))