I want to write a bitboard in common lisp, so I need a 64 bit integer. How do I get a 64 bit integer in common lisp? Also, are there any libraries that could help me accomplish this without writing everything from scratch?
You can declare your variables to be of type (signed-byte 64) or (unsigned-byte 64):
CL-USER> (typexpand '(unsigned-byte 64))
(INTEGER 0 18446744073709551615)
T
CL-USER> (typexpand '(signed-byte 64))
(INTEGER -9223372036854775808 9223372036854775807)
T
It depends upon your implementation if it is actually clever enough to really stuff this in 8 consecutive bytes or if it will use a bignum for this. Appropriate optimize-declarations might help.
Here's a (very simple) example of such type declarations, and handling integers in binary:
(let* ((x #b01)
(y #b10)
(z (logior x y)))
(declare ((signed-byte 64) x y z))
(format t "~a~%" (logbitp 1 x))
(format t "~a~%" (logbitp 1 (logior x (ash 1 1))))
(format t "~b~%" z))
Output:
NIL
T
11
Here's a setf-expander definition to get a simple setter for bits in integers, and a corresponding getter:
(define-setf-expander logbit (index place &environment env)
(multiple-value-bind (temps vals stores store-form access-form)
(get-setf-expansion place env)
(let ((i (gensym))
(store (gensym))
(stemp (first stores)))
(values `(,i ,#temps)
`(,index ,#vals)
`(,store)
`(let ((,stemp (dpb ,store (byte 1 ,i) ,access-form))
,#(cdr stores))
,store-form
,store)
`(logbit ,i ,access-form)))))
(defun logbit (index integer)
(ldb (byte 1 index) integer))
These can be used like this:
(let ((x 1))
(setf (logbit 3 x) 1)
x)
==> 9
(let ((x 9))
(setf (logbit 3 x) 0)
x)
==> 1
(logbit 3 1)
==> 0
(logbit 3 9)
==> 1
In portable Common Lisp 'Integers' are as large as you like. There is a more efficient subset of integers called 'fixnums'. The exact range of fixnums is implementation depended. But it is typically not the full 64 bit (on a 64bit architecture) which can be used, since most Common Lisp implementations need type tag bits. For the user there is not much of a difference. Fixnums are a subset of integers and one can add two fixnums and get a not-fixnum integer result. The only differences that may be observable is that computation with non-fixnum integers is slower, needs more storage, ... Generally, if you want to do computation with integers, you don't need to declare that you want to calculate with 64bit. You just use Integers and the usual operations for those.
If you want real 64bit large integers (represented in only 64bits, without tags, etc.) and computation with those, you'll leave the portable ANSI CL capabilities. If and how CLISP supports that, is best asked on the CLISP mailing list.
Documentation
Type FIXNUM
Type INTEGER
Example usage of bit vectors/arrays to implement a 8x8 bit-board
(starting with brutally and prematurely optimized code just to show a
way to get tight assembler code):
(defun make-bitboard ()
(make-array '(8 8) :element-type '(mod 2) :initial-element 0))
MAKE-BITBOARD will create a 8x8 bitboard as an array of bits. When
using SBCL, this is internally represented as 1 bit per element (so
you have 64 bits + array instance overhead). If you ask for
optimizations when accessing the board, you'll get fast code.
(declaim (inline get-bitboard))
(defun get-bitboard (bit-board x y)
(declare (optimize speed (safety 0) (debug 0))
(type (simple-array (mod 2) (8 8)) bit-board)
(type fixnum x y))
(aref bit-board x y))
(declaim (notinline get-bitboard))
The DECLAIMs are there to allow local
inlining requests for
GET-BITBOARD.
An example of using GET-BITBOARD:
(defun use-bitboard (bit-board)
(declare (optimize speed (safety 0) (debug 0))
(type (simple-array (mod 2) (8 8)) bit-board)
(inline get-bitboard))
(let ((sum 0))
(declare (type fixnum sum))
(dotimes (i 8)
(declare (type fixnum i))
(dotimes (j 8)
(declare (type fixnum j))
(incf sum (the (mod 2) (get-bitboard bit-board i j)))))
sum))
Since there is no SET-BITBOARD yet, an example of using USE-BITBOARD is:
(use-bitboard (make-bitboard))
Disassembling USE-BITBOARD (SBCL again, Linux x64) shows that the
compiler inlined GET-BITBOARD:
; disassembly for USE-BITBOARD
; 030F96A2: 31F6 XOR ESI, ESI ; no-arg-parsing entry point
; 6A4: 31D2 XOR EDX, EDX
; 6A6: EB54 JMP L3
; 6A8: 90 NOP
; 6A9: 90 NOP
; 6AA: 90 NOP
; 6AB: 90 NOP
; 6AC: 90 NOP
; 6AD: 90 NOP
; 6AE: 90 NOP
; 6AF: 90 NOP
; 6B0: L0: 31DB XOR EBX, EBX
; 6B2: EB3E JMP L2
; 6B4: 90 NOP
; 6B5: 90 NOP
; 6B6: 90 NOP
; 6B7: 90 NOP
; 6B8: 90 NOP
; 6B9: 90 NOP
; 6BA: 90 NOP
; 6BB: 90 NOP
; 6BC: 90 NOP
; 6BD: 90 NOP
; 6BE: 90 NOP
; 6BF: 90 NOP
; 6C0: L1: 488D04D500000000 LEA RAX, [RDX*8]
; 6C8: 4801D8 ADD RAX, RBX
; 6CB: 4C8B4711 MOV R8, [RDI+17]
; 6CF: 48D1F8 SAR RAX, 1
; 6D2: 488BC8 MOV RCX, RAX
; 6D5: 48C1E906 SHR RCX, 6
; 6D9: 4D8B44C801 MOV R8, [R8+RCX*8+1]
; 6DE: 488BC8 MOV RCX, RAX
; 6E1: 49D3E8 SHR R8, CL
; 6E4: 4983E001 AND R8, 1
; 6E8: 49D1E0 SHL R8, 1
; 6EB: 4C01C6 ADD RSI, R8
; 6EE: 4883C302 ADD RBX, 2
; 6F2: L2: 4883FB10 CMP RBX, 16
; 6F6: 7CC8 JL L1
; 6F8: 4883C202 ADD RDX, 2
; 6FC: L3: 4883FA10 CMP RDX, 16
; 700: 7CAE JL L0
; 702: 488BD6 MOV RDX, RSI
; 705: 488BE5 MOV RSP, RBP
; 708: F8 CLC
; 709: 5D POP RBP
; 70A: C3 RET
Not sure why the compiler put in all those NOPs (leaving space for
instrumentation later? alignments?) but if you look at the code at the
end it's pretty compact (not as compact as hand-crafted assembler, of
course).
Now this is an obvious case of premature optimization. The correct way
to start here would be to simply write:
(defun get-bitboard (bit-board x y)
(aref bit-board x y))
(defun use-bitboard (bit-board)
(let ((sum 0))
(dotimes (i 8)
(dotimes (j 8)
(incf sum (get-bitboard bit-board i j))))
sum))
... and then use a profiler when running the game code that uses the
bit-board to see where the CPU bottlenecks are. SBCL includes a nice
statistical profiler.
Starting with the simpler and slower code, with no declarations for
speed, is best. Just compare the size of the code - I started with the
code with plenty of declarations to make the simple code at the end
look even simpler by comparison :-). The advantage here is that you
can treat Common Lisp as a scripting/prototyping language when trying
out ideas, then squeeze more performance out of the code that the
profiler suggests.
The assembly code is obviously not as tight as loading the whole board in
one 64 bit register and then accessing individual bits. But if you
suddenly decide that you want more than 1 bit per square, it's much
easier to change the CL code than to change assembler code (just
change the array type everywhere from '(mod 2) to '(mod 16), for
instance).
You want to use bit vectors, which are arbitrary sized arrays of bits, rather than something like a 64 bit integer. The implementation will deal with the internal representations for you.
Related
I've been solving some challenges at codesignal.com using C-Lisp to learn it and I've been avoiding using loops to make lisp style code.
In this challenge called alternatingSums (which gives you an int array a that can be very large and ask you to return an array/list {sumOfEvenIndexedElements, sumOfOddIndexedElements}) i have been receiving stack overflow error with this code:
(defun alternatingSums(a &optional (index 0) (accumulated '(0 0)))
(cond ((= index (length a))
accumulated)
((evenp index)
(alternatingSums
a
(1+ index)
`(,(+ (svref a index ) (elt accumulated 0)) ,(elt accumulated 1)))
)
((oddp index)
(alternatingSums
a
(1+ index)
`(,(elt accumulated 0) ,(+ (svref a index ) (elt accumulated 1))))
)
)
)
isn't it tail-recursive or can tail-recursive functions still get stack-overflow?
Recursive functions which call themselves from tail position can lead to stack overflow; language implementations must support some form of tail call elimination to avoid the problem.
I've been avoiding using loops to make lisp style code.
Common Lisp does not require that implementations do tail call elimination, but Scheme implementations must do so. It is idiomatic in Scheme to use recursion for iteration, but in Common Lisp it is idiomatic to use other iteration devices unless recursion provides a natural solution for the problem at hand.
Although Common Lisp implementations are not required to do tail call elimination, many do. Clisp does support limited tail call elimination, but only in compiled code, and only for self-recursive tail calls. This is not well-documented, but there is some discussion to be found here #Renzo. OP posted code will be subject to tail call elimination when compiled in Clisp since the function alternatingSums calls itself from tail position. This covers most cases in which you may be interested in tail call elimination, but note that tail call elimination is then not done for mutually recursive function definitions in Clisp. See the end of this answer for an example.
Defining a function from the REPL, or loading a definition from a source file, will result in interpreted code. If you are working in a development environment like SLIME, it is easy to compile: from the source file buffer either do Ctrl-c Ctrl-k to compile the whole file and send it to the REPL, or place the point inside of or immediately after a function definition and do Ctrl-c Ctrl-c to compile a single definition and send it to the REPL.
You could also compile the source file before loading it, e.g. (load (compile-file "my-file.lisp")). Or you could load the source file, and compile a function after that, e.g. (load "my-file.lisp"), then (compile 'my-function).
As already mentioned, it would probably be more likely that idiomatic Common Lisp code would not use recursion for this sort of function anyway. Here is a definition using the loop macro that some would find more clear and concise:
(defun alternating-sums (xs)
(loop for x across xs
and i below (length xs)
if (evenp i) sum x into evens
else sum x into odds
finally (return (list evens odds))))
The Case of Mutually Recursive Functions in Clisp
Here is a simple pair of mutually recursive function definitions:
(defun my-evenp (n)
(cond ((zerop n) t)
((= 1 n) nil)
(t (my-oddp (- n 1)))))
(defun my-oddp (n)
(my-evenp (- n 1)))
Neither function calls itself directly, but my-evenp has a call to my-oddp in tail position, and my-oddp has a call to my-evenp in tail position. One would like for these tail calls to be eliminated to avoid blowing the stack for large inputs, but Clisp does not do this. Here is the disassembly:
CL-USER> (disassemble 'my-evenp)
Disassembly of function MY-EVENP
14 byte-code instructions:
0 (LOAD&PUSH 1)
1 (CALLS2&JMPIF 172 L16) ; ZEROP
4 (CONST&PUSH 0) ; 1
5 (LOAD&PUSH 2)
6 (CALLSR&JMPIF 1 47 L19) ; =
10 (LOAD&DEC&PUSH 1)
12 (CALL1 1) ; MY-ODDP
14 (SKIP&RET 2)
16 L16
16 (T)
17 (SKIP&RET 2)
19 L19
19 (NIL)
20 (SKIP&RET 2)
CL-USER> (disassemble 'my-oddp)
Disassembly of function MY-ODDP
3 byte-code instructions:
0 (LOAD&DEC&PUSH 1)
2 (CALL1 0) ; MY-EVENP
4 (SKIP&RET 2)
Compare with a tail recursive function that calls itself. Here there is no call to factorial in the disassembly, but instead a jump instruction has been inserted: (JMPTAIL 2 5 L0).
(defun factorial (n acc)
(if (zerop n) acc
(factorial (- n 1) (* n acc))))
CL-USER> (disassemble 'factorial)
Disassembly of function FACTORIAL
11 byte-code instructions:
0 L0
0 (LOAD&PUSH 2)
1 (CALLS2&JMPIF 172 L15) ; ZEROP
4 (LOAD&DEC&PUSH 2)
6 (LOAD&PUSH 3)
7 (LOAD&PUSH 3)
8 (CALLSR&PUSH 2 57) ; *
11 (JMPTAIL 2 5 L0)
15 L15
15 (LOAD 1)
16 (SKIP&RET 3)
Some Common Lisp implementations do support tail call elimination for mutually recursive functions. Here is the disassembly of my-oddp from SBCL:
;; SBCL
; disassembly for MY-ODDP
; Size: 40 bytes. Origin: #x52C8F9E4 ; MY-ODDP
; 9E4: 498B4510 MOV RAX, [R13+16] ; thread.binding-stack-pointer
; 9E8: 488945F8 MOV [RBP-8], RAX
; 9EC: BF02000000 MOV EDI, 2
; 9F1: 488BD3 MOV RDX, RBX
; 9F4: E8771B37FF CALL #x52001570 ; GENERIC--
; 9F9: 488B5DF0 MOV RBX, [RBP-16]
; 9FD: B902000000 MOV ECX, 2
; A02: FF7508 PUSH QWORD PTR [RBP+8]
; A05: E9D89977FD JMP #x504093E2 ; #<FDEFN MY-EVENP>
; A0A: CC10 INT3 16 ; Invalid argument count trap
This is a little harder to read than the previous examples because SBCL compiles to assembly language instead of byte code, but you can see that a jump instruction has been substituted for the call to my-evenp:
; A05: E9D89977FD JMP #x504093E2 ; #<FDEFN MY-EVENP>
Common Lisp compilers are not required to optimize tail calls. Many do, but not all implementations compile your code by default; you have to compile the file using compile-file, or else the function individually with (compile 'alternatingsums).
CLISP contains both an interpreter, which processes the nested-list representation of Lisp source code, and a byte code compiler. The compiler supports tail recursion, whereas the interpreter doesn't:
$ clisp -q
[1]> (defun countdown (n) (unless (zerop n) (countdown (1- n))))
COUNTDOWN
[2]> (countdown 10000000)
*** - Program stack overflow. RESET
[3]> (compile 'countdown)
COUNTDOWN ;
NIL ;
NIL
[4]> (countdown 10000000)
NIL
Peeking under the hood a little bit:
[5]> (disassemble 'countdown)
Disassembly of function COUNTDOWN
1 required argument
0 optional arguments
No rest parameter
No keyword parameters
8 byte-code instructions:
0 L0
0 (LOAD&PUSH 1)
1 (CALLS2&JMPIF 172 L10) ; ZEROP
4 (LOAD&DEC&PUSH 1)
6 (JMPTAIL 1 3 L0)
10 L10
10 (NIL)
11 (SKIP&RET 2)
NIL
We can see that the virtual machine has a JMPTAIL primitive.
Another approach to tail calling is via macros. Years ago, I hacked up a macro called tlet which lets you define (what look like) lexical functions using syntax similar to labels. The tlet construct compiles to a tagbody form in which the tail calls among the functions are go forms. It does not analyze calls for being in tail position: all calls are unconditional transfers that do not return regardless of their position in the syntax. The same source file also provides a trampoline-based implementation of tail calling among global functions.
Here is tlet in CLISP; note: the expression has not been compiled, yet it doesn't run out of stack:
$ clisp -q -i tail-recursion.lisp
;; Loading file tail-recursion.lisp ...
;; Loaded file tail-recursion.lisp
[1]> (tlet ((counter (n) (unless (zerop n) (counter (1- n)))))
(counter 100000))
NIL
tlet is not an optimizer. The call to counter is semantically a goto, always; it's not a procedure call that can sometimes turn into a goto under the right circumstances. Watch what happens when we add a print:
[2]> (tlet ((counter (n) (unless (zerop n) (print (counter (1- n))))))
(counter 100000))
NIL
That's right; nothing! (counter (1- n)) never returns, and so print is never called.
I've had this question more than once before.
Generic Question
Is it possible to transparently locally shadow a function f with a wrapper of it with the same name f?
I.e., how to locally have (f wrapped-args...) expand to (f args...)?
Flet seems to let us do so, but has limitations, namely, the resulting wrapper is not setf-able. Is it possible to do so without resorting to flet?
Ideally there would be a macro that lets us write the "wrapped" f calls and it expands the code to the original "non-wrapped" f call.
At first I believed macrolet could be that, for it says in the documentation that it first expands the macro and then applies setf on the expanded form, but I'm not being able to use it (keep reading below).
Motivation
This is useful in contexts where some paremeters are implicit and should not be repeated over and over, for more DRY code.
In my previous question (let-curry) there's a particular example of that. Attempting to "automatically" assign some of the parameters of the functions (let-curry).
Caveats of flet
I got some excellent answers there, however, I hit some limitations. By resorting to flet to accomplish such local "shadowing" of the function name to a wrapper over it, such wrappers are not setf-able, thus, such wrappers cannot be used as flexibly as the original function, only to read values, not write.
Concrete question
With the link above, how can one write the macro flet-curry and have the wrapper functions be setf-able?
Bonus: Can that macro expand the wrapped calls to the original ones with 0 runtime overhead?
I tried taking the selected answer in that post and using macrolet instead of flet to no avail.
Thank you!
UPDATE
I was asked to give a concrete example for this generic question.
Comments of wishes in the code:
(locally (declare (optimize safety))
(defclass scanner ()
((source
:initarg :source
:accessor source
:type string)
(tokens
:initform nil
:accessor tokens
:type list)
(start
:initform 0
:accessor start
:type integer)
(current
:initform 0
:accessor current
:type integer)
(line
:initform 1
:accessor line
:type integer))
(:metaclass checked-class)))
(defun lox-string (scanner)
"Parse string into a token and add it to tokens"
;; Any function / defmethod / accessor can be passed to let-curry
;; 1. I'd like to add the accessor `line` to this list of curried methods:
(let-curry scanner (peek at-end-p advance source start current)
(loop while (and (char/= #\" (peek))
(not (at-end-p)))
do
;; 2. but cannot due to the incf call which calls setf:
(if (char= #\Newline (peek)) (incf (line scanner))
(advance)))
(when (at-end-p)
(lox.error::lox-error (line scanner) "Unterminated string.")
(return-from lox-string nil))
(advance) ;; consume closing \"
(add-token scanner 'STRING (subseq (source)
(1+ (start))
(1- (current))))))
Meaning I'd like let-curry to transform any call of the curried functions in that block from
(f arg1 arg2 ...)
to
(f scanner arg1 arg2 ...)
in place, as if I'd written the latter form and not the former in the source code. If that were the case with some ?macro?, then it would be setf-able by design.
It seems a macro would be the right tool for this but I don't know how.
Thanks again :)
P.S.: If you need access to the full code it's here: https://github.com/AlbertoEAF/cl-lox (scanner.lisp)
Binding with macrolet is not trivial since:
Once you bind f in a macrolet, if it expands as (f ...), you are going to have infinite macroexpansion.
Also, you could expand the macrolet as (apply #'f ...) (which is great, since APPLY can be a SETF place1), but then you have errors because #'f is bound to a local macro, not the original function. If, however, you first evaluate #'f, bind it to a hidden variable, then define a macro that applies the variable's value, SETF APPLY complains (at least in SBCL) that the function must not be a symbol (ie. dynamically computed).
1: For example (let ((x (list 0 1 2))) (prog1 x (setf (apply #'second list ()) 9)))
But you don't need macrolet, since you can bind SETF functions in FLET; here is what you could write manually if you wanted to redefine some functions locally:
(defun lox-string (scanner)
(flet
((peek () (peek scanner))
(at-end-p () (at-end-p scanner))
(advance () (advance scanner))
(line () (line scanner))
((setf line) (n) (setf (line scanner) n))
(source () (source scanner))
(start () (start scanner))
(current () (current scanner)))
(loop
while (and (char/= #\" (peek))
(not (at-end-p)))
do
(if (char= #\Newline (peek))
(incf (line))
(advance)))
(when (at-end-p)
(error "Unterminated string at line ~a" (line)))
(advance)
(add-token scanner 'STRING (subseq (source)
(1+ (start))
(1- (current))))))
Expand as FLET
The following macro expands as inlinable flets and handles SETF functions in a special way, since the first argument is always the value being set:
(defmacro with-curry ((&rest fn-specs) prefix &body body)
(loop
with args = (gensym)
and n = (gensym)
and prefix = (alexandria:ensure-list prefix)
for f in fn-specs
collect (if (and (consp f) (eq 'setf (first f)))
`(,f (,n &rest ,args) (apply #',f ,n ,#prefix ,args))
`(,f (&rest ,args) (apply #',f ,#prefix ,args)))
into flets
finally (return
`(flet ,flets
(declare (inline ,#fn-specs))
,#body))))
For example:
(let ((scanner (make-instance 'scanner)))
(with-curry (start (setf start)) scanner
(setf (start) (+ (start) 10))))
This macroexpands as:
(LET ((SCANNER (MAKE-INSTANCE 'SCANNER)))
(FLET ((START (&REST #:G849)
(APPLY #'START SCANNER #:G849))
((SETF START) (#:G850 &REST #:G849)
(APPLY #'(SETF START) #:G850 SCANNER #:G849)))
(DECLARE (INLINE START (SETF START)))
(LET* ((#:NEW1 (+ (START) 10)))
(FUNCALL #'(SETF START) #:NEW1))))
Inlining FLET
The inline declaration is a request (the compiler may ignore it) to replace each calls to the function by its body (parameters are substituted by the function call arguments; it looks like β-reduction in lambda-calculus).
When the compiler recognizes it, it is as-if you defined the code as a macrolet, removing the need to call a function. When inlining is in effect, apply will see during compilation both the function object to call and all the arguments, so the compiler can emit code as-if you wrote directly all parameters.
Let's test that with SBCL, first with a notinline declaration to explicitly prevent inlining:
(disassemble
(lambda ()
(declare (optimize (debug 0) (safety 0)))
(flet ((p (&rest args) (apply #'print args)))
(declare (notinline p))
(p 0) (p 1))))
The output of the disassembler is a bit long, and I won't claim I understand what happens exactly; there is a first segment that apparently allocates memory (for the local function?):
; disassembly for (LAMBDA ())
; Size: 187 bytes. Origin: #x53F0A5B6 (segment 1 of 2) ; (LAMBDA ())
; 5B6: 49896D28 MOV [R13+40], RBP ; thread.pseudo-atomic-bits
; 5BA: 4D8B5D68 MOV R11, [R13+104] ; thread.alloc-region
; 5BE: 498D4B10 LEA RCX, [R11+16]
; 5C2: 493B4D70 CMP RCX, [R13+112]
; 5C6: 0F878C000000 JNBE L8
; 5CC: 49894D68 MOV [R13+104], RCX ; thread.alloc-region
; 5D0: L0: 498D4B07 LEA RCX, [R11+7]
; 5D4: 49316D28 XOR [R13+40], RBP ; thread.pseudo-atomic-bits
; 5D8: 7402 JEQ L1
; 5DA: CC09 INT3 9 ; pending interrupt trap
; 5DC: L1: C7410117001050 MOV DWORD PTR [RCX+1], #x50100017 ; NIL
; 5E3: 488BDD MOV RBX, RBP
; 5E6: 488D5424F0 LEA RDX, [RSP-16]
; 5EB: 4883EC10 SUB RSP, 16
; 5EF: 48891A MOV [RDX], RBX
; 5F2: 488BEA MOV RBP, RDX
; 5F5: E82F000000 CALL L4
; 5FA: 49896D28 MOV [R13+40], RBP ; thread.pseudo-atomic-bits
; 5FE: 4D8B5D68 MOV R11, [R13+104] ; thread.alloc-region
; 602: 498D4B10 LEA RCX, [R11+16]
; 606: 493B4D70 CMP RCX, [R13+112]
; 60A: 775A JNBE L9
; 60C: 49894D68 MOV [R13+104], RCX ; thread.alloc-region
; 610: L2: 498D4B07 LEA RCX, [R11+7]
; 614: 49316D28 XOR [R13+40], RBP ; thread.pseudo-atomic-bits
; 618: 7402 JEQ L3
; 61A: CC09 INT3 9 ; pending interrupt trap
; 61C: L3: C641F902 MOV BYTE PTR [RCX-7], 2
; 620: C7410117001050 MOV DWORD PTR [RCX+1], #x50100017 ; NIL
; 627: EB03 JMP L5
; 629: L4: 8F4508 POP QWORD PTR [RBP+8]
... followed by a second segment which looks like it actually defines and call the local function (?):
; Origin #x53F0A62C (segment 2 of 2) ; (FLET P)
; 62C: L5: 488BF4 MOV RSI, RSP
; 62F: L6: 4881F917001050 CMP RCX, #x50100017 ; NIL
; 636: 7412 JEQ L7
; 638: FF71F9 PUSH QWORD PTR [RCX-7]
; 63B: 488B4901 MOV RCX, [RCX+1]
; 63F: 8D41F9 LEA EAX, [RCX-7]
; 642: A80F TEST AL, 15
; 644: 74E9 JEQ L6
; 646: CC0A INT3 10 ; cerror trap
; 648: 06 BYTE #X06 ; BOGUS-ARG-TO-VALUES-LIST-ERROR
; 649: 04 BYTE #X04 ; RCX
; 64A: L7: 488B053FFFFFFF MOV RAX, [RIP-193] ; #<FUNCTION PRINT>
; 651: FF2425A8000052 JMP QWORD PTR [#x520000A8] ; TAIL-CALL-VARIABLE
; 658: L8: 6A11 PUSH 17
; 65A: FF142550000052 CALL QWORD PTR [#x52000050] ; CONS->R11
; 661: E96AFFFFFF JMP L0
; 666: L9: 6A11 PUSH 17
; 668: FF142550000052 CALL QWORD PTR [#x52000050] ; CONS->R11
; 66F: EB9F JMP L2
Anyway, it is very different from the disassembly output of the inline case:
(disassemble
(lambda ()
(declare (optimize (debug 0) (safety 0)))
(flet ((p (&rest args) (apply #'print args)))
(declare (inline p))
(p 0) (p 1))))
This prints:
; disassembly for (LAMBDA ())
; Size: 45 bytes. Origin: #x540D3CF6 ; (LAMBDA ())
; CF6: 4883EC10 SUB RSP, 16
; CFA: 31D2 XOR EDX, EDX
; CFC: B902000000 MOV ECX, 2
; D01: 48892C24 MOV [RSP], RBP
; D05: 488BEC MOV RBP, RSP
; D08: B8C2283950 MOV EAX, #x503928C2 ; #<FDEFN PRINT>
; D0D: FFD0 CALL RAX
; D0F: BA02000000 MOV EDX, 2
; D14: B902000000 MOV ECX, 2
; D19: FF7508 PUSH QWORD PTR [RBP+8]
; D1C: B8C2283950 MOV EAX, #x503928C2 ; #<FDEFN PRINT>
; D21: FFE0 JMP RAX
The above is shorter, and directly calls print.
It is equivalent to the disassembly where inlining is done by hand:
(disassemble (lambda ()
(declare (optimize (debug 0) (safety 0)))
(print 0) (print 1)))
; disassembly for (LAMBDA ())
; Size: 45 bytes. Origin: #x540D4066 ; (LAMBDA ())
; 66: 4883EC10 SUB RSP, 16
; 6A: 31D2 XOR EDX, EDX
; 6C: B902000000 MOV ECX, 2
; 71: 48892C24 MOV [RSP], RBP
; 75: 488BEC MOV RBP, RSP
; 78: B8C2283950 MOV EAX, #x503928C2 ; #<FDEFN PRINT>
; 7D: FFD0 CALL RAX
; 7F: BA02000000 MOV EDX, 2
; 84: B902000000 MOV ECX, 2
; 89: FF7508 PUSH QWORD PTR [RBP+8]
; 8C: B8C2283950 MOV EAX, #x503928C2 ; #<FDEFN PRINT>
; 91: FFE0 JMP RAX
While I have not been following this in detail, note that setf does not have to be a problem here.
Consider this:
(defclass grunga-object ()
;; grunga objects have grungas, but they may be unbound
((grunga :accessor object-grunga :initarg :grunga)))
(defgeneric object-has-valid-grunga-p (o)
;; Does some object have a valid grunga?
(:method (o)
nil))
(defmethod object-has-valid-grunga-p ((o grunga-object))
;; grunga object's grungas are valid if they are bound
(slot-boundp o 'grunga))
(defun grunga (object &optional (default 'grunga))
;; get the grunga of a thing
(if (object-has-valid-grunga-p object)
(object-grunga object)
default))
(defun (setf grunga) (new object)
;; set the grunga of a thing
(setf (object-grunga object) new))
Now this will work fine:
(defun foo (o)
(flet ((grunga (object)
(grunga object 3)))
(setf (grunga o) (grunga o))
o))
and (grunga (foo (make-instance 'grunga-object))) will return 3. In this case the local grunga function calls the global one, while (setf grunga) – a different function – is called directly.
If you want to override the (setf grunga) function you can do that as well:
(defun bar (o &optional (exploded-value 'exploded))
(flet ((grunga (object)
(grunga object 3))
((setf grunga) (new object &optional (exploding t))
(setf (grunga object) (if exploding (cons exploded-value new) new))))
(setf (grunga o t) (grunga o))
o))
And now (grunga (bar (make-instance 'grunga-object) 'crunched)) is (cruched . 3). In this case both grunga and (setf grunga) are local functions which call their global counterparts.
Note that this may be more complicated with setf forms defined by define-setf-*: I never use those if I can possibly avoid it.
As mentioned here, I'm trying to teach myself lisp by implementing lodash.
I have basically no experience with lisp, so work that would be trivial in js is foreign to me.
For instance, I'm working on implementation of a _.chunk method, which in js takes an array and a size variable and 'chunks' the array by the size:
_.chunk(['a', 'b', 'c', 'd'], 2);
// => [['a', 'b'], ['c', 'd']]
_.chunk(['a', 'b', 'c', 'd'], 3);
// => [['a', 'b', 'c'], ['d']]
As somebody totally new to common lisp data types, I would assume that the analogous type would be a vector, not an array, is that correct?
Secondly, my way of solving this algorithmically would be to retain a length variable, and a pointer variable, and to grab a subset of the array/vector, [pointer to pointer + size], while pointer + size was < length, and then return [pointer to length] when that was no longer true, and incrementing pointer to pointer + size + 1 otherwise.
No idea how to implement this in lisp, here is my code so far.
(defun _.chunk (vector &optional (size 1 size-p))
(if (or (not size-p) (eq size 1))
vector
((let (
(array_length (array-total-size array))
(pointer)
)
???
))
)
)
For this implementation I would first write an idiomatic Common Lisp version of chunk that can be useful in a CL program (efficient, etc.), and then write a thin lodash layer that only wraps around those functions.
For example, I would first write a helper function to allow sharing storage with the chunked vector. A displaced array refers to another array but with an offset and different size. It may be useful to have chunks be only views of the original vector, so that they all share the same underlying storage array. It is not only a memory optimization: the behaviour is different when mutating either a chunk or the original vector, since any change in one is visible in the other. But as far as I know lodash is (was?) a pure functional language, so it makes sense to share some data if you don't mutate them. Some languages call those kind of indirect arrays "slices".
(defun slice (vector start end)
(make-array (- end start)
:element-type (array-element-type vector)
:displaced-to vector
:displaced-index-offset start))
So I would also make chunk-vector accept :start and :end parameters, as commonly done, along with sharedp which specifies if storage should be shared with the original vector:
(defun chunk-vector (size vector &key start end sharedp)
(check-type size (integer 1))
(loop
with slicer = (if sharedp #'slice #'subseq)
and low = (or start 0)
and high = (or end (length vector))
for s from low below high by size
for e from (+ low size) by size
collect (funcall slicer vector s (min e high))))
Note: I assume nil is a possible value for end that means the end of the vector, to mirror how subseq works. I do the same for start, because for those variables the nil value can be used without ambiguity to mean "default value". I could also have defined defaults in the lambda list, as done in tfb's answer.
Here are some tests:
(chunk-vector 3 #(0 1 2 3 4 5 6 7 8 9) :sharedp t)
(#(0 1 2) #(3 4 5) #(6 7 8) #(9))
(chunk-vector 2 #(0 1 2 3 4 5 6 7 8 9))
(#(0 1) #(2 3) #(4 5) #(6 7) #(8 9))
(chunk-vector 1 #(0 1 2 3 4 5 6 7 8 9))
(#(0) #(1) #(2) #(3) #(4) #(5) #(6) #(7) #(8) #(9))
Likewise, you could also define a chunk-list function and have the lodash chunck function dispatch to each specialized version based on the sequence type.
This can be done with CLOS, but since that is already demonstrated in another answer, I'll just define individual specialized functions.
Here is an implementation of chunk-list that is based on LDIFF.
I tried first mixing all cases in one function, but this becomes needlessly complex.
Here is first an unbounded chunk function:
(defun chunk-list/unbounded (size list)
(loop
for front = list then next
for next = (nthcdr size front)
collect (ldiff front next)
while next))
front is defined as initially list, then the current value of next at each step
next is the next chunk, computed using size; this plays nicely with lists that have not enough elements, since in that case nthcdr just returns the remaining elements.
A bit more complex case is required to handle the end argument, and for that we define the bounded version where there is also an additional upper-limit counter, that decreases by size at each step of iteration. It represents remaining number of elements to add, and is used along with size to compute (min size upper-limit), the size of the next chunk:
(defun chunk-list/bounded (size list upper-limit)
(loop
for front = list then next
for next = (nthcdr (min size upper-limit) front)
collect (ldiff front next)
do (decf upper-limit size)
while (and next (plusp upper-limit))))
Finally, chunk-list dispatches on both versions based on whether end is nil or not; the calls are inlined here (because we can):
(defun chunk-list (size list &key (start 0) end)
(declare (inline check-list/bounded check-list/simple))
(check-type size (integer 1))
(let ((list (nthcdr start list)))
(when list
(if end
(chunk-list/bounded size list (- end start))
(chunk-list/unbounded size list)))))
Some examples:
(chunk-list 3 '(1 2 3 4 5 6 7))
((1 2 3) (4 5 6) (7))
(chunk-list 29 '(1 2))
((1 2))
(chunk-list 2 (alexandria:iota 100 :start 0) :start 10 :end 20)
((10 11) (12 13) (14 15) (16 17) (18 19))
i would propose step-by step slicing iterating over the chunk index (since you can easily find out the total amount of chunks), using dotimes.
this could look something like the following:
(defun chunked (seq size)
(let* ((total (length seq))
(amount (ceiling total size))
(res (make-array amount :fill-pointer 0)))
(dotimes (i amount res)
(vector-push (subseq seq (* i size) (min (* (1+ i) size) total))
res))))
CL-USER> (chunked "abcdefgh" 3)
;; #("abc" "def" "gh")
CL-USER> (chunked #*00101 2)
;; #(#*00 #*10 #*1)
CL-USER> (chunked (list :a :b :c :d :e) 1)
;; #((:A) (:B) (:C) (:D) (:E))
CL-USER> (chunked (list :a :b :c :d :e) 4)
;; #((:A :B :C :D) (:E))
This is an addendum to coredump's answer, as well as referring to a comment by Kaz. Most of this is about style, which is always a matter of opinion and I do not claim my opinion is better than theirs: I just think it is interesting to talk about the choices as Lisp programming is very much about style choice, since the language is so flexible compared to most others. The last section ('extending') might be interesting however.
Argument order
The problem with a signature which is (size vector ...) is that size can't be optional. If you want it to be, it can't be the first argument to the function. Whether that outweighs the easy utility of partial-application libraries I don't know (however, in the 'do the right thing' spirit, if I wrote a partial application library it would allow you to specify which args it was currying, so this would not be a problem).
So if size needs to be optional then the argument order must be (vector size ...).
Further, since coredump's answer uses keyword arguments, I would make size be one as well as you almost never want to mix keyword & optional arguments. So that leads to a signature which would be (vector &key size start end sharedp), and I'd then write the actual function as
(defun chunk-vector (vector &key (size 1) (start 0) (end (length vector))
(sharedp nil))
(check-type size (integer 1))
(let ((slicer (if sharedp #'slice #'subseq)))
(loop for s from start below end by size
for e from (+ start size) by size
collect (funcall slicer thing s (min e end)))))
This slightly improves on coredump's version by defaulting the arguments in the arglist rather than later.
Extending chunk-vector
Pretty obviously you might want to chunk other kinds of things, such as lists, and pretty obviously the algorithm for chunking a list will be very different than that for chunking a vector, because you really do not want to repeatedly call subseq on a list.
Well, this is what CLOS is for. First of all we can define a generic chunk function:
(defgeneric chunk (thing &key)
;; in real life we might want to specify some of the keyword
;; arguments at the GF level, but we won't
)
And now define methods for classes we care about. Firstly the method to chunk vectors, which is pretty much the previous function:
(defmethod chunk ((thing vector) &key
(size 1) (start 0) (end (length thing)) (sharedp nil))
(check-type size (integer 1))
(let ((slicer (if sharedp #'slice #'subseq)))
(loop for s from start below end by size
for e from (+ start size) by size
collect (funcall slicer thing s (min e end)))))
And now, for instance, one to chunk lists. Note this may be buggy, and there may be better ways of doing this.
(defmethod chunk ((thing list) &key
(size 1) (start 0) (end nil endp) (sharedp nil))
;; This does not implemenent SHAREDP: this could only be useful for
;; the last chunk, and since you don't know if you could share a
;; chunk until you have already walked the list it did not seem
;; worth it. It may also be buggy in its handling of END.
(declare (ignorable sharedp))
(flet ((next (lt)
(nthcdr size lt))
(the-chunk (lt p)
(loop for c below (if endp (min size (- end p)) size)
for e in lt
do (print c)
collect e)))
(loop for tail on (nthcdr start thing) by #'next
for pos upfrom start by size
while (or (not endp) (< pos end))
collect (the-chunk tail pos))))
And of course you can now define methods on this function for other appropriate types.
The input could certainly be a vector (a vector is a 1-dimensional array). Lisp has a few more sensible options of how to represent the result: it could be a 2-dimensional array, a vector of vectors, or maybe even a list of vectors.
To get a 2-dimensional array:
(defun reshape-2d (column-count vector &optional padding-element)
(let* ((row-count (ceiling (length vector) column-count))
(array (make-array (list row-count column-count)
:initial-element padding-element)))
(loop :for i :below (length vector)
:do (setf (row-major-aref array i) (aref vector i)))
array))
To get a vector of vectors:
(defun chunkv (size vector)
(let ((vectors (make-array (ceiling (length vector) size))))
(loop :for i :below (length vector) :by size
:for j :below (length vectors)
:do (setf (aref vectors j) (subseq vector
i
(min (1- (length vector))
(+ i size)))))
vectors))
To get a list of vectors:
(defun chunkl (size vector)
(loop :for i :below (length vector) :by size
:collect (subseq vector
i
(min (1- (length vector))
(+ i size)))))
This last version could actually chunk any sequence because it only uses sequence functions.
If I create a closure like this,
(let ((A (make-array '(10) :initial-element 5)))
(defun h (i)
(aref a i))
(defsetf h (i) (x) `(setf (aref ,a ,i) ,x)))
then, as I expect, (h i) will return the i-th element of a:
(h 1) ;; => 5
(h 2) ;; => 5
Butalthough the setf expansion semes to work and correctly set the i-th element of a, it also produces a warning in SBCL:
(setf (h 1) 10)
; in: SETF (H 1)
; (SETF (AREF #(5 10 5 5 5 5 5 5 5 5) 1) #:G1124)
; --> LET* MULTIPLE-VALUE-BIND LET FUNCALL SB-C::%FUNCALL
; ==>
; ((SETF AREF) #:NEW0 #(5 10 5 5 5 5 5 5 5 5) 1)
;
; caught WARNING:
; Destructive function (SETF AREF) called on constant data.
; See also:
; The ANSI Standard, Special Operator QUOTE
; The ANSI Standard, Section 3.2.2.3
;
; compilation unit finished
; caught 1 WARNING condition
In GCL an error is signalled:
>(setf (h 1) 10)
Error:
Fast links are on: do (si::use-fast-links nil) for debugging
Signalled by LAMBDA-CLOSURE.
Condition in LAMBDA-CLOSURE [or a callee]: INTERNAL-SIMPLE-UNBOUND-VARIABLE: Cell error on A: Unbound variable:
Broken at LIST. Type :H for Help.
1 Return to top level.
In CLISP and ECL, the example works just fine.
I am returning to Common Lisp after writing Scheme for a couple of years, so I may be mixing the two languages, conceptually. I suppose I have triggered behavior that is undefined according to the spec, but I can't see exactly what I did wrong. I would appreciate any help with this!
Your Problem
It is often instructive to try macroexpand:
(macroexpand '(setf (h 2) 7))
==>
(LET* ()
(MULTIPLE-VALUE-BIND (#:G655)
7
(SETF (AREF #(5 5 5 5 5 5 5 5 5 5) 2) #:G655)))
As you can see, your setf call expands into a form which calls setf on a literal array which is a bad idea in general and, in fact, this is precisely what SBCL is warning you about:
Destructive function (SETF AREF) called on constant data.
Note that despite the warning SBCL (and other conformant implementations like CLISP and ECL) will do what you expect them to do.
This is because the literal array is referred to by the local variable which is accessible to the function h.
Solution
I suggest that you use a function instead
(let ((A (make-array '(10) :initial-element 5)))
(defun h (i)
(aref a i))
(defun (setf h) (x i)
(setf (aref a i) x)))
For Project Euler Problem 8, I am told to parse through a 1000 digit number.
This is a brute-force Lisp solution, which basically goes through every 5 consecutive digits and multiplies them from start to finish, and returns the largest one at the end of the loop.
The code:
(defun pep8 ()
(labels ((product-of-5n (n)
(eval (append '(*)
(loop for x from n to (+ n 5)
collect (parse-integer
1000digits-str :start x :end (+ x 1)))))))
(let ((largestproduct 0))
(do ((currentdigit 0 (1+ currentdigit)))
((> currentdigit (- (length 1000digits-str) 6)) (return largestproduct))
(when (> (product-of-5n currentdigit) largestproduct)
(setf largestproduct (product-of-5n currentdigit)))))))
It compiles without any warnings, but upon running it I get:
no non-whitespace characters in string "73167176531330624919225119674426574742355349194934...".
[Condition of type SB-INT:SIMPLE-PARSE-ERROR]
I checked to see if the local function product-of-5n was working by writing it again as a global function:
(defun product-of-5n (n)
(eval (append '(*)
(loop for x from n to (+ n 5)
collect (parse-integer
1000digits-str :start x :end (+ x 1))))))
This compiled without warnings and upon running it, appears to operate perfectly. For example,
CL_USER> (product-of-5n 1) => 882
Which appears to be correct since the first five digits are 7, 3, 1, 6 and 7.
As for 1000digits-str, it was simply compiled with defvar, and with Emacs' longlines-show-hard-newlines, I don't think there are any white-space characters in the string, because that's what SBCL is complaining about, right?
I don't think there are any white-space characters in the string, because that's what SBCL is complaining about, right?
The error-message isn't complaining about the presence of white-space, but about the absence of non-white-space. But it's actually a bit misleading: what the message should say is that there's no non-white-space in the specific substring to be parsed. This is because you ran off the end of the string, so were parsing a zero-length substring.
Also, product-of-5n is not defined quite right. It's just happenstance that (product-of-5n 1) returns the product of the first five digits. Strings are indexed from 0, so (product-of-5n 1) starts with the second character; and the function iterates from n + 0 to n + 5, which is a total of six characters; so (product-of-5n 1) returns 3 × 1 × 6 × 7 × 1 × 7, which happens to be the same as 7 × 3 × 1 × 6 × 7 × 1.
EVAL is not a good idea.
Your loop upper bound is wrong.
Otherwise I tried it with the number string and it works.
It's also Euler 8, not 9.
This is my version:
(defun euler8 (string)
(loop for (a b c d e) on (map 'list #'digit-char-p string)
while e maximize (* a b c d e)))
since I don't know common lisp, I slightly modified your code to fit with elisp. As far as finding bugs go and besides what have been said ((product-of-5n 1) should return 126), the only comment I have is that in (pep8), do length-4 instead of -6 (otherwise you loose last 2 characters). Sorry that I don't know how to fix your parse-error (I used string-to-number instead), but here is the code in case you find it useful:
(defun product-of-5n (n) ;take 5 characters from a string "1000digits-str" starting with nth one and output their product
(let (ox) ;define ox as a local variable
(eval ;evaluate
(append '(*) ;concatenate the multiplication sign to the list of 5 numbers (that are added next)
(dotimes (x 5 ox) ;x goes from 0 to 4 (n is added later to make it go n to n+4), the output is stored in ox
(setq ox (cons ;create a list of 5 numbers and store it in ox
(string-to-number
(substring 1000digits-str (+ x n) (+ (+ x n) 1) ) ;get the (n+x)th character
) ;end convert char to number
ox ) ;end cons
) ;end setq
) ;end dotimes, returns ox outside of do, ox has the list of 5 numbers in it
) ;end append
) ;end eval
) ;end let
)
(defun pep8 () ;print the highest
(let ((currentdigit 0) (largestproduct 0)) ;initialize local variables
(while (< currentdigit (- (length 1000digits-str) 4) ) ;while currentdigit (cd from now on) is less than l(str)-4
;(print (cons "current digit" currentdigit)) ;uncomment to print cd
(when (> (product-of-5n currentdigit) largestproduct) ;when current product is greater than previous largestproduct (lp)
(setq largestproduct (product-of-5n currentdigit)) ;save lp
(print (cons "next good cd" currentdigit)) ;print cd
(print (cons "with corresponding lp" largestproduct)) ;print lp
) ;end when
(setq currentdigit (1+ currentdigit)) ;increment cd
) ;end while
(print (cons "best ever lp" largestproduct) ) ;print best ever lp
) ;end let
)
(setq 1000digits-str "73167176531330624919")
(product-of-5n 1)
(pep9)
which returns (when ran on the first 20 characters)
"73167176531330624919"
126
("next good cd" . 0)
("with corresponding lp" . 882)
("next good cd" . 3)
("with corresponding lp" . 1764)
("best ever lp" . 1764)
I've done this problem some time ago, and there's one thing you are missing in the description of the problem. You need to read consequent as starting at any offset into a sting, not only the offsets divisible by 5. Therefore the solution to the problem will be more like the following:
(defun pe-8 ()
(do ((input (remove #\Newline
"73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450"))
(tries 0 (1+ tries))
(result 0))
((= tries 5) result)
(setq result
(max result
(do ((max 0)
(i 0 (+ 5 i)))
((= i (length input)) max)
(setq max
(do ((j i (1+ j))
(current 1)
int-char)
((= j (+ 5 i)) (max current max))
(setq int-char (- (char-code (aref input j)) 48))
(case int-char
(0 (return max))
(1)
(t (setq current (* current int-char))))))))
input (concatenate 'string (subseq input 1) (subseq input 0 1)))))
It's a tad ugly, but it illustrates the idea.
EDIT sorry, I've confused two of your functions. So that like was incorrect.