thanks @spdegabrielle
I wanted to find the first element in a list which has no duplicates. I came up with: (define xs '(1 1 1 1 1 1 1 1 1 1 1 1 2 3 3 3 3 3 3 3 3))
(for/first ([x (in-list xs)] #:when (= 1 (count (curry = x) xs))) x)
Which works — and looks cute :slightly_smiling_face: — but is probably horrendously inefficient. How can I make it better?
I guess doing this should be better? (for/first ([x (in-list (remove-duplicates xs))] #:when (= 1 (count (curry = x) xs))) x)
Feels like this could be done better using sets maybe.
(define (first-unique xs)
(define ht (make-hash))
(define (add! x) (hash-update! ht x add1 0))
(for-each add! xs)
(for/or ([x xs]) (and (= 1 (hash-ref ht x)) x)))
Nice (and fast).
My first idea was to use a multi set (bag), but I couldn’t find one.
Could use a hash map but that sounds inefficient.
(require rebellion/collection/multiset)
(define (first-unique xs)
(define m (list->multiset xs))
(for/or ([x xs])
(and (= 1 (multiset-frequency m x)) x)))
(require rebellion/collection/multiset)
(define (first-unique xs)
(define m (list->multiset xs))
(for/first ([x xs] #:when (= (multiset-frequency m x) 1))
x))
rebellion
?
googles
Yes Rebellion’s multiset is the same as a bag.
(sorry catching up)
This one works in one pass. It depends on the input being sorted though (per the example) (define (first-unique vs)
(define (step prev vs)
(match vs
[(list) #f]
[(list (== prev) vs ...)
(step prev vs)]
[(list x y vs ...)
(cond
[(equal? x y) (step y vs)]
[else x])]))
(match vs
[(list) #f]
[(cons v vs) (step v vs)]))
eyeballing it, that looks like it would return 2
for (list 1 2 3)
and raise a “no matching clause found” error for (list 1 2)
...
matches 0 or more,
so the two essential cases are covered, empty list and list of at least one element.
I didn’t know about the match expander ==
! Glad I know now.
Right. Don’t understand how that works :slightly_smiling_face:
What does (list (== prev) vs ...)
mean?
I realize I’m a long way from any sort of mastery of this :confused:
That branch matches lists of 0 elements, list of 1 or more elements where the first is equal to prev
, and other lists of 2 or more elements, but it doesn’t have a clause for lists of 1 or more elements where the first isn’t prev
er, lists of exactly one element which isn’t prev
beware that multisets are unordered! so you won’t necessarily get the first element with only one occurrence, you’ll just get an element
Good catch. An additional clause [(list x) x]
solves that.
Is the list sorted?
Nice procrastination exercise for me :slightly_smiling_face: Assumes a sorted list where #f
is not a valid element.
@badkins If you’d like to get it to work with lists containing #f
, Rebellion also provides an option
type which is either a (present v)
value or the absent
constant.
Thanks @notjack - this was just a “what’s the first one-pass solution that pops out of my head” sort of thing :slightly_smiling_face:
It’s a great solution :simple_smile:
I don’t like seeing multiple (loop (cdr lst)
though
Tangent: there’s also “reducers” in Rebellion which are basically “one-pass operations on sequences”. Your approach would make for a good reducer, which would make it work on arbitrary sequences.
I’ll definitely have to check out Rebellion.
Hmm… I thought I’d try and create a macro next
that I could use as (next #f element)
instead of (loop (cdr lst) #f element)
- it’s not as trivial as I’d hoped. What is the general mechanism for referring to an existing binding such as loop
and lst
?
To clarify, I’d like to the macro to refer to a binding that doesn’t exist at macro definition time, but does exist at macro invocation time.
Could you make a helper function that calls loop
?
@notjack I’m not positive, but I don’t think so. I’m not exactly sure how “named let” is implemented, but I just tried a simple helper unsuccessfully
And even if it’s possible, I’d really like to know how to refer to bindings in a “simple substitution” manner.
oops - hold on!
<sigh> I had a stupid error that probably affected all of my macro attempts also :disappointed: Yes, the helper works fine.
How to input characters like “ℕ” in DrRacket with keybind, like “\alpha” and then “C-“?
On the bright side, now you’ve got it working :grin:
(define-simple-macro (next a b)
#:with loop (datum->syntax #'a 'loop)
#:with lst (datum->syntax #'a 'lst)
(loop (cdr lst) a b))
@badkins if you define the macro locally, inside the scope where you want to use it, it should work without the #:with
clauses
Confirmed
I have a pict
question. I have a png image which I would like to draw on top of. I want to draw a green tick or a red cross on the png. How do I do that?
Or maybe I need another library?
The dc
function creates a pict?
from arbitrary drawing procedure https://docs.racket-lang.org/pict/Basic_Pict_Constructors.html#(def._((lib._pict%2Fmain..rkt)._dc))
and bitmap
creates a pict?
from an image file. I dont’ know if there’s a better approach though.
I found read-bitmap
from racket/draw
too. I’ll try them all out thanks.
read-bitmap
is for creating bitmap%
objects afaik
you are correct
I’m not sure what the relationship between the two libraries is or where is best to start.
IIUC, bitmap%
from racket/draw
is a lower-level primitive that let you draw almost everything, working at the pixel level
@aymano.osman You can turn the png into a pict, then make a separate pict with your green tick / red cross, then combine them with cc-superimpose
which will put one pict directly on top of the center of another
and pict
is an abstraction for pictures
And the bitmap
function will, given a path to a png file, read that file and give you back a pict
So if you have /path/to/base-image.png
and /path/to/green-tick.png
, you can combine them with this: (define base-image (bitmap "/path/to/base-image.png"))
(define green-tick (bitmap "/path/to/green-tick.png"))
(cc-superimpose base-image green-tick)
perfect :slightly_smiling_face: thanks
@aymano.osman You’re welcome :simple_smile: also, here’s some real-world example code where I loaded a png file into a pict: https://github.com/jackfirth/chess/blob/346d0506d367890ee67f67f2f2495dda01e1ca4e/pict.rkt#L34-L82
Nice. That looks like it will be very helpful.