How does eq? in Racket work with symbols and number? - racket

Racket document told me “eq? return #t if v1 and v2 refer to the same object”, but two fixnums that are = are also the same according to eq?, = “returns #t if all of the arguments are numerically equal”. I can't find any message about “numbers” and “symbols”, but in the example I found :
> (eq? 'yes 'yes)
#t
This is contradictory to the above, because it was never mentioned above that symbol was special, so 'yes and 'yes are not the same.
This one makes me even more confused :
> (eq? (expt 2 100) (expt 2 100))
#f
> (eq? (* 6 7) 42)
#t
if numbers are tested numerically, then (eq? (expt 2 100) (expt 2 100)) should return #t, otherwise, numbers are tested by refer, then (eq? (* 6 7) 42) should return #f, so I guess both of the above situations are not right...
Why?!

(expt 2 100)
is too big to be a fixnum. Let's try to evaluate:
(expt 2 100) ; => 1267650600228229401496703205376
(fixnum? (expt 2 100)) ; => #f
(expt 2 10) ; => 1024
(fixnum? (expt 2 100)) ; => #t
This is because large numbers are allocated in several memory cells (think of them as a list of groups of digits).
On the other hand, each symbol, when read, is “internalized”. This means that the first time it is read, a new symbol value is created for it. Subsequently, when it is read again, the system checks if a symbol with that name is already present, and in this case the old symbol value is returned, without creating any new object in memory. So:
(eq 'yes 'yes) ; => #t
since what are apparently two different symbols with the same name are in effect the same object in memory.

This is to give additional information in addition to #Renzo's answer
how can I check if a data type is “internalized”
The answer is that it's complicated.
One factor is the reader:
Symbols, keywords, strings, byte strings, regexps, characters, and numbers produced by the reader in read-syntax mode are interned, which means that such values in the result of read-syntax are always eq? when they are equal? (whether from the same call or different calls to read-syntax). Symbols and keywords are interned in both read and read-syntax mode. Sending an interned value across a place channel does not necessarily produce an interned value at the receiving place. See also datum-intern-literal and datum->syntax.
So (eq? (expt 2 100) (expt 2 100)) returns #f because (expt 2 100) is needed to be computed at runtime. On the other hand, (eq? 1267650600228229401496703205376 1267650600228229401496703205376) returns #t because the value is apparent at reading time, allowing Racket to intern the number.
Another factor is datatypes. For instance, a fixnum is always interned even if the value is not apparent at reading time, according to https://docs.racket-lang.org/reference/numbers.html
Two fixnums that are = are also the same according to eq?. Otherwise, the result of eq? applied to two numbers is undefined
That means (eq? (+ 1 2) 3) is guaranteed to be #t.
A symbol is normally interned, but it's possible to make it uninterned via string->uninterned-symbol and gensym.
A symbol is like an immutable string, but symbols are normally interned, so that two symbols with the same character content are normally eq?.
The two procedures string->uninterned-symbol and gensym generate uninterned symbols, i.e., symbols that are not eq?, eqv?, or equal? to any other symbol, although they may print the same as other symbols.
So:
> (eq? (string->symbol "ab") (string->symbol (string-append "a" "b")))
#t
> (eq? (string->uninterned-symbol "ab") (string->uninterned-symbol "ab"))
#f

Related

Lisp: How to set element in list of lists?

