Macro to simplify recursive function syntax - scheme

As I learn scheme and racket I find myself repeating this pattern again and again. Where I have a recursive function where some of the parameters to the function change but some of the parameters do not. I build an outer function that takes all the parameters and within that define an inner function that takes only the changing parameters and recur on that.
As a concrete example heres a case based somewhat on a function exercise in "The Little Schemer"
;inserts an item to the right of an element in a list
(define (insert-to-right new old lat)
(define (insert-to-right lat)
(cond
[(null? lat) lat]
[(eq? old (car lat) ) (cons old (cons new (cdr lat)))]
[else (cons (car lat) (insert-to-right (cdr lat)))]))
(insert-to-right lat))
Is it possible to build a macro define* and an operator (for example a vertical bar) such that I would type:
(define* (insert-to-right new old | lat)
(cond
[(null? lat) lat]
[(eq? old (car lat) ) (cons old (cons new (cdr lat)))]
[else (cons (car lat) (insert-to-right (cdr lat)))]))
and this would then expand into the first form with all the parameters being passed to the outer function but only the parameters after the vertical bar being passed to the inner loop.

You could write such a macro, but you could also just use named let:
(define (insert-to-right new old lat)
(let loop ([lat lat])
(cond
[(null? lat) lat]
[(eq? old (car lat)) (cons old (cons new (cdr lat)))]
[else (cons (car lat) (loop (cdr lat)))])))

