The :KEY parameter is included in some functions that ship with Common Lisp. All of the descriptions that I have found of them are unhelpful, and :KEY is difficult to search in a search engine because the ":" is usually ignored.
How would it be used, for example, in the member function which allows both :TEST and :KEY?
The :key argument is documented, somewhat cryptically, in the introductory sections to the Sequences Library (Section 17) in the Common Lisp HyperSpec, under 17.2.1 Satisfying a Two-Argument Test as well as 17.2.2 Satisfying a One-Argument Test. This is because its behavior is consistent across the library.
Quite simply, :key specifies the function which is applied to the elements of the sequence or sequences being processed. The return value of the function is used in place of those elements. In the terminology of some functional languages, this is called a "projection". The elements are projected through the key function. You can imagine that the default key function is identity, if you don't supply this argument.
One important thing to understand is that in functions which accept some object argument and a sequence (for instance functions which search a sequence for the occurrence of an object), the key function is not applied to the input object; only to the elements of the sequence.
The second important thing is that :key doesn't substitute for the item, only for the value which is used to identify the item. For instance, a function which searches for an item in a sequence will retrieve the original item from a sequence, even if the items of the sequence are projected to alternative keys via :key. The value retrieved by the key function is only used for comparison.
E.g. if obj-list is a list of objects which have a name accessible via a function called obj-name, we might look for the object named "foo" using (find "foo" obj-list :key #'obj-name). The function obj-name is applied to each element, and its result is compared with the string "foo" (to which the function isn't applied). If at least one object by that name exists in obj-list, then the first such object is returned.
The :key argument is a function of one parameter; it is applied to each element of the sequence to generate the value used for testing. If omitted, the identity function is used.
Here's an example from the CLHS:
(member 2 '((1 . 2) (3 . 4)) :test-not #'= :key #'cdr) => ((3 . 4))
Imagine that we have a list of cities:
(defparameter *cities*
; City Population Area km^2
'((Paris 2265886 105.4)
(Mislata 43756 2.06)
(Macau 643100 30.3)
(Kallithea 100050 4.75)
(Nea-Smyrni 73090 3.52)
(Howrah 1072161 51.74)))
Now we can compute the population density in people/km^2
(defun city-density (city)
"the density is the population number divided by the area"
(/ (second city) (third city)))
Now we want to compute a list of all cities which have a density less than 21000 people/km^2.
We remove all larger ones from the list and are providing a :test-not function. We need to provide an anonymous function which does the test and computes the density of the city to compare.
CL-USER 85 > (remove 21000 *cities*
:test-not (lambda (a b)
(>= a (city-density b))))
((NEA-SMYRNI 73090 3.52) (HOWRAH 1072161 51.74))
We can write it simpler without the anonymous function by providing the numeric :test-not function >= and use the city-density function as the key to compute the value from each provided cities:
CL-USER 86 > (remove 21000 *cities* :test-not #'>= :key #'city-density)
((NEA-SMYRNI 73090 3.52) (HOWRAH 1072161 51.74))
So having both a test predicate and a key function makes it easier to provide the building blocks for sequence computations...
Now imagine that we use CLOS and a list of city CLOS objects:
(defclass city ()
((name :initarg :name :reader city-name)
(population :initarg :population :reader city-population)
(area :initarg :area :reader city-area)))
(defparameter *city-objects*
(loop for (name population area) in *cities*
collect (make-instance 'city
:name name
:population population
:area area)))
(defmethod density ((c city))
(with-slots (population area)
c
(/ population area)))
Now we compute the list as above:
CL-USER 100 > (remove 21000 *city-objects* :test-not #'>= :key #'density)
(#<CITY 42D020DDFB> #<CITY 42D020DF23>)
CL-USER 101 > (mapcar #'city-name *)
(NEA-SMYRNI HOWRAH)
If we have the density as a slot with a getter, we can do this:
(defclass city ()
((name :initarg :name :reader city-name)
(population :initarg :population :reader city-population)
(area :initarg :area :reader city-area)
(density :reader city-density)))
(defmethod initialize-instance :after ((c city) &key)
(with-slots (density)
c
(setf density (density c))))
(defparameter *city-objects*
(loop for (name population area) in *cities*
collect (make-instance 'city
:name name
:population population
:area area)))
Now we compute the list as above, but the key is the getter of the density slot:
CL-USER 102 > (remove 21000 *city-objects* :test-not #'>= :key #'city-density)
(#<CITY 42D026D7EB> #<CITY 42D026D913>)
CL-USER 103 > (mapcar #'city-name *)
(NEA-SMYRNI HOWRAH)
Related
In short: (gethash 'PARIS pandemic-hash-table) is returning nil, despite 'PARIS being a key in the table; this seems to be related to quoting/evaluation of symbols during the hash-table creation in some way but I can't figure it out.
I'm playing with a graph search (testing for the shortest route between cities in the board game Pandemic; just for fun - trying to find optimal research lab placement in a way more sophisticated than 'has the most edges'). I'm using a hash table to hold the route data (nodes and edges), and, needed to input the data as a preliminary:
(defvar *nodes* '('San-Francisco 'Chicago 'Atlanta 'Washington 'Montreal 'New-York 'Madrid 'Paris 'London 'Essen 'Milan 'St-Petersburg))
(defvar *edges* '(('Chicago 'St-Petersburg)
('San-Francisco 'Atlanta 'Montreal)
('Chicago 'Washington)
('Atlanta 'Montreal 'New-York)
('Chicago 'Washington 'New-York)
('Montreal 'Washington 'Madrid 'London)
('New-York 'London 'Paris)
('Madrid 'Essen 'London 'Milan)
('Madrid 'Essen 'London 'New-York)
('London 'Paris 'Milan 'St-Petersburg)
('Paris 'Essen)
('Essen 'Chicago)))
(defvar *pandemic-node-hash* (make-hash-table))
(loop for node in *nodes*
for edges in *edges*
do (setf (gethash node *pandemic-node-hash*) edges))
If I look at the resulting hash table:
CL-USER> (loop for key being the hash-keys of *pandemic-node-hash*
do (print key))
'SAN-FRANCISCO
... ;other keys removed for brevity
'PARIS
NIL
So it's making the table (and the edges show up similarly), but, (gethash 'PARIS *pandemic-node-hash*) returns nil. If I then add another 'PARIS node directly (setf (gethash 'paris *pandemic-node-hash*) 'somevalue), and check the keys, I get:
(loop for key being the hash-keys of *pandemic-node-hash*
do (print key))
'other keys
'PARIS
PARIS
NIL
So, the problem has something to do with the evaluation of the symbols ('PARIS and friends) in the initial hash table creation loop, but I can't quite figure out what's going on or how to do that correctly. I'm guessing node evaluates to the un-evaluated symbol, passing that to gethash ... but what's the right way? Surely not (eval node)? Backtick the list, with commas in front of the symbols? (ugh).
Remember: 'foo is the short form for (quote foo). It's a list with two elements: the symbol CL:QUOTE and the symbol FOO.
(defun show-it (arg)
(print (list arg (type-of arg)))
(values))
Above is a smaller helper function for this answer.
CL-USER 37 > (show-it 'hamburg)
(HAMBURG SYMBOL)
Above shows that the function sees the symbol hamburg and not the value of a variable hamburg.
Not
CL-USER 38 > (show-it '('hamburg))
(((QUOTE HAMBURG)) CONS)
Above: the function sees a nested list, with a list, which has quote as a symbol.
Note: ((quote hamburg)) can be written shorter as('hamburg).
CL-USER 39 > (show-it (first '('hamburg)))
((QUOTE HAMBURG) CONS)
Above: if we get the first element, we get the list with the quote symbol.
Better
CL-USER 40 > (show-it '(hamburg))
((HAMBURG) CONS)
Above provides a list with one symbol, the symbol hamburg.
CL-USER 41 > (show-it (first '(hamburg)))
(HAMBURG SYMBOL)
Above gets the first element, which is the symbol hamburg.
Evaluation
Make sure that you understand evaluation in Lisp. quote blocks evaluation for the whole quoted expression and on all levels of those. Thus it makes no sense to quote contents inside a quoted list.
When passing arguments in a function call, then quoting is used to prevent lists and symbols to be evaluated. Thus quote creates literal data: literal symbols, literal lists, literal vectors, etc.
Thus quoting is a mechanism of code, not of data.
Code:
(first '(hamburg)) vs. (first (hamburg))
getting the first element of a literal list vs.
getting the first element of the result
of calling the function `hamburg`.
Data:
(paris hamburg berlin rome) vs. ('paris 'hamburg 'berlin 'rome)
A list of city names vs. a list of city names,
each nested in a list (quote ...)
Thus:
('paris 'hamburg 'berlin 'rome) makes no sense.
Why not
(dolist (node *nodes*)
(dolist (edges *edges*)
(setf (gethash node *pandemic-node-hash*) edges)))
But also you've double quoted your symbols (as someone else commented)
Why:
'(('Chicago 'St-Petersburg) ...)
when it should probably be just this (don't quote the list and each symbol)
'((Chicago St-Petersburg) ...)
You would see this if you evaluated: *edges*
Prelude
In Raku there's a notion called infinite list AKA lazy list which is defined and used like:
my #inf = (1,2,3 ... Inf);
for #inf { say $_;
exit if $_ == 7 }
# => OUTPUT
1
2
3
4
5
6
7
I'd like to implement this sort of thing in Common Lisp, specifically an infinite list of consecutive integers like:
(defun inf (n)
("the implementation"))
such that
(inf 5)
=> (5 6 7 8 9 10 .... infinity)
;; hypothetical output just for the demo purposes. It won't be used in reality
Then I'll use it for lazy evaluation like this:
(defun try () ;; catch and dolist
(catch 'foo ;; are just for demo purposes
(dolist (n (inf 1) 'done)
(format t "~A~%" n)
(when (= n 7)
(throw 'foo x)))))
CL-USER> (try)
1
2
3
4
5
6
7
; Evaluation aborted.
How can I implement such an infinite list in CL in the most practical way?
A good pedagogical approach to this is to define things which are sometimes called 'streams'. The single best introduction to doing this that I know of is in Structure and Interpretation of Computer Programs. Streams are introduced in section 3.5, but don't just read that: read the book, seriously: it is a book everyone interested in programming should read.
SICP uses Scheme, and this sort of thing is more natural in Scheme. But it can be done in CL reasonably easily. What I've written below is rather 'Schemy' CL: in particular I just assume tail calls are optimised. That's not a safe assumption in CL, but it's good enough to see how you can build these concepts into a language which does not already have them, if your language is competent.
First of all we need a construct which supports lazy evaluation: we need to be able to 'delay' something to create a 'promise' which will be evaluated only when it needs to be. Well, what functions do is evaluate their body only when they are asked to, so we'll use them:
(defmacro delay (form)
(let ((stashn (make-symbol "STASH"))
(forcedn (make-symbol "FORCED")))
`(let ((,stashn nil)
(,forcedn nil))
(lambda ()
(if ,forcedn
,stashn
(setf ,forcedn t
,stashn ,form))))))
(defun force (thing)
(funcall thing))
delay is mildly fiddly, it wants to make sure that a promise is forced only once, and it also wants to make sure that the form being delayed doesn't get infected by the state it uses to do that. You can trace the expansion of delay to see what it makes:
(delay (print 1))
-> (let ((#:stash nil) (#:forced nil))
(lambda ()
(if #:forced #:stash (setf #:forced t #:stash (print 1)))))
This is fine.
So now, we'll invent streams: streams are like conses (they are conses!) but their cdrs are delayed:
(defmacro cons-stream (car cdr)
`(cons ,car (delay ,cdr)))
(defun stream-car (s)
(car s))
(defun stream-cdr (s)
(force (cdr s)))
OK, let's write a function to get the nth element of a stream:
(defun stream-nth (n s)
(cond ((null s)
nil)
((= n 0) (stream-car s))
(t
(stream-nth (1- n) (stream-cdr s)))))
And we can test this:
> (stream-nth 2
(cons-stream 0 (cons-stream 1 (cons-stream 2 nil))))
2
And now we can write a function to enumerate an interval in the naturals, which by default will be an half-infinite interval:
(defun stream-enumerate-interval (low &optional (high nil))
(if (and high (> low high))
nil
(cons-stream
low
(stream-enumerate-interval (1+ low) high))))
And now:
> (stream-nth 1000 (stream-enumerate-interval 0))
1000
And so on.
Well, we'd like some kind of macro which lets us traverse a stream: something like dolist, but for streams. Well we can do this by first writing a function which will call a function for each element in the stream (this is not the way I'd do this in production CL code, but it's fine here):
(defun call/stream-elements (f s)
;; Call f on the elements of s, returning NIL
(if (null s)
nil
(progn
(funcall f (stream-car s))
(call/stream-elements f (stream-cdr s)))))
And now
(defmacro do-stream ((e s &optional (r 'nil)) &body forms)
`(progn
(call/stream-elements (lambda (,e)
,#forms)
,s)
,r))
And now, for instance
(defun look-for (v s)
;; look for an element of S which is EQL to V
(do-stream (e s (values nil nil))
(when (eql e v)
(return-from look-for (values e t)))))
And we can then say
> (look-for 100 (stream-enumerate-interval 0))
100
t
Well, there is a lot more mechanism you need to make streams really useful: you need to be able to combine them, append them and so on. SICP has many of these functions, and they're generally easy to turn into CL, but too long here.
For practical purposes it would be wise to use existing libraries, but since the question is about how to implemented lazy lists, we will do it from scratch.
Closures
Lazy iteration is a matter of producing an object that can generate the new value of a lazy sequence each time it is asked to do so.
A simple approach for this is to return a closure, i.e. a function that closes over variables, which produces values while updating its state by side-effect.
If you evaluate:
(let ((a 0))
(lambda () (incf a)))
You obtain a function object that has a local state, namely here the variable named a.
This is a lexical binding to a location that is exclusive to this function, if you evaluate a second time the same expression, you'll obtain a different anonymous function that has its own local state.
When you call the closure, the value stored in a in incremented and its value is returned.
Let's bind this closure to a variable named counter, call it multiple times and store the successive results in a list:
(let ((counter (let ((a 0))
(lambda () (incf a)))))
(list (funcall counter)
(funcall counter)
(funcall counter)
(funcall counter)))
The resulting list is:
(1 2 3 4)
Simple iterator
In your case, you want to have an iterator that starts counting from 5 when writing:
(inf 5)
This can implemented as follows:
(defun inf (n)
(lambda ()
(shiftf n (1+ n))))
Here is there is no need to add a let, the lexical binding of an argument to n is done when calling the function.
We assign n to a different value within the body over time.
More precisely, SHIFTF assigns n to (1+ n), but returns the previous value of n.
For example:
(let ((it (inf 5)))
(list (funcall it)
(funcall it)
(funcall it)
(funcall it)))
Which gives:
(5 6 7 8)
Generic iterator
The standard dolist expects a proper list as an input, there is no way you can put another kind of data and expect it to work (or maybe in an implementation-specific way).
We need a similar macro to iterate over all the values in an arbitrary iterator.
We also need to specify when iteration stops.
There are multiple possibilities here, let's define a basic iteration protocol as follows:
we can call make-iterator on any object, along with arbitrary arguments, to obtain an iterator
we can call next on an iterator to obtain the next value.
More precisely, if there is a value, next returns the value and T as a secondary value; otherwise, next returns NIL.
Let's define two generic functions:
(defgeneric make-iterator (object &key)
(:documentation "create an iterator for OBJECT and arguments ARGS"))
(defgeneric next (iterator)
(:documentation "returns the next value and T as a secondary value, or NIL"))
Using generic functions allows the user to define custom iterators, as long as they respect the specified behaviour above.
Instead of using dolist, which only works with eager sequences, we define our own macro: for.
It hides calls to make-iterator and next from the user.
In other words, for takes an object and iterates over it.
We can skip iteration with (return v) since for is implemented with loop.
(defmacro for ((value object &rest args) &body body)
(let ((it (gensym)) (exists (gensym)))
`(let ((,it (make-iterator ,object ,#args)))
(loop
(multiple-value-bind (,value ,exists) (next ,it)
(unless ,exists
(return))
,#body)))))
We assume any function object can act as an iterator, so we specialize next for values f of class function, so that the function f gets called:
(defmethod next ((f function))
"A closure is an interator"
(funcall f))
Also, we can also specialize make-iterator to make closures their own iterators (I see no other good default behaviour to provide for closures):
(defmethod make-iterator ((function function) &key)
function)
Vector iterator
For example, we can built an iterator for vectors as follows. We specialize make-iterator for values (here named vec) of class vector.
The returned iterator is a closure, so we will be able to call next on it.
The method accepts a :start argument defaulting to zero:
(defmethod make-iterator ((vec vector) &key (start 0))
"Vector iterator"
(let ((index start))
(lambda ()
(when (array-in-bounds-p vec index)
(values (aref vec (shiftf index (1+ index))) t)))))
You can now write:
(for (v "abcdefg" :start 2)
(print v))
And this prints the following characters:
#\c
#\d
#\e
#\f
#\g
List iterator
Likewise, we can build a list iterator.
Here to demonstrate other kind of iterators, let's have a custom cursor type.
(defstruct list-cursor head)
The cursor is an object which keeps a reference to the current cons-cell in the list being visited, or NIL.
(defmethod make-iterator ((list list) &key)
"List iterator"
(make-list-cursor :head list))
And we define next as follows, specializeing on list-cursor:
(defmethod next ((cursor list-cursor))
(when (list-cursor-head cursor)
(values (pop (list-cursor-head cursor)) t)))
Ranges
Common Lisp also allows methods to be specialized with EQL specializers, which means the object we give to for might be a specific keyword, for example :range.
(defmethod make-iterator ((_ (eql :range)) &key (from 0) (to :infinity) (by 1))
(check-type from number)
(check-type to (or number (eql :infinity)))
(check-type by number)
(let ((counter from))
(case to
(:infinity
(lambda () (values (incf counter by) t)))
(t
(lambda ()
(when (< counter to)
(values (incf counter by) T)))))))
A possible call for make-iterator would be:
(make-iterator :range :from 0 :to 10 :by 2)
This also returns a closure.
Here, for example, you would iterate over a range as follows:
(for (v :range :from 0 :to 10 :by 2)
(print v))
The above expands as:
(let ((#:g1463 (make-iterator :range :from 0 :to 10 :by 2)))
(loop
(multiple-value-bind (v #:g1464)
(next #:g1463)
(unless #:g1464 (return))
(print v))))
Finally, if we add small modification to inf (adding secondary value):
(defun inf (n)
(lambda ()
(values (shiftf n (1+ n)) T)))
We can write:
(for (v (inf 5))
(print v)
(when (= v 7)
(return)))
Which prints:
5
6
7
I'll show it with a library:
How to create and consume an infinite list of integers with the GTWIWTG generators library
This library, called "Generators The Way I Want Them Generated", allows to do three things:
create generators (iterators)
combine them
consume them (once).
It is not unsimilar to the nearly-classic Series.
Install the lib with (ql:quickload "gtwiwtg"). I will work in its package: (in-package :gtwiwtg).
Create a generator for an infinite list of integers, start from 0:
GTWIWTG> (range)
#<RANGE-BACKED-GENERATOR! {10042B4D83}>
We can also specify its :from, :to, :by and :inclusive parameters.
Combine this generator with others: not needed here.
Iterate over it and stop:
GTWIWTG> (for x *
(print x)
(when (= x 7)
(return)))
0
1
2
3
4
5
6
7
T
This solution is very practical :)
I am trying to create a function games-won that consumes a list of Games, results, and a string, name, and produces the number of games in results that name won.
For example:
(define-struct game (winner loser high low))
(check-expect (games-won (list (make-game "Lori" "Troy" 52 34)
(make-game "Mary" "Lori" 30 20)) "Lori") 1)
Below is what I have so far:
(define (won? game name)
(equal? (game-winner game) name))
(define (wonlst results)
(filter won? results))
(define (lst-length lst)
(cond
[(empty? lst) 0]
[(cons? lst) (+ 1 (length (rest lst)))]))
(define (games-won results)
(cond
[(cons? (wonlst results)) (lst-length (wonlst results))]
[else 0]))
Can anyone help correct the errors in my code and maybe tell me how to use local and put the functions all together?
Here are the fixes:
As the test suggests, games-won should accept two arguments: a results list and a name. So we add a parameter - name - to games-won.
You don't need a custom lst-length function, you can just use length. Also, games-won doesn't need to worry about returning 0 in the else case. The base case is taken care of by the list-abstractions.
Note that won? takes in two inputs, but the predicate function in a filter only accepts one input. So we remove name from won?. Once we put won? in a local, it can just use name from the surrounding function's context.
We put a local in games-won and put the two helpers - won? and won-lst - in the local.
You should use string=? instead of equal?for since we know that name and winner field of game is always a String.
(define-struct game (winner loser high low))
; games-won : [List-of Game] String -> Number
(define (games-won results name)
(local (; won? : Game -> Boolean
(define (won? game)
(string=? (game-winner game) name))
; wonlst : [List-of Game] -> [List-of Game]
(define (wonlst results)
(filter won? results)))
(length (wonlst results))))
(define my-games1 (list (make-game "Lori" "Troy" 52 34)
(make-game "Mary" "Lori" 30 20)))
(check-expect (games-won my-games1 "Lori") 1)
We can just put everything in one function along with a lambda like the following:
(define (games-won results name)
(length (filter (λ (game) (string=? (game-winner game) name)) results)))
How to use local
A local expression has the following shape:
(local [definition ...] body-expression)
Within the square brackets, you can put as many definitions (i.e. defines, define-structs) as you want, and in the body of the local — body-expression — you can put any expression that may or may not use the definitions within the square brackets. The definitions are only available in the body.
From HtDP, here's how we compute with locals:
We rename the locally defined constants and functions to use names that aren’t used elsewhere in the program.
We lift the definitions in the local expression to the top level and evaluate the body of the local expression next.
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 ().
As part of some Eulerian travails, I'm trying to code a Sieve of Eratosthenes with a factorization wheel. My code so far is:
(defun ring (&rest content)
"Returns a circular list containing the elements in content.
The returned list starts with the first element of content."
(setf (cdr (last content)) content))
(defun factorization-wheel (lst)
"Returns a circular list containing a factorization
wheel using the list of prime numbers in lst"
(let ((circumference (apply #'* lst)))
(loop for i from 1 to circumference
unless (some #'(lambda (x) (zerop (mod i x))) lst)
collect i into wheel
finally (return (apply #'ring
(maplist
#'(lambda (x) ; Takes exception to long lists (?!)
(if (cdr x)
(- (cadr x) (car x))
(- circumference (car x) -1)))
wheel))))))
(defun eratosthenes (n &optional (wheel (ring 4 2)))
"Returns primes up to n calculated using
a Sieve of Eratosthenes and a factorization wheel"
(let* ((candidates (loop with s = 1
for i in wheel
collect (setf s (+ i s))
until (> s n))))
(maplist #'(lambda (x)
(if (> (expt (car x) 2) n)
(return-from eratosthenes candidates))
(delete-if
#'(lambda (y) (zerop (mod y (car x))))
(cdr x)))
candidates)))
I got the following result for wheels longer than 6 elements. I didn't really understand why:
21 > (factorization-wheel '(2 3 5 7 11 13))
(16 2 4 6 2 6 4 2 4 6 6 2 6 4 2 6 4 6 8 4 ...)
21 > (factorization-wheel '(2 3 5 7 11 13 17))
> Error: Too many arguments.
> While executing: FACTORIZATION-WHEEL, in process listener(1).
The algorithm seems to be working OK otherwise and churns out primes with wheels having 6 or fewer elements.
Apparently apply or ring turn up their noses when long lists are passed to them.
But shouldn't the list count as a single argument? I admit I'm thoroughly flummoxed. Any input is appreciated.
ANSI Common Lisp allows implementations to constrain the maximum number of arguments which can be passed to a function. This limit is given by call-arguments-limit can be as small as 50.
For functions which behave like algebraic group operators obeying the
associative property (+, list, and others), we can get around the limit by using reduce to decimate the input list while treating the function as binary.
For instance to add a large list of numbers: (reduce #'+ list) rather than (apply #'+ list).
Notes on reduce
In Common Lisp, reduce will appear to work even if the list is empty. Few other languages give you this, and it doesn't actually come from reduce: it won't work for all functions. But with + we can write (reduce #'+ nil) and it calculates zero, just like (apply #'+ nil).
Why is that? Because the + function can be called with zero arguments, and when called with zero arguments, it yields the identity element for the additive group: 0. This dovetails with the reduce function.
In some other languages the fold or reduce function must be given an initial seed value (like 0), or else a nonempty list. If it is given neither, it is an error.
The Common Lisp reduce, if it is given an empty list and no :initial-value, will call the kernel function with no arguments, and use the return value as the initial value. Since that value is then the only value (the list is empty), that value is returned.
Watch out for functions with special rules for the leftmost argument. For instance:
(apply #'- '(1)) -> -1 ;; same as (- 1), unary minus semantics.
(reduce #'- '(1)) -> 1 ;; what?
What's going on is that when reduce is given a one-element list, it just returns the element without calling the function.
Basically it is founded on the mathematical assumption mentioned above that if no :initial-value is supplied then f is expected to support (f) -> i, where i is some identity element in relation to f, so that (f i x) -> x. This is used as the initial value when reducing the singleton list, (reduce #'f (list x)) -> (f (f) x) -> (f i x) -> x.
The - function doesn't obey these rules. (- a 0) means "subtract zero from a" and so yields a, whereas (- a) is the additive inverse of a, probably for purely pragmatic, notational reasons (namely, not making Lisp programmers write (- 0 a) just to flip a sign, just for the sake of having - behave more consistently under reduce and apply). The - function also may not be called with zero arguments.
If we want to take a list of numbers and subtract them all from some value x, the pattern for that is:
(reduce #'- list-of-numbers :initial-value x)