Skip to content

Instantly share code, notes, and snippets.

@jaawerth
Last active January 19, 2022 02:04
Show Gist options
  • Save jaawerth/fb1f66090c640d2eeb6c95f598a9f5db to your computer and use it in GitHub Desktop.
Save jaawerth/fb1f66090c640d2eeb6c95f598a9f5db to your computer and use it in GitHub Desktop.
(local identity #$)
(local second #$2)
(local always-nil #nil)
(fn int? [v] (and (= :number (type v)) (= v (math.floor v))))
(fn assert-int [v ?msg ?lvl]
(if (int? v) v
(error (or ?msg (.. "expected value '" (tostring v) "' to be a number"))
(or ?lvl 2))))
(fn rawget-or-0 [self k] (or (rawget self k) 0)) ; (. t k) but bypass __index
(fn count-into [into-tgt ?op src ...]
(local op (or ?op second))
(local (gen param state)
(match (type src)
:function (values src ...)
:string (string.gmatch src ".")
:nil always-nil
_ (pairs src) ; assume table/userdata-with-__pairs as fallback;
; will result in error for number/boolean/non-iterable userdata
))
(accumulate [C into-tgt k n (values gen param state)]
(let [n (if (= n nil) 1 n)
n- (rawget-or-0 C k)]
(doto C (rawset k (op n- (assert-int n)) )))))
(local multi-set-mt nil) ; deferred 'til after *multi-set*'
(fn new-multi-set [src ...]
(setmetatable (count-into {} nil src ...) multi-set-mt))
(local *multi-set*
{:new new-multi-set
:get rawget-or-0 ; (. t k) but bypass methods
:update (λ update [self op ?src ...]
(count-into (new-multi-set self) op (or ?src self) ...))
:update! (λ [self op ?src ...] (count-into self op (or ?src self) ...))
:keys (fn [self] (icollect [k (pairs self)] k))
:add (fn [self addend] (update self #(+ $1 $2) addend))
:sub (fn [self subtr] (update self #(- $1 $2) subtr))})
(set-forcibly! multi-set-mt
{:__index (fn [self key] ; check methods, then fall back to 0
(or (. *multi-set* key) 0))
:__newindex (fn [self key n] (rawset self key (assert-int n)))
:__add *multi-set*.add
:__sub *multi-set*.sub})
*multi-set*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment