From Newsgroup: comp.lang.misc
On 2024-07-29, HenHanna <
HenHanna@devnull.tb> wrote:
@Memoize doesn't work in Python 3 ?
Do we have this in Gauche scheme? (Common Lisp?)
In TXR Lisp there is something called parameter list macros,
or parameter macros, for short.
It is my invention.
Parameter list macros are named after keyword symbols.
When a keyword symbol occurs at the head of a macro or lambda
parameter list, if that symbol has a parameter macro binding,
the expander is invoked. The expander takes four arguments:
the parameter list, the body, a macro environmet and the entire
form that is defining the function (for error reporting).
The expander must produce a transformed parameter list, and
transformed function body.
No memoizing parameter macro is included in the language,
but the manual gives an example of how to write one. Excerpt:
The following example shows the implementation of a parameter
macro :memo which provides rudimentary memoization. Using the
macro is extremely easy. It is a matter of simply inserting the
:memo keyword at the front of a function's parameter list. The
function is then memoized.
(defvarl %memo% (hash :weak-keys))
(defun ensure-memo (sym)
(or (gethash %memo% sym)
(sethash %memo% sym (hash))))
(define-param-expander :memo (param body)
(let* ((memo-parm [param 0..(posq : param)])
(hash (gensym)) (key (gensym)))
^(,param (let ((,hash (ensure-memo ',hash))
(,key (list ,*memo-parm)))
(or (gethash ,hash ,key)
(sethash ,hash ,key (progn ,*body)))))))
The above :memo macro may be used to define a memoized Fibonacci
function as follows:
(defun fib (:memo n)
(if (< n 2)
(clamp 0 1 n)
(+ (fib (pred n)) (fib (ppred n)))))
All that is required is the insertion of the :memo keyword.
Not shown in the example is that :memo only memoizes over the
required parameters (ones before the colon : symbol that separates
fixed from optional). :memo does nothing with the parameters; they
are returned as is.
TXR Lisp does not have keywords in the native function calling model, but provides a :key parameter macro that implements them via code transformation. This uses -- to separate the specification of key parameters:
(lambda (:key a b c -- foo bar (xyzzy 42))
...)
There is no strict checking (i.e. what you opt out of in Common Lisp
by means of &allow-other-keys).
The entire implementation is here:
https://www.kylheku.com/cgit/txr/tree/stdlib/keyparams.tl
An interesting observation is that TXR Lisp also has a defset
macro (similar to Common Lisp defsetf).
When you use defset to define a place with keywords, it
Just Works, even though defset knows nothing about :key at all.
Like in the test cases related to (defset :foo ...)
in this file:
https://www.kylheku.com/cgit/txr/tree/tests/012/defset.tl
I was blown away, not expecting that to work.
Given
(defset foo (:key x y -- a b c (d 4)) n ^(bar ,x ,y, a, b, c ,d ,n))
With the defset macro having no knowledge of the existence of :key,
this works:
(inc (foo 1 2 :a 3 :b 4) 5) ;; increment (foo ...) place by 5
producing an expansion like:
(let ((#:new-value (+ (foo 1 2 :a 3 :b 4) 5)))
(bar 1 2 3 4 () 4 #:new-value)))
The :a and :b key values are decoded and inserted in the right spot in the (bar form), and this happens when the place defined by defset is expanded by a place-mutating form like inc.
More complex example showing that the arguments are evaluated just
once by the increment operation:
2> (expand '(inc (foo (one) (two) :a (three) :b (four)) 5))
(let ((#:g0074 (one))
(#:g0075 (two))
(#:g0076 (three))
(#:g0077 (four)))
(let ((#:g0026 (+ (foo #:g0074
#:g0075 :a
#:g0076 :b
#:g0077)
5)))
(bar #:g0074
#:g0075 #:g0076
#:g0077 ()
4 #:g0026)))
Common Lisp implementations of defsetf typically fudge around with keyword argument parsing in order to make stuff like this work.
--
TXR Programming Language:
http://nongnu.org/txr
Cygnal: Cygwin Native Application Library:
http://kylheku.com/cygnal
Mastodon: @
Kazinator@mstdn.ca
--- Synchronet 3.20a-Linux NewsLink 1.114