Hello, thanks for looking. I’ve been dabbling with Racket on and off for a while now and used it to write this “raffle” program. I got the repeater function from an old Racket mailing list entry. Everything works fine in the program I just am trying to learn different ways to think about applying functions. In this case we’re using the anonymous function to mutate a hash that keeps track of the winners of the “raffle” and I think it might be more desirable to return the hash rather than declare it outside of the function and mutate it within. Is there a way to use “for” so that it returns a value that I could then use to increment a local copy of a Hash?
@mwb some thoughts:
+1
/ -1
doesn’t make sense. just use 0..length
.
you’re passing in user-list to random-member, but only half using the variable.
The length is fixed, so store it off and use that. This is needlessly slow.
list-ref will also be costly, consider using vectors or something else instead.
;; yours (lists):
;; lists + list-ref + hash-set! : cpu time: 1784 real time: 1782 gc time: 0
;; mine (vectors):
;; for/fold + hash-set + hash-ref : cpu time: 396 real time: 396 gc time: 23
;; let + for + hash-set! : cpu time: 240 real time: 241 gc time: 0
;; for/fold + hash-update : cpu time: 430 real time: 430 gc time: 20
I wound up with code that looks like:
(time
(void
(for/fold ((h (hash)))
((_ (in-range 1000000)))
(let* ((i (random 0 user-length))
(key (vector-ref users i)))
(hash-update h key add1 0)))))
Thanks @zenspider! I do follow on the 0 .. length, I’m not sure what was going on in my head thinking that list indexes started at 1. I will adjust that.
In regards to the random-member
function you’re saying it’s unnecessarily slow because it has to calculate the list’s length every time it is called. I guess I fell a little too in love with the idea of having functions consuming other functions when a binding within the iterator would work.
I see the hash-update
function eliminates the need for the function I wrote but I don’t understand what the for/fold
construct is doing. From the intitial declaration of ((h (hash)))
to the the next line ((_ (in-range 1000000)))
I do understand the binding and the subsequent function calls
@mwb for/fold
is like for
, but it starts with an initial value (hash)
and then each iteration through (in-range ...)
it returns the next value to go back into h
look at foldl
as well
@zenspider thanks! will do. the documentation is a little confusing to me
I’m familiar with a fold in principal
your code is slow because it BOTH calculates the length 1m times AND has to access the Nth element of a list.
Mine calculates the length once and then uses direct access for random element
direct access meaning the vector-ref?
1*O(n) + 1m*O(1)
yeah
there’s probably other things to improve as well… I’m only a beginnerish at racket myself
ok, thanks! You also answered the main question I had in terms of returning a hash from the for rather than creating a hash and mutating it in a for loop
fold is your friend there… but the mutator version is the fastest version of all of them, once you’re using the right data structures
ruby has a method on array called sample
to return N random elements. I’d be a bit surprised if there wasn’t such a thing in racket
I use a lot of list mapping/iteration. In general would it be best to use vectors? When would using a list still be prudent?
I’d say it’s pretty safe to always start with a list to get the thing working… worry about optimizing later, once you have profiling numbers.
fair enough. I’ll look at the docs for vectors
thanks again!
your original code requires 3 minor tweaks to switch from lists to vectors and is almost as fast as my fastest at that point.
your inclusion of hash-update
simplifies things significantly though
I guess I stopped reading the docs after I found hash-set
I thought I found a regression for 6.90.0.901 related to racket-mode … but — turns out it’s something that worked after 6.2.1 through at least 6.7. However it’s not working in 6.10 or 6.90.0.901. ;; Check whether Racket is new enough (newer than 6.2.1) that
;; module->namespace works with module+ and (module* _ #f __)
;; forms when errortrace is enabled.
(module+ check
(define x 42))
(define (can-enter-module+-namespace?)
(define mp (quote-module-path check))
(dynamic-require mp #f)
(with-handlers ([exn:fail? (λ _ #f)])
(eval 'x (module->namespace mp))
#t))
racket-mode uses this check to maybe a print a warning: ; Note: '(submod #<path:foo.rkt> test) will be evaluated.
; However your Racket version is old. You will be unable to
; use the REPL to examine definitions in the body of a module+
; or (module* _ #f ___) form when errortrace is enabled. Either
; upgrade Racket, or, set the Emacs variable racket-error-context
; to 'low or 'medium.
Lately I’ve mostly used racket-mode myself with racket-error-context
set to 'medium
, so I hadn’t noticed this until just now, on account of doing some manual testing on the 7.0 RC. (Sorry.)
can you test with 6.8 and 6.9?
Yes, I can narrow it down. 6.7 is the latest I have on this machine. I can download and try 6.8 and 6.9, too.
copying 50,000 items to “Applications” :clock:
I made a mistake before re 6.10. It works fine there. I just downloaded a bunch of Racket versions and re-checked each. Actually it works fine on all of 6.7, 6.8, 6.9, 6.10, 6.11, and 6.12. It doesn’t work only in 6.90.0.901.
TL;DR The following program returns #t
in DrRacket 6.12 and earlier (back to circa 6.3) — but returns #f
in DrRacket 6.90.0.901: #lang racket/base
(require syntax/location)
(module+ check
(define x 42))
(define (can-enter-module+-namespace?)
(define mp (quote-module-path check))
(dynamic-require mp #f)
(with-handlers ([exn:fail? (λ _ #f)])
(eval 'x (module->namespace mp))
#t))
(can-enter-module+-namespace?)
weird .. it returned #f
in DrRacket REPL but #t
in xrepl
the error message is head/racket/collects/racket/private/more-scheme.rkt:261:28: x: undefined;
cannot reference an identifier before its definition
@shu—hung The issue I’m talking about is related to it working when errortrace
is being used. The command line racket equivalent would be — if that program I showed is in /tmp/mod.rkt
— something like: $ /Applications/Racket_v6.12/bin/racket -l errortrace -t /tmp/mod.rkt
#t
$ /Applications/Racket_v6.90.0.901/bin/racket -l errortrace -t /tmp/mod.rkt
#f
Without errortrace
in the picture, both are fine: $ /Applications/Racket_v6.12/bin/racket /tmp/mod.rkt
#t
$ /Applications/Racket_v6.90.0.901/bin/racket /tmp/mod.rkt
#t
Hmm, errortrace does affect the behavior. But in DrRacket I have errortrace off though
Anyway sorry again for posting this to #beginners by mistake. Thanks @samth for guiding me to research it more. I posted distilled version to racket-dev https://groups.google.com/forum/#!topic/racket-dev/VOnK_8iaI0Y