mwb
2018-7-2 17:27:44

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?


zenspider
2018-7-2 21:23:59

@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

zenspider
2018-7-2 21:24:24

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)))))

mwb
2018-7-2 22:52:48

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


zenspider
2018-7-2 22:59:04

@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


zenspider
2018-7-2 22:59:14

look at foldl as well


mwb
2018-7-2 22:59:39

@zenspider thanks! will do. the documentation is a little confusing to me


mwb
2018-7-2 22:59:49

I’m familiar with a fold in principal


zenspider
2018-7-2 23:00:02

your code is slow because it BOTH calculates the length 1m times AND has to access the Nth element of a list.


zenspider
2018-7-2 23:00:18

Mine calculates the length once and then uses direct access for random element


mwb
2018-7-2 23:00:37

direct access meaning the vector-ref?


zenspider
2018-7-2 23:00:51

1*O(n) + 1m*O(1)


zenspider
2018-7-2 23:00:52

yeah


zenspider
2018-7-2 23:01:48

there’s probably other things to improve as well… I’m only a beginnerish at racket myself


mwb
2018-7-2 23:02:44

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


zenspider
2018-7-2 23:03:24

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


zenspider
2018-7-2 23:04:16

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


mwb
2018-7-2 23:04:49

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?


zenspider
2018-7-2 23:05:54

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.


mwb
2018-7-2 23:06:17

fair enough. I’ll look at the docs for vectors


mwb
2018-7-2 23:06:24

thanks again!


zenspider
2018-7-2 23:07:46

your original code requires 3 minor tweaks to switch from lists to vectors and is almost as fast as my fastest at that point.


mwb
2018-7-2 23:09:33

your inclusion of hash-update simplifies things significantly though


mwb
2018-7-2 23:10:20

I guess I stopped reading the docs after I found hash-set


greg
2018-7-3 02:01:38

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))


greg
2018-7-3 02:02:11

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.


greg
2018-7-3 02:03:20

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.)


samth
2018-7-3 02:03:52

can you test with 6.8 and 6.9?


greg
2018-7-3 02:04:45

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.


greg
2018-7-3 02:09:34

copying 50,000 items to “Applications” :clock:


greg
2018-7-3 02:26:47

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.


greg
2018-7-3 02:31:23

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?)


greg
2018-7-3 02:34:02

greg
2018-7-3 02:34:38

Oh carp, I just realized I’m in #beginners not #general — sorry.


shu--hung
2018-7-3 02:42:17

weird .. it returned #f in DrRacket REPL but #t in xrepl


shu--hung
2018-7-3 02:43:35

the error message is head/racket/collects/racket/private/more-scheme.rkt:261:28: x: undefined; cannot reference an identifier before its definition


greg
2018-7-3 02:53:21

@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


greg
2018-7-3 02:54:37

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


shu--hung
2018-7-3 02:59:14

Hmm, errortrace does affect the behavior. But in DrRacket I have errortrace off though


greg
2018-7-3 03:14:45

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