I am familiar with how to set elements in a 2D array, which can be done using the following statement.
(setf (aref array2D 0 0) 3)
However, I am not familiar how to set elements in a list of lists, such as the following input: '((1) (2) (2) (1)). I can't use aref, since it only works on arrays.
As mentioned, while aref works on arrays, elt works on sequences which can be:
an ordered collection of elements
a vector or a list.
* (setf test-list '((1) (2) (2) (1)))
((1) (2) (2) (1))
* (setf (elt test-list 2) 'hi)
HI
* test-list
((1) (2) HI (1))
You can indeed use variables in place of fixed offsets:
* (setf test-list '((1) (2) (2) (1)))
((1) (2) (2) (1))
* (setf offset 2)
2
* (setf (elt test-list offset) 'hi)
HI
* test-list
((1) (2) HI (1))
To access the nth element of a list, there are (at least) two functions: nth and elt. The order of the parameters is different, and nth only work on lists while elt works on any sequence (i.e. lists, vector, strings ...):
(nth 1 '(foo bar baz)) => BAR
(nth 1 #(foo bar baz)) => ERROR
(elt '(foo bar baz) 1) => BAR
(elt #(foo bar baz) 1) => BAR
Now, in general, the way to set a value (as opposed to simply access it) is very straightforward, and at least for built-in functions this is almost always the case: whenever you have some form FORM which retrieves some value from what is called a place, the form (setf FORM <value>) will set this element to the given <value>. This works for functions such as car, cdr, gethash, aref, slot-value, symbol-function and many others, and any combination of those.
In your example, you have a list of lists. So, for example, to modify the "inner integer" in say the third list:
* (setf test-list '((0) (1) (2) (3))) ; changed the values to have something clearer
((0) (1) (2) (3))
* (car (nth 2 test-list)) ; this accesses the integer in the second list
2
* (setf (car (nth 2 test-list)) 12) ; this modifies it. Notice the syntax
12
* test-list
((0) (1) (12) (3))
On a side note, you should avoid modifying literal lists (created using the quote symbol '). If you want to modify lists, create them at runtime using the list function.
EDIT:
What happens is that setf knows, by "looking" at the form you give it, how to actually find the place that you want to modify, potentially using functions in this process.
If you look at other languages, such as Python, you also have some kind of duality in the syntax used both to get and to set values. Indeed, if you have a list L or a dictionary d, then L[index] and d[thing] will get the corresponding element while L[index] = 12 and d[thing] = "hello" will modify it.
However, in Python, those accessors use a special syntax, namely, the squares brackets []. Other types of objects use another syntax, for example, the dot notation to access slots/attributes of an object as in my-object.attr. A consequence is that the following code is invalid in Python:
>>> L = [1, 2, 3, 2, 1]
>>> max(L)
3
>>> max(L) = 12
Traceback (most recent call last):
File "<string>", line 9, in __PYTHON_EL_eval
File "/usr/lib/python3.8/ast.py", line 47, in parse
return compile(source, filename, mode, flags,
File "<string>", line 1
SyntaxError: cannot assign to function call
You have to write an other function, for example, setMax(L, val), to change the maximum of a list. This means that you now have to functions, and no symmetry anymore.
In Common Lisp, everything is (at least syntactically) a function call. This means that you can define new ways to access and modify things, for any function ! As a (bad) example of what you could do:
* (defun my-max (list)
(reduce #'max list))
MY-MAX
* (my-max '(1 2 3 8 4 5))
8
* (defun (setf my-max) (val list)
(do ((cur list (cdr cur))
(cur-max list (if (< (car cur-max) (car cur))
cur
cur-max)))
((endp (cdr cur)) (setf (car cur-max) val))))
(SETF MY-MAX)
* (setf test-list (list 0 4 5 2 3 8 6 3))
(0 4 5 2 3 8 6 3)
* (setf (my-max test-list) 42)
42
* test-list
(0 4 5 2 3 42 6 3)
This way, the syntax used to both set and get the maximum of a list is identical (FORM to get, (setf FORM val) to set), and combines automatically with every other "setter". No explicit pointers/references involved, it's just functions.

Unique identifier for Racket objects?

Is there a way to get a unique identifier for an object in Racket? For instance, when we use Racket's eq? operator to check whether two variables refer to the same object, what identifier is it using to achieve this comparison?
I'm looking for something like python's id function or Ruby's object_id method, in other words, some function id such that (= (id obj) (id obj2)) means that (eq? obj obj2) is true.
Some relevant docs:
Object Identity and Comparisons
Variables and Locations
Is eq-hash-code what you want?
> (define l1 '(1))
> (define l2 '(1))
> (eq? l1 l2)
#f
> (eq-hash-code l1)
9408
> (eq-hash-code l2)
9412
There's a way to get a C pointer of an object via ffi/unsafe, with the obvious caveat that it's UNSAFE.
;; from https://rosettacode.org/wiki/Address_of_a_variable#Racket
(require ffi/unsafe)
(define (madness v) ; i'm so sorry
(cast v _racket _gcpointer))
To use it:
(define a (list 1 2))
(define b (list 1 2))
(printf "a and b have different address: ~a ~a\n"
(equal? (madness a) (madness b))
(eq? a b))
(printf "a and a have the same address: ~a ~a\n"
(equal? (madness a) (madness a))
(eq? a a))
(printf "1 and 1 have the same address: ~a ~a\n"
(equal? (madness 1) (madness 1))
(eq? 1 1))
Though the pointer is not a number or an identifier. It's an opaque object... So in a sense, this is kinda useless. You could have used the real objects with eq? instead.
I also don't know any guarantee of this method. In particular, I don't know if the pointer will be updated to its latest value when the copy GC copies objects.
Here is an implementation of such a function using a weak hash table.
Using a weak hash table ensures that objects are garbage collected correctly
even if we have given it an id.
#lang racket
(define ht (make-weak-hasheq))
(define next 0)
(define (get-id x)
(define id (hash-ref ht x #f))
(or id
(begin0
next
(hash-set! ht x next)
(set! next (+ next 1)))))
(get-id 'a)
(get-id 'b)
(get-id 'a)
Note that Sylwester's advice is sound. The standard is to store the value directly.
You most likely won't find an identity, but the object itself is only eq? with itself and nothing else. eq? basically compares the address location of the values. So if you want an id you can just store the whole object at that place and it will be unique.
A location is a binding. Think of it as an address you cannot get and an address which has an address to a object. Eg. a binding ((lambda (a) a) 10) would store the address location of the object 10 in the first stack address and the code in the body just returns that same address. A location can change by set! but you'll never get the memory location of it.
It's common for lisp systems to store values in pointers. That means that some types and values doesn't really have an object at the address, but the address has a value and type encoded in it that the system knows. Typically small integers, chars, symbols and booleans can be pointer equal even though they are constructed at different times. eg. '(1 2 3) would only use 3 pairs and not any space for the values 1-3 and ().

Number Partitioning in R5RS

I was asked in an internship interview to do a R5RS program that creates a function, let's say two-subsets. This function has to return #t if the list L contains two subsets with equal sums of elements and with equal numbers of elements, otherwise it returns #f. It takes in entry the list L (only positive numbers) and some parameters (that I judge useful. There is no conditions on the number of parameters) all equal to 0 at the beginning.
The requirements as I still remember were as follow:
- Do not define other functions and call them inside the "two-subsets" function.
- It can only use the following constructs: null?, cond, car, cdr, else, + ,=, not, and, #t, #f, two-subsets (itself for recursive call), the names of the parameters, such as list, sum, ...etc, numeric constants and parentheses.
There were some given examples on the results that we are supposed to have, let's say:
(two-subsets '(7 7) 0 0 0) returns #t. The two subsets are {7} and {7}.
(two-subsets '(7 7 1) 0 0) returns #t. The two subsets are {7} and {7}.
(two-subsets '(5 3 2 4) 0 0) returns #t. The two subsets are {2, 5} and {3, 4}.
(two-subsets '(1 2 3 6 9) 0 0) returns #f.
I started by writing the signature that it looks to me it should be something like this:
(define two-subsets (lambda (L m n ... other parameters)
(cond
The problem is really complicated and it's complexity is obviously more than O(n), I read on it on https://en.wikipedia.org/wiki/Partition_problem .
I tried to start by defining the algorithm first before coding it. I thought about taking as parameters: sum of the list L so in my conditions I'll iterate only on the combinations which sum is <= sum(L)/2. By doing that I can reduce a little bit the complexity of the problem, but still I couldn't figure out how to do it.
It looks like an interesting problem and I really want to know more about it.
Here is a version which does not depend on the numbers being all positive. I am reasonably sure that, by knowing they are, you can do much better than this.
Note this assumes that:
the partition does not need to be exhaustive;
but the sets must not be empty.
I'd be very interested to see a version which relies on the elements of the list being +ve!
(define (two-subsets? l sl sld ssd)
;; l is the list we want to partition
;; sl is how many elements we have eaten from it so far
;; sld is the length difference in the partitions
;; ssd is the sum difference in the partitions
(cond [(and (not (= sl 0))
(= sld 0)
(= ssd 0))
;; we have eaten some elements, the differences are zero
;; we are done.
#t]
[(null? l)
;; out of l, failed
#f]
;; this is where I am sure we could be clever about the set containing
;; only positive numbers, but I am too lazy to think
[(two-subsets? (cdr l)
(+ sl 1)
(+ sld 1)
(+ ssd (car l)))
;; the left-hand set worked
#t]
[(two-subsets? (cdr l)
(+ sl 1)
(- sld 1)
(- ssd (car l)))
;; the right-hand set worked
#t]
[else
;; finally drop the first element of l and try the others
(two-subsets? (cdr l) sl sld ssd)]))

Members in a list - LISP [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 8 years ago.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Improve this question
I need to write a program in Lisp to see the number of occurrences of a specific character in a list. For example the occurrences of 1 in the following list [1, 2, 3, 1,1]
A list in Lisp is a sequence of cons nodes: pairs of pointers - the first to the payload datum, and the second to the rest of the list. E.g. for [1,2,3,1,1],
.
/ \
1 .
/ \
2 .
/ \
3 ...... .
/ \
1 NIL
NIL is a special value signaling the empty list, such that the system knows not to try to explore it any further. In Scheme,
(define NIL '())
Recursive list processing paradigm is captured by the notion of fold, where each node . is "replaced" with a binary function f, and the special node NIL is replaced with some special "zero" value z, to create an application chain (f 1 (f 2 (f 3 (... (f 1 z) ...)))). In Scheme,
(define (myfold f z list)
(cond
((null? list) z) ; replace NIL with the initial ("zero") value
(else
(f ; combine
(car list) ; the payload datum, and the delayed,
(lambda () ; by creating a function to calculate it,
(myfold f z ; result of recursively folding
(cdr list))))))) ; the rest of list
That way, the combining function f must process two values: one is a node's payload datum, the other is the (delayed) result of recursively folding, with the same f and z, the rest of the list after that node.
(define (keep-equals v list)
(myfold
(lambda (a r) ; combine ...
(if (equal? v a)
(cons a ... ) ; the same thing goes over the dots, here
... )) ; and here
'() ; replace the NIL of the argument list with this
list))
Since the recursive folding results' calculation is delayed by creating a function to-be-called when the results are needed, we need to "force" that calculation to be performed, when we indeed need those results, by calling that function.
And if you want to count the number of occurrences instead of collecting them in a list, you just need to use a different combining function with a different initial ("zero") value.
In particular, we build a list by consing a value onto the rest of list (with NIL as the initial value, the empty list); whereas we count by incrementing a counter (with 0 as the initial value of that counter).
Calculating e.g. a list's length by folding, we essentially turn its elements each into 1: length [a,b,c,d,e] == 1 + (1 + (1 + (1 + (1 + 0)))). Here, the combining function will need to increment the counter conditionally, only when the payload data are such that we want to count them.
I like pretty well the answers already posted to this question. But it seems like they both involve a fair bit more than the necessary amount of work. On the other hand, given all the thought everyone's put into this, I'm almost embarrassed of how simple my answer is. Anyway, here's what I did:
(defun count-things-in (needle haystack)
"Count the number of NEEDLEs in HAYSTACK."
(reduce '+
(map 'list
#'(lambda (straw)
(if (equalp straw needle) 1 0))
haystack)))
(count-things-in 1 '(1 2 3 1 1))
;; => 3
It's pretty straightforward: you just map over HAYSTACK a function which returns 1 for an element which is EQUALP to NEEDLE or 0 for an element which isn't, and then reduce the resulting list by +. For the given example list, the map operation results in a list (1 0 0 1 1), which the reduce operation then treats as (1 + (0 + (0 + (1 + 1)))), which evaluates to 3.
Benefits of this approach include the use of an equality predicate loose enough to work with strings as well as numbers, and with numbers of different types but the same value -- that is, (equalp 1 1.0) => t; if you desire different behavior, use another equality predicate instead. Using the standard MAP and REDUCE functions, rather than implementing your own, also gives you the benefit of whatever optimizations your Lisp system may be able to apply.
Drawbacks include being not nearly as impressive as anyone else's implementation, and being probably not low-level enough to satisfy the requirements of the asker's homework problem -- not that that latter especially dismays me, given that this answer does satisfy the stated requirement.
I'm new to lisp myself but here is how I would do it. I haven't looked at the other answer yet from Will so I'll check that out after I post this. The member function has the utility of both telling you if it found something in a list, and also returning the rest of that list starting from where it found it:
CL-USER> (member '1 '(0 1 2 3))
(1 2 3)
You could then recursively call a function that uses member and increment a counter from returned values in a variable from a let:
(defun find1 (alist)
(let ((count 0))
(labels ((findit (list)
(let ((part (member '1 list)))
(if part
(progn (incf count)
(findit (rest part)))
0))
count))
(findit alist))))
Here is the result:
CL-USER> (find1 '(1 2 3 4 5))
1
CL-USER> (find1 '(1 1 2 3 4 5))
2
CL-USER> (find1 '(1 1 1 2 3 1 4 5 1 1))
6
You could get rid of that unattractive progn by using cond instead of if
UPDATE: Here is an updated and more elegant version of the above, based on the comments, that I think would qualify as tail recursive as well:
(defun find1 (alist &optional (accum 0))
(let ((part (member '1 alist)))
(if part
(find1 (rest part) (+ accum 1))
accum)))
Here it is in action:
CL-USER> (find1 '(1 2 3 4))
1
CL-USER> (find1 '(1 1 1 1))
4
CL-USER> (find1 '(1 1 0 1 1))
4
CL-USER> (find1 '(0 2 1 0 1 1 0 1 1))
5

How to define symbols that will work like ( and ) by symbol macro?

I am trying define symbols a and b in following way
a + 1 1 b
2
I am trying to do this by using define-symbol-macro
(define-symbol-macro a '( )
(define-symbol-macro b ') )
but this way is not working.
What Lisp does with source code
Common Lisp is an incredibly flexible language, in part because its source code can be easily represented using the same data structures that are used in the language. The most common form of macro expansion transforms the these structures into other structures. These are the kind of macros that you can define with define-symbol-macro, define-compiler-macro, defmacro, and macrolet. Before any of those kind of macroexpansions can be performed, however, the system first needs to read the source from an input stream (typically a file, or an interactive prompt). That's the reader's responsibility. The reader also is capable of executing some special actions when it encounters certain characters, such ( and '. What you're trying to do probably needs to be happening down at the reader level, if you want to have, e.g., (read-from-string "a + 1 1 b") return the list (+ 1 1), which is what you want if you want (eval (read-from-string "a + 1 1 b")) to return 2. That said, you could also define a special custom language (like loop does) where a and b are treated specially.
Use set-macro-character, not define-symbol-macro
This isn't something that you would do using symbol-macros, but rather with macro characters. You can set macro characters using the aptly named set-macro-character. For instance, in the following, I set the macro character for % to be a function that reads a list, using read-delimited-list that should be terminated by ^. (Using the characters a and b here will prove very difficult, because you won't be able to write things like (set-macro-character ...) afterwards; it would be like writing (set-m(cro-ch(r(cter ...), which is not good.)
CL-USER> (set-macro-character #\% (lambda (stream ignore)
(declare (ignore ignore))
(read-delimited-list #\^ stream)))
T
CL-USER> % + 1 1 ^
2
The related set-syntax-from-char
There's a related function that almost does what you want here, set-syntax-from-char. You can use it to make one character behave like another. For instance, you can make % behave like (
CL-USER> (set-syntax-from-char #\% #\()
T
CL-USER> % + 1 1 )
2
However, since the macro character associated with ( isn't looking for a character that has the same syntax as ), but an actual ) character, you can't simply replace ) with ^ in the same way:
CL-USER> (set-syntax-from-char #\^ #\))
T
CL-USER> % + 1 1 ^
; Evaluation aborted on #<SB-INT:SIMPLE-READER-ERROR "unmatched close parenthesis" {1002C66031}>.
set-syntax-from-char is more useful when there's an existing character that, by itself does something that you want to imitate. For instance, if you wanted to make ! an additional quotation character:
CL-USER> (set-syntax-from-char #\! #\')
T
CL-USER> (list !a !(1 2 3))
(A (1 2 3))
or make % be a comment character, like it is in LaTeX:
CL-USER> (set-syntax-from-char #\% #\;)
T
CL-USER> (list 1 2 % 3 4
5 6)
(1 2 5 6)
But consider why you're doing this at all…
Now, even though you can do all of this, it seems like something that would be utterly surprising to anyone who ran into it. (Perhaps you're entering an obfuscated coding competition? ;)) For the reasons shown above, doing this with commonly used characters such as a and b will also make it very difficult to write any more source code. It's probably a better bet to define an entirely new readtable that does what you want, or even write a new parser. even though (Common) Lisp lets you redefine the language, there are still things that it probably makes sense to leave alone.
A symbol-macro is a symbol that stands for another form. Seems like you want to look at reader macros.
http://clhs.lisp.se/Body/f_set__1.htm
http://dorophone.blogspot.no/2008/03/common-lisp-reader-macros-simple.html
I would second Rainer's comment though, what are you trying to make?
Ok so I love your comment on the reason for this and now I know this is for 'Just because it's lisp' then I am totally on board!
Ok so you are right about lisp being great to use to make new languages because we only have to 'compile' to valid lisp code and it will run. So while we cant use the normal compiler to do the transformation of the symbols 'a and 'b to brackets we can write this ourselves.
Ok so lets get started!
(defun symbol-name-equal (a b)
(and (symbolp a) (symbolp b) (equal (symbol-name a) (symbol-name b))))
(defun find-matching-weird (start-pos open-symbol close-symbol code)
(unless (symbol-name-equal open-symbol (nth start-pos code))
(error "start-pos does not point to a weird open-symbol"))
(let ((nest-index 0))
(loop :for item :in (nthcdr start-pos code)
:for i :from start-pos :do
(cond ((symbol-name-equal item open-symbol) (incf nest-index 1))
((symbol-name-equal item close-symbol) (incf nest-index -1)))
(when (eql nest-index 0)
(return i))
:finally (return nil))))
(defun weird-forms (open-symbol close-symbol body)
(cond ((null body) nil)
((listp body)
(let ((open-pos (position open-symbol body :test #'symbol-name-equal)))
(if open-pos
(let ((close-pos (find-matching-weird open-pos open-symbol close-symbol body)))
(if close-pos
(weird-forms open-symbol close-symbol
`(,#(subseq body 0 open-pos)
(,#(subseq body (1+ open-pos) close-pos))
,#(subseq body (1+ close-pos))))
(error "unmatched weird brackets")))
(if (find close-symbol body :test #'symbol-name-equal)
(error "unmatched weird brackets")
(loop for item in body collect
(weird-forms open-symbol close-symbol item))))))
(t body)))
(defmacro with-weird-forms ((open-symbol close-symbol) &body body)
`(progn
,#(weird-forms open-symbol close-symbol body)))
So there are a few parts to this.
First we have (symbol-name-equal), this is a helper function because we are now using symbols and symbols belong to packages. symbol-name-equal gives us a way of checking if the symbols have the same name ignoring what package they reside in.
Second we have (find-matching-weird). This is a function that takes a list and and index to an opening weird bracket and returns the index to the closing weird bracket. This makes sure we get the correct bracket even with nesting
Next we have (weird-forms). This is the juicy bit and what it does is to recursively walk through the list passed as the 'body' argument and do the following:
If body is an empty list just return it
if body is a list then
find the positions of our open and close symbols.
if only one of them is found then we have unmatched brackets.
if we find both symbols then make a new list with the bit between the start and end positions inside a nested list.
we then call weird forms on this result in case there are more weird-symbol-forms inside.
there are no weird symbols then just loop over the items in the list and call weird-form on them to keep the search going.
OK so that function transforms a list. For example try:
(weird-forms 'a 'b '(1 2 3 a 4 5 b 6 7))
But we want this to be proper lisp code that executes so we need to use a simple macro.
(with-weird-forms) is a macro that takes calls the weird-forms function and puts the result into our source code to be compiled by lisp. So if we have this:
(with-weird-forms (a b)
(+ 1 2 3 a - a + 1 2 3 b 10 5 b 11 23))
Then it macroexpands into:
(PROGN (+ 1 2 3 (- (+ 1 2 3) 10 5) 11 23))
Which is totally valid lisp code, so it will run!
CL-USER> (with-weird-forms (a b)
(+ 1 2 3 a - a + 1 2 3 b 10 5 b 11 23))
31
Finally if you have settled on the 'a' and 'b' brackets you could write another little macro:
(defmacro ab-lang (&rest code)
`(with-weird-forms (a b) ,#code))
Now try this:
(ab-lang a let* a a d 1 b a e a * d 5 b b b a format t "this stupid test gives: ~a" e b b)
Cheers mate, this was great fun to write. Sorry for dismissing the problem earlier on.
This kind of coding is very important as ultimately this is a tiny compiler for our weird language where symbols can be punctuation. Compilers are awesome and no language makes it as effortless to write them as lisp does.
Peace!