• how do I do setf over recursive function?

    From Bigos@21:1/5 to All on Mon Sep 19 06:56:41 2022
    Can the following be done with setf?


    (defun pht (obj &key (test 'eql) (size 7))
    (alexandria:plist-hash-table obj :test test :size size))

    (defun recpath (root path)
    (if (endp path)
    root
    (recpath (gethash (first path) root) (rest path))))

    (defun hashpath (root path)
    (if (null (cdr path))
    root
    (hashpath (gethash (first path) root) (rest path))))

    (defun try-me ()
    (let ((dat (pht
    (list
    :windows (pht (list
    :win1 'win1
    :win2 'win2))))))
    (recpath dat '(:windows :win1))
    (let ((h (hashpath dat '(:windows :win1))))
    (setf (gethash :win1 h) :win111))
    (recpath dat '(:windows :win1))
    ))

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bigos@21:1/5 to All on Mon Sep 19 07:04:35 2022
    I mean the fragment


    (let ((h (hashpath dat '(:windows :win1))))
    (setf (gethash :win1 h) :win111))

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bigos@21:1/5 to All on Mon Sep 19 07:23:16 2022
    what about the following? How do I writhe variant with setf?



    (defun hashpath (root path)
    (if (null (cdr path))
    root
    (hashpath (gethash (first path) root) (rest path))))

    (defun set-hashpath (root path new-value)
    (if (null (cdr path))
    (progn
    (setf (gethash (car path) root) new-value)
    root)
    (set-hashpath (gethash (first path) root) (rest path) new-value)))

    (defun try-me ()
    (let ((dat (pht
    (list
    :windows (pht (list
    :win1 'win1
    :win2 'win2))))))
    (recpath dat '(:windows :win1))
    (set-hashpath dat '(:windows :win1) "win 11111")

    (recpath dat '(:windows :win1))
    ))

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bigos on Mon Sep 19 17:41:32 2022
    On 2022-09-19, Bigos <ruby.object@googlemail.com> wrote:
    Can the following be done with setf?


    (defun pht (obj &key (test 'eql) (size 7))
    (alexandria:plist-hash-table obj :test test :size size))

    (defun recpath (root path)
    (if (endp path)
    root
    (recpath (gethash (first path) root) (rest path))))

    (defun hashpath (root path)
    (if (null (cdr path))
    root
    (hashpath (gethash (first path) root) (rest path))))

    The quick and dirty way would be to provide another function:

    (defun set-hashpath (root path new-value) ...)

    Then the short form of defsetf:

    (defsetf hashpath set-hashpath)

    The disadvantage is that modify operations like
    (incf (hashpath ...)) and (push whatever (hashpath ...))
    walk the path twice.

    To improve that you can have some way of retrieving
    a locative value from the path:

    (hashpath-loc root path)
    (hashpath-get loc)
    (hashpath-set loc)

    Then (let ((loc (hashpath-loc root path))) (hashpath-get loc))
    is equivalent to (hashpath root path), but we keep that
    function.

    You can then use define-setf-expander to register the
    hashpath place in such a way that the expander uses the
    location-based API. E.g. (incf (hashpath ...))
    turns into code like this

    (let ((#:loc0023 (hashpath-loc ...)))
    (incf (hashpath-get #:loc0023)))

    We could probably capture this whole pattern into
    a simple defining facility where you specify
    the the function for getting the loc from
    the arguments, and the loc accessors.

    Here is how that might go:

    (defmacro define-loc-setf (name loc-constructor loc-getter loc-setter)
    `(progn
    (defsetf ,loc-getter ,loc-setter)
    (let ((loc (gensym "LOC-"))
    (store (gensym "STORE-")))
    (define-setf-expander ,name (&rest args)
    (values (list loc)
    (list `(,',loc-constructor ,@args))
    (list store)
    `(,',loc-setter ,loc ,store)
    `(,',loc-getter ,loc))))))

    ;; Test: define a path place (COOL-PATH args ...) for which we can obtain
    ;; a location using (COOL-PATH-LOC args ...) and then get/set via the location. ;; None of these functions exist; we can use this to verify that we
    ;; get a right-looking expansion.

    (define-loc-setf cool-path cool-path-loc cool-loc-get cool-loc-set)

    Now try expanding a for which increments a cool-path place:

    (macroexpand '(incf (cool-path a b c d e)))
    (LET* ((#:LOC-3318 (COOL-PATH-LOC A B C D E))
    (#:STORE-3319 (+ (COOL-LOC-GET #:LOC-3318) 1)))
    (COOL-LOC-SET #:LOC-3318 #:STORE-3319)) ;
    T

    Looks about right. The place arguments are passed to cool-path-loc,
    and then the transaction is done with the loc accessors.

    Caveat: I've not tested this beyond just looking at expansions.

    Note: the cool-loc-set function must return the new value, otherwise the
    result value semantics of setf and other operators breaks.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)