I need to implement the function which creates a list from arguments that are passed to the function.
Here's my code:
(defun lstbuilder (&rest args)
(if (eq (car args) NIL)
NIL
(cons (car args)
(lstbuilder (cdr args)))))
This function doesn't work correctly.
Results:
(lstbuilder 'a 'b 'c 'd 'e) ;expected (a b c d e)
(a (b c d e)) ;result
Style
Please use standard Lisp formatting. Use an editor will help you to indent Lisp code.
Don't put a parenthesis alone on a line. This just wastes space without benefit.
Longer names get a - between the words: list-builder.
Don't use carand cdr for list operations. Use first and rest.
The end of list test is endp.
Example:
(defun list-builder (&rest args)
(if (endp args)
nil
(cons (first args)
(apply #'list-builder (rest args)))))
Since the args variable is already a list, we can just copy it:
(defun list-builder (&rest args)
(copy-list args))
Or we can just reuse the list function, which already creates a list of its args:
(setf (symbol-function 'list-builder)
#'list)
You need to use (apply #'lstbuilder (cdr args)) in order to "splat" the list's contents as the function call arguments.
Related
I found myself calling lots of methods whose first argument is a complex object from a given class.
Whilst with-slots and with-accessors are useful, generic methods cannot be bound in this way. So I thought: if we could locally curry any functions, slots + accessors + generic functions + functions could all be addressed with the same construct.
Example of code I want to clean up:
(defun clox-string (scanner)
"Parse string into a token and add it to tokens"
(loop while (and (char/= #\" (peek scanner))
(not (at-end-p scanner)))
do
(if (char= #\Newline (peek scanner)) (incf (line scanner))
(advance scanner)))
(when (at-end-p scanner)
(clox.error::clox-error (line scanner) "Unterminated string.")
(return-from clox-string nil))
(advance scanner) ;; consume closing "
(add-token scanner 'STRING (subseq (source scanner)
(1+ (start scanner))
(1- (current scanner)))))
This would be cleaner (I'm imitating this in CL https://craftinginterpreters.com/scanning.html#reserved-words-and-identifiers but I often end up with more verbose and less readable code than in Java - specially when using this classes a lot). As in CL methods don't belong to classes you end up declaring such arguments over and over. This would be a bit better:
(defun clox-string (scanner)
"Parse string into a token and add it to tokens"
(let-curry scanner (peek at-end-p line source start current advance add-token)
(loop while (and (char/= #\" (peek))
(not (at-end-p)))
do
(if (char= #\Newline (peek)) (incf (line))
(advance)))
(when (at-end-p)
(clox.error::clox-error (line) "Unterminated string.")
(return-from clox-string nil))
(advance) ;; consume closing "
(add-token 'STRING (subseq (source)
(1+ (start))
(1- (current)))))
sketch of macro (not working):
;; Clearly not as I don't understand macros very well :) non-working code:
(defmacro let-curry (obj functions &body body)
"Locally curry all functions"
(let ((fn (gensym)))
`(flet (loop
for ,fn in ,functions
collect (list ,fn (&rest args)
(funcall ,fn ,obj args)))
,#body)))
EDIT (ADD): Notice that scanner is a class; start, source, line, etc., accessors to the slots with the same name; add-token a generic function of more than one argument, advance a generic method of one argument:
(defclass scanner ()
((source
:initarg :source
:accessor source)
...
(...)))
(defmethod advance ((scanner scanner)) ...)
(defmethod add-token ((scanner scanner) token-type) ...)
Simpler Example with error:
;; With
(defun add (x y) (+ x y))
(defun mul (x y) (* x y))
;; I want to have this:
(let-curry 1000 (add mul)
(print (add 3))
(print (mul 3)))
;; expanding to:
(flet ((add (y) (add 1000 y))
(mul (y) (mul 1000 y)))
(print (add 3))
(print (mul 3)))
;; but instead I'm getting:
Execution of a form compiled with errors.
Form:
(FLET (LOOP
FOR
#1=#:G777
IN
(ADD MUL
)
COLLECT
(LIST #1#
(&REST ARGS)
(FUNCALL #1# 1000 ARGS)))
(PRINT (ADD 3))
(PRINT (MUL 3)))
Compile-time error:
The FLET definition spec LOOP is malformed.
[Condition of type SB-INT:COMPILED-PROGRAM-ERROR]
Thanks! The basic question is: is it possible to make such macro work?
Your version didn't expand to what you wanted but:
(flet (loop for #:g8307 in (add mul) collect (list #:g8307 (&rest args) (funcall #:g8307 1000 args)))
(print (add 3)) (print (mul 3)))
Now the loop needs to be done at macro expansion time.
Here is a working version:
(defmacro let-curry (obj (&rest functions) &body body)
"Locally curry all functions"
`(flet ,(loop for fn in functions
collect `(,fn (&rest args)
(apply #',fn ,obj args)))
,#body))
;; test it using add and mul from OP
(macroexpand-1 '(let-curry 10 (add mul) (list (add 5) (mul 5))))
;; ==>
(flet ((add (&rest args) (apply #'add 10 args))
(mul (&rest args) (apply #'mul 10 args)))
(list (add 5) (mul 5)))
(let-curry 10 (add mul) (list (add 5) (mul 5)))
;; ==> (15 50)
Using gensym is only needed if you are in danger of shadowing/colliding something or to ensure evaluation order is least surprising, but in your case you actually want to shadow the original names with the curried version so it makes sense to just use the original name.
If you want to have more than one argument you should use apply
since you know the function is in the function namespace you need to call #'symbol instead of symbol.
I've done (&rest functions) instead of functions in the prototype that with bad usage (not a list) you get a compile time error and it is more preciese.
I am having the following trouble: when trying to use APPLY function with a MAPCAR call, the lambda function passed to APPLY, which contains only one parameter, the list returned by MAPCAR, gives the following error :
*** - EVAL/APPLY: too many arguments given to :LAMBDA
The following code identifies if a heterogenous list has the last atom at any level a numerical atom.
(DEFUN hasLastNumeric (L)
(COND
((NUMBERP L) T)
((ATOM L) NIL)
((LISTP L)
(APPLY #'(LAMBDA (Lst)
(COND ((EQ (LAST Lst) T) T)
(T NIL)))
(MAPCAR 'hasLastNumeric L)))))
(WRITE (hasLastNumeric '(1 2 5)))
You don't need APPLY. Why would you use it? Remember: APPLY calls a function and uses the provided list as the list of arguments.
MAPCAR returns a list.
(let ((foo (mapcar #'1+ '(1 2 3 4))))
(cond ((eql (last foo) ...) ...)
...))
Check also what last actually returns...
If you call a function eg. (#'(lambda (a b) (+ a b)) 2 3) there is a requirement that the number of arguments fits the number of provided arguments. When using apply the requirements are the same so (apply #'(lambda (one) ...) lst) require that lst is only one element list like '(a), but it cannot be '() or '(a b). The only way to support variable number of arguments you need to use &rest arguments eg. (apply #'(lambda (&rest lst) ...) '(a b))
Looking at the logic I don't understand it. You want to return t when you have encountered a list with the last element as a number but also searched list elements on the way and returned early had you found them. It should be possible without the use of last at each step. eg.
(defun has-a-last-numeric (lst)
(labels ((helper (lst)
(loop :for (e . rest) :on lst
:if (and (null rest) (numberp e))
:do (return-from has-a-last-numeric t)
:if (listp e)
:do (helper e))))
(helper lst)))
For a college project we're working on learning scheme however we've been thrown into a difficult assignment with little knowledge. We're given certain functions like "'let", "'cond", "and'" etc. and asked to add macros.
(define eval
(λ (e env)
(cond ((symbol? e) (lookup-var e env))
((not (list? e)) e) ; non-list non-symbol is self evaluatory
;; special forms go here:
((equal? (car e) 'λ) ; (λ VARS BODY)
(let ((vars (cadr e)) (body (caddr e)))
(list '%closure vars body env)))
((equal? (car e) 'if)
(eval_ (if (eval_ (cadr e) env) (caddr e) (cadddr e)) env))
((equal? (car e) 'quote) (cadr e))
;; Add More Macros Here:
;; ((equal? (car e) 'let) xxx)
;;((equal? (car e) 'cond) xxx)
;;((equal? (car e) 'and) xxx)
;((equal? (car e) 'or) xxx)
(else (let ((eeoe (map (λ (e0) (eval_ e0 env)) e)))
(apply_ (car eeoe) (cdr eeoe)))))))
So basically we're asked to fill in the blanks, where the ';;' comments are.
I tried doing the 'cond part and got this
((equal? (car e) 'cond)
(env (cdr e) env))
But I have no idea if it's correct (very little knowledge of scheme). Any help in figuring this out will be much appreciated. Thanks.
It's not really macros but special forms you are adding. If you know that let is just syntax sugar for anonymous function call. eg.
(let ((a expr1) (b expr2)) body ...)
is supported in you evaluator already if you change and evaluate:
((lambda (a b) body ...) expr1 expr2)
To get you going let works like this:
(let ((bindings (cadr e))
(body (cddr e)))
(eval_ `((lambda ,(map car bindings) ,#body) ,#(map cadr bindings))))
Now real macros you would introduce a new type of %closure so that whenever you find it as operator you bind the symbols not their evaluation, run the evaluator on it as if it was a function, then do _eval on the result. Then instead of implementing cond and let you could just add a function on how to rewrite it down to something you already support. Thus you could make let like this:
((lambda (let)
(let ((a expr1) (b expr2))
(cons a b)))
(~ (bindings . body) `((lambda ,(map car bindings) ,#body) ,#(map cadr bindings))))
It assumes you have quasiquote and map, but it could easily be implemented without these with more verbose code. ~ is just chosen randomly to be the macro version of λ. When evaluated it makes perhaps a %mclosure structure and you need to handle it specially to not evaluate its arguments, but evalaute the result. From that on you could support the special forms by having predefined %mclosure in the boot environment.
Can you help me? I don't understand this code's part
(lambda (this &rest args) ;; Adds The THIS Argument
(apply (get-slot this
method-name)
(append (list this)
args))))
Rewrite Method
;;;; This Function Add The Argument This To The Method
(defun rewrite-method (method-spec)
(list 'lambda (append (list 'this) ;; Add The THIS Argument
(second method-spec))
(cons 'progn (rest (rest method-spec))))) ;; Eval All The Method's Body
;;;; Method-Process
;;;; Function Used To Process Methods, Rewriting Them To
;;;; Lisp Functions
(defun method-process (method-name method-spec)
(setf (fdefinition method-name) ;; Associate The Lambda To The Method's Name
(lambda (this &rest args) ;; Adds The THIS Argument
(apply (get-slot this
method-name)
(append (list this)
args))))
(eval (rewrite-method method-spec))) ;; Returned Value
It takes an arglist (foo bar baz) and adds a this parameter: (this foo bar baz).
Thus a function (lambda (foo bar baz) ...) then is (lambda (this foo bar baz) ...).
This is useful in implementation of single-inheritance object-oriented languages where the first argument is the object which receives a message and other arguments. Here the parameter this is the receiving object.
Note that the call to APPLY could be improved. (apply function (append (list this) args)) is just (apply function this args).
I realize that a function can be referenced using #'PRINT to reference the PRINT function. If we have a list S where the first element is 'PRINT , can we reference this using #(car S) ?
Say I'm looking at a list where the elements in the list are atoms which are the names of some functions. Currently, I can do this:
(defun aritheval (S)
(funcall
(cond
((eq '+ (car S)) #'+)
((eq '- (car S)) #'-)
((eq '* (car S)) #'*)
((eq '/ (car S)) #'/)
)
'2
'3
)
)
This function would compute 2+3, 2-3, 2*3 or 2/3 depending on the first element in list S. S contains the names of these functions.
#(car S) makes no sense. The syntax exists but means a vector with symbols CAR and S.
use
(funcall (first somelist) someargument)
or
(apply (first somelist) a-list-of-arguments)
Your function is non-Lispy formatted.
Trailing parentheses are not used in proper Lisp code. You also don't need to quote numbers. Numbers are self-evaluating, they evaluate to themselves. Also we now may prefer FIRST over CAR and REST over CDR. The functions do the same, but the names are better. Whenever we deal with simple lists, FIRST, SECOND, THIRD, ... and REST are used.
(defun aritheval (S)
(funcall (cond ((eq '+ (car S)) #'+)
((eq '- (car S)) #'-)
((eq '* (car S)) #'*)
((eq '/ (car S)) #'/))
2 3)))
Since symbols can be used as names for global functions, above is not necessary.
This function below does the same, given the mapping from symbol to function is the same.
(defun aritheval (s)
(funcall (first s) 2 3)))