After playing around I've built a macro that does what I want.
(define-syntax-rule
(define* (function-name (outer-var ...) (inner-var ...)) expr ...)
(define (function-name outer-var ... inner-var ...)
(define (function-name inner-var ...)expr ...)
(function-name inner-var ...)))
(define* (insert-to-right [new old] [lat])
(cond
[(null? lat) lat]
[(eq? old (car lat) ) (cons old (cons new (cdr lat)))]
[else (cons (car lat) (insert-to-right (cdr lat)))]))
> (insert-to-right 11 3 '(1 2 3 4 5 6))
'(1 2 3 11 4 5 6)
In the define* statement it doesn't use a separator between the inner and outer parameters (as I originally tried to do) but puts the inner and outer parameters in the define* statement into separate lists which I think is more idiomatic Scheme/Racket.

You should not use macros to do this. This is a textbook case for higher-order functions; in particular, I believe your example can be written with pair-fold-right from SRFI-1. Untested code (I hope this is right):
(define (insert-to-right new old lat)
(pair-fold-right (lambda (pair rest)
(if (eq? (car pair) old)
(cons (car pair)
(cons new rest))
pair))
'()
lat))
;;; Example implementation of pair-fold-right, just for one list—your Scheme system
;;; probably has this as a library function somewhere
(define (pair-fold-right fn init list)
(if (null? list)
init
(fn list (pair-fold-right fn init (cdr list)))))

Related

Scheme with postfix

Does anyone can help me to deal with the problem?
I tried for many times, but it still has the error information.
This is my code(scheme)
Thanks!!!
(define (postfix l s)
(cond(
((null? l)(car s))
(else (postfix (cdr l) update-s((car s)))))))
(define (update-s x s)
(cond(((number? x) (cons x s))
(else (cons (eval '(x (car s) (cadr s))) (scheme-report-environment 5) (cdr(cdr s)))))))
And this is the error inform:
else: not allowed as an expression in: (else (postfix (cdr l) update-s ((car s) s)))
Next time, don't forget to add a description of your problem (what should this code do?), expected inputs and outputs, and a version of Scheme you use.
You should also use better names for variables (no l, s, x) and describe their meaning and expected type in your question.
If I understand correctly, you were trying to create a calculator which uses reverse Polish/ postfix notation, where:
l is a list of numbers or symbols
s is a stack with results, represented as a list of numbers
x can be a number or symbol representing some function
From (scheme-report-environment 5) I guess you use r5rs Scheme.
Now some of your errors:
you should define update-s before function postfix
your cond has some additional parentheses
if cond has only two branches, you should use if instead
this part (postfix (cdr l) update-s((car s))) should be (postfix (cdr l) (update-s (car l) s)
(cdr(cdr s)) should be (cddr s)
as for eval, I understand why it's here, you were trying to get a function from the symbol, but you should be always careful, as it can also evaluate code provided by user. Consider this example: (postfix '(1 2 (begin (write "foo") +)) '()). Maybe it could be better to don't expect this input: '(1 2 +), but this: (list 1 2 +) and get rid of eval.
The whole code:
(define (update-s object stack)
(if (number? object)
(cons object stack)
(cons ((eval object (scheme-report-environment 5))
(car stack) (cadr stack))
(cddr stack))))
(define (postfix lst stack)
(if (null? lst)
(car stack)
(postfix (cdr lst)
(update-s (car lst) stack))))
Example:
> (postfix '(1 2 +) '())
3
Solution without eval with different input:
(define (update-s object stack)
(if (number? object)
(cons object stack)
(cons (object (car stack) (cadr stack))
(cddr stack))))
(define (postfix lst stack)
(if (null? lst)
(car stack)
(postfix (cdr lst)
(update-s (car lst) stack))))
Example:
> (postfix (list 1 2 +) '())
3

Invalid Function Elisp

I have the following code:
(defun rember
(lambda (a lat)
(cond
((null lat) '())
(else (cond
((eq (car lat) a) (cdr lat))
(else (rember a
(cdr lat))))))))
(rember 2 '(4 5 6 7))
When I run this using C-x C-e, I get the following error:
Invalid function: (lambda (lambda (a lat) (cond ((null lat) (quote nil)) (else (cond\
((eq (car lat) a) (cdr lat)) (else (rember a (cdr lat))))))) nil)
I don't know why. Can someone help?
Looks like you're mixing up Lisp and Scheme syntax for defining functions. In Lisp, as you're using defun instead of defvar, it is already implied that you're defining a function, so you don't need to wrap the code in lambda:
(defun rember (a lat)
(cond
((null lat) '())
(else (cond
((eq (car lat) a) (cdr lat))
(else (rember a
(cdr lat)))))))
(Next you'll find that else is not treated specially inside cond in Emacs Lisp, so you'll need to use t instead.)

Inserting word beside another word starting from the end of list

I have code which is inserting new word on the right side of choosen word
(define insertR
(lambda (new old lst)
(cond
((null? lst) (lst))
(else (cond
((eq? (car lst) old)
(cons old
(cons new (cdr lst))))
(else (cons (car lst)
(insertR new old
(cdr lst)))))))))
i need to make it insert that word beside first appearance of word starting from the end of list. Tried to work with reverse but could not get that to work.
There are two strategies you can take to add it next to the last occurence.
The first is to use a helper and start off with the reverse list. This is very simple and my preferred solution.
(define (insert-by-last-match insert find lst)
(let loop ((lst (reverse lst)) (acc '()))
(if (null? lst)
acc
(let ((a (car lst)))
(if (equal? a find)
(append (reverse (cdr lst))
(list* find insert acc))
(loop (cdr lst) (cons a acc)))))))
The other one is kind of obscure. Whenever you find the element you replace last-match with a callback that replaces the computation since it was made and until it gets called with the replacement and the rest of the list, which of course is the correct result. The work done until the end of the list is simply discarded since it is not used, but we do it since we are not sure if we are going to find a later one and then all the work uptil that is of course included in the result.
(define (insert-by-last-match insert find lst)
(define (helper lst last-match)
(if (null? lst)
(last-match)
(let* ((a (car lst)) (d (cdr lst)))
(cons a
(if (equal? a find)
(let/cc k
(helper d (lambda () (k (cons insert d)))))
(helper d last-match))))))
(helper lst (lambda () lst)))
call/cc (or its variant let/cc) is often described as time travel or advanced goto. It is not very intuitive. Here is a CPS version:
(define (insert-by-last-match insert find lst)
(define (helper lst last-match k)
(if (null? lst)
(last-match)
(let* ((a (car lst)) (d (cdr lst)) (k2 (lambda (v) (k (cons a v)))))
(if (equal? a find)
(helper d (lambda () (k2 (cons insert d))) k2)
(helper d last-match k2)))))
(helper lst (lambda () lst) (lambda (v) v)))
Basically this is the same as the previous only that here I have written the CPS code and with the let/cc version the implementation does it for me and I get to use k exactly where I need it. In this version you see there is no magic or time travel but the execution that should happen later is simply replaced at a point.
Write in a similar way insertL and apply it to the reversed list.
And reverse the result. Then you will have an insertion beside first appearance of word starting from the end of list
(define insertL
(lambda (new old lst)
(cond ((null? lst) '())
((eq? (car lst) old) (cons new lst))
(else (cons (car lst) (insertL new old (cdr lst)))))))
(define last-insertR
(lambda (new old lst)
(let* ((rlst (reverse lst))
(result (insertL new old rlst)))
(reverse result))))
test:
(last-insertR 'aa 'a '(b c d a h i a g))
;; '(b c d a h i a aa g)
By the way, the beauty of cond is that you can put the conditions always at the beginning - listed one under the other.
So one can write your insertR nicer as:
(define insertR
(lambda (new old lst)
(cond ((null? lst) '())
((eq? (car lst) old) (cons old (cons new (cdr lst))))
(else (cons (car lst) (insertR new old (cdr lst)))))))

Recurring with anonymous functions Common Lisp vs. Scheme

I'm working through the Little Schemer and I'm trying to convert all of the answers into Common Lisp.
In chapter 8, anonymous functions are discussed, as well as returning anonymous functions.
For example:
(define insertL-f
(lambda (test?)
(lambda (new old l)
(cond
((null? l) (quote ()))
((test? (car l) old) (cons new l)))
(else (cons (car l) ((insertL-f test?) new old (cdr l))))))))
My code:
(defun insertL-f (test)
(lambda (new old l)
(cond
((null l) '())
((funcall test (car l) old) (cons new l))
(t (cons (car l) (insertL-f test) new old (cdr l))))))
The problem is the last line of the second block of code. I get the error "too many arguments for cons" but I can't add an extra pair of parentheses like the Scheme code does. Is this style of recursion just not possible in Common Lisp?
(defun insertL-f (test)
(lambda (new old l)
(cond
((null l) '())
((funcall test (car l) old) (cons new l))
(t (cons (car l)
(funcall (insertL-f test)
new
old
(cdr l)))))))
insertL-f returns a function and in your Scheme version you apply it while in CL have flattened the list instead if applying it with funcall However it seems like the function that is to be returned is equal to the one it fetches so you can cache it by defining it locally with labels:
(defun insert-l-f (test)
(labels ((func (new old l)
(cond
((null l) '())
((funcall test (car l) old) (cons new l))
(t (cons (car l) (func new old (cdr l)))))))
#'func))
The same in Scheme using local define (which really is aletrec with flatter syntax):
(define (insert-l-f test?)
(define (func new old l)
(cond
((null? l) (quote ()))
((test? (car l) old) (cons new l)))
(else (cons (car l) (func new old (cdr l)))))
func)

Using match in chez scheme

I'm trying to learn how to use match in scheme. I sort of understand how it works with really short problems (ie: defining length is just two lines) but not with problems where there's more than one input, and helper programs. For example, here's a popular way of defining union:
(define ele?
(lambda (ele ls)
(cond
[(null? ls) #f]
[(eq? ele (car ls)) #t]
[else (ele? ele (cdr ls))])))
(define union
(lambda (ls1 ls2)
(cond
[(null? ls2) ls1]
[(ele? (car ls2) ls1) (union ls1 (cdr ls2))]
[else (union (cons (car ls2) ls1) (cdr ls2))])))
How do you do this using match in both programs? (or would you even need two programs?)
the first one could be implemented like that:
(define ele?
(lambda (a b)
(let ((isa? (lambda (x) (eq? (car x) a))))
(match b [(? null?) #f]
[(? isa?) #t]
[_ (ele? a (cdr b))]))))
then the second is easy
(define uni
(lambda (ls1 ls2)
(let ((carinls2? (lambda (x) (ele? (car x) ls1))))
(match ls2 [(? null?) ls1]
[(? carinls2?) (uni ls1 (cdr ls2))]
[_ (uni (cons (car ls2) ls1) (cdr ls2))]))))
maybe there is a smarter way to avoid these one argument let lambdas but i'm still learning ;)

Resources