random: move sequence support to random-ref
, in racket/random
.
And add `random-sample`.
This commit is contained in:
parent
6af2f711b7
commit
707f9bffa6
|
@ -847,10 +847,6 @@ both in binary and as integers.
|
||||||
[rand-gen pseudo-random-generator?
|
[rand-gen pseudo-random-generator?
|
||||||
(current-pseudo-random-generator)])
|
(current-pseudo-random-generator)])
|
||||||
exact-nonnegative-integer?]
|
exact-nonnegative-integer?]
|
||||||
[(random [seq sequence?]
|
|
||||||
[rand-gen pseudo-random-generator?
|
|
||||||
(current-pseudo-random-generator)])
|
|
||||||
any/c]
|
|
||||||
[(random [rand-gen pseudo-random-generator?
|
[(random [rand-gen pseudo-random-generator?
|
||||||
(current-pseudo-random-generator)])
|
(current-pseudo-random-generator)])
|
||||||
(and/c real? inexact? (>/c 0) (</c 1))])]{
|
(and/c real? inexact? (>/c 0) (</c 1))])]{
|
||||||
|
@ -861,10 +857,6 @@ exact integer in the range @racket[0] to @math{@racket[k]-1}.
|
||||||
When called with two integer arguments @racket[min] and @racket[max], returns a
|
When called with two integer arguments @racket[min] and @racket[max], returns a
|
||||||
random exact integer in the range @racket[min] to @math{@racket[max]-1}.
|
random exact integer in the range @racket[min] to @math{@racket[max]-1}.
|
||||||
|
|
||||||
When called with a sequence argument @racket[seq], returns a random element of
|
|
||||||
the sequence. Like @racket[sequence-length], does not terminate on infinite
|
|
||||||
sequences, and extracts elements up to the returned element.
|
|
||||||
|
|
||||||
When called with zero arguments, returns a random inexact number between
|
When called with zero arguments, returns a random inexact number between
|
||||||
@racket[0] and @racket[1], exclusive.
|
@racket[0] and @racket[1], exclusive.
|
||||||
|
|
||||||
|
@ -873,7 +865,10 @@ generator (which defaults to the current one, as produced by
|
||||||
@racket[current-pseudo-random-generator]). The generator maintains an
|
@racket[current-pseudo-random-generator]). The generator maintains an
|
||||||
internal state for generating numbers. The random number generator
|
internal state for generating numbers. The random number generator
|
||||||
uses a 54-bit version of L'Ecuyer's MRG32k3a algorithm
|
uses a 54-bit version of L'Ecuyer's MRG32k3a algorithm
|
||||||
@cite["L'Ecuyer02"].}
|
@cite["L'Ecuyer02"].
|
||||||
|
|
||||||
|
@history[#:changed "6.4"]{Added support for ranges.}}
|
||||||
|
|
||||||
|
|
||||||
@defproc[(random-seed [k (integer-in 1 (sub1 (expt 2 31)))])
|
@defproc[(random-seed [k (integer-in 1 (sub1 (expt 2 31)))])
|
||||||
void?]{
|
void?]{
|
||||||
|
@ -944,16 +939,17 @@ three integers is non-zero. Otherwise, the result is @racket[#f].}
|
||||||
|
|
||||||
@; ------------------------------------------------------------------------
|
@; ------------------------------------------------------------------------
|
||||||
|
|
||||||
@subsection{System-Provided Randomness}
|
@subsection{Other Randomness Utilities}
|
||||||
|
|
||||||
@defmodule[racket/random]{The @racketmodname[racket/random] module
|
@defmodule[racket/random]{}
|
||||||
provides an interface to randomness from the underlying operating
|
|
||||||
system. Use @racket[crypto-random-bytes]
|
|
||||||
instead of @racket[random] wherever security is a concern.}
|
|
||||||
|
|
||||||
@defproc[(crypto-random-bytes [n exact-positive-integer?])
|
@defproc[(crypto-random-bytes [n exact-positive-integer?])
|
||||||
bytes?]{
|
bytes?]{
|
||||||
|
|
||||||
|
Provides an interface to randomness from the underlying operating system. Use
|
||||||
|
@racket[crypto-random-bytes] instead of @racket[random] wherever security is a
|
||||||
|
concern.
|
||||||
|
|
||||||
Returns @racket[n] random bytes. On Unix systems, the bytes are
|
Returns @racket[n] random bytes. On Unix systems, the bytes are
|
||||||
obtained from @filepath{/dev/urandom}, while Windows uses
|
obtained from @filepath{/dev/urandom}, while Windows uses
|
||||||
the @tt{RtlGenRand} system function.
|
the @tt{RtlGenRand} system function.
|
||||||
|
@ -964,6 +960,34 @@ the @tt{RtlGenRand} system function.
|
||||||
|
|
||||||
@history[#:added "6.3"]}
|
@history[#:added "6.3"]}
|
||||||
|
|
||||||
|
@defproc[(random-ref [seq sequence?]
|
||||||
|
[rand-gen pseudo-random-generator?
|
||||||
|
(current-pseudo-random-generator)])
|
||||||
|
any/c]{
|
||||||
|
|
||||||
|
Returns a random element of the sequence. Like @racket[sequence-length], does
|
||||||
|
not terminate on infinite sequences, and extracts elements up to the returned
|
||||||
|
element.
|
||||||
|
|
||||||
|
@history[#:added "6.4"]}
|
||||||
|
|
||||||
|
@defproc[(random-sample [seq sequence?]
|
||||||
|
[n exact-positive-integer?]
|
||||||
|
[rand-gen pseudo-random-generator?
|
||||||
|
(current-pseudo-random-generator)]
|
||||||
|
[#:replacement? replacement? any/c #t])
|
||||||
|
(listof any/c)]{
|
||||||
|
|
||||||
|
Returns a list of @racket[n] elements of @racket[seq], picked at random.
|
||||||
|
If @racket[replacement?] is non-false, elements are drawn with replacement,
|
||||||
|
which allows for duplicates.
|
||||||
|
|
||||||
|
Like @racket[sequence-length], does not terminate on infinite sequences, and
|
||||||
|
extracts elements up to the returned element.
|
||||||
|
|
||||||
|
@history[#:added "6.4"]}
|
||||||
|
|
||||||
|
|
||||||
@; ------------------------------------------------------------------------
|
@; ------------------------------------------------------------------------
|
||||||
@subsection{Number--String Conversions}
|
@subsection{Number--String Conversions}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
(Section 'numbers)
|
(Section 'numbers)
|
||||||
|
|
||||||
(require racket/extflonum)
|
(require racket/extflonum racket/random)
|
||||||
|
|
||||||
(test #f number? 'a)
|
(test #f number? 'a)
|
||||||
(test #f complex? 'a)
|
(test #f complex? 'a)
|
||||||
|
@ -2452,12 +2452,12 @@
|
||||||
(test (begin (random-seed 23) (list (random 10 20) (random 20 30) (random 30 40)))
|
(test (begin (random-seed 23) (list (random 10 20) (random 20 30) (random 30 40)))
|
||||||
'random-seed-same2
|
'random-seed-same2
|
||||||
(begin (random-seed 23) (list (random 10 20) (random 20 30) (random 30 40))))
|
(begin (random-seed 23) (list (random 10 20) (random 20 30) (random 30 40))))
|
||||||
(test (begin (random-seed 23) (list (random '(1 2 3)) (random '(4 5 6)) (random '(7 8 9))))
|
(test (begin (random-seed 23) (list (random-ref '(1 2 3)) (random-ref '(4 5 6)) (random-ref '(7 8 9))))
|
||||||
'random-seed-same3
|
'random-seed-same3
|
||||||
(begin (random-seed 23) (list (random '#(1 2 3)) (random '#(4 5 6)) (random '#(7 8 9)))))
|
(begin (random-seed 23) (list (random-ref '#(1 2 3)) (random-ref '#(4 5 6)) (random-ref '#(7 8 9)))))
|
||||||
(test (begin (random-seed 23) (list (random '#(1 2 3)) (random '#(4 5 6)) (random '#(7 8 9))))
|
(test (begin (random-seed 23) (list (random-ref "123") (random-ref "123") (random-ref "123")))
|
||||||
'random-seed-same4
|
'random-seed-same4
|
||||||
(begin (random-seed 23) (list (random '#(1 2 3)) (random '#(4 5 6)) (random '#(7 8 9)))))
|
(begin (random-seed 23) (list (random-ref "123") (random-ref "123") (random-ref "123"))))
|
||||||
(arity-test random-seed 1 1)
|
(arity-test random-seed 1 1)
|
||||||
(arity-test random 0 3)
|
(arity-test random 0 3)
|
||||||
(err/rt-test (random-seed "apple"))
|
(err/rt-test (random-seed "apple"))
|
||||||
|
@ -2514,9 +2514,16 @@
|
||||||
(parameterize ([current-pseudo-random-generator
|
(parameterize ([current-pseudo-random-generator
|
||||||
(vector->pseudo-random-generator
|
(vector->pseudo-random-generator
|
||||||
#(3620087466 1904163406 3177592043 1406334318 257151704 3090455638))])
|
#(3620087466 1904163406 3177592043 1406334318 257151704 3090455638))])
|
||||||
(test 3 random '(1 2 3 4 5))
|
(test 3 random-ref '(1 2 3 4 5))
|
||||||
(test 10 random '#(7 6 8 9 10))
|
(test 10 random-ref '#(7 6 8 9 10))
|
||||||
(test #\e random "abcde"))
|
(test #\e random-ref "abcde"))
|
||||||
|
(parameterize ([current-pseudo-random-generator
|
||||||
|
(vector->pseudo-random-generator
|
||||||
|
#(3620087466 1904163406 3177592043 1406334318 257151704 3090455638))])
|
||||||
|
(test '(3) random-sample '(1 2 3 4 5) 1)
|
||||||
|
(test '(5 5 5) random-sample '(1 2 3 4 5) 3)
|
||||||
|
(test '(2 4 5) random-sample '(1 2 3 4 5) 3 #:replacement? #f))
|
||||||
|
|
||||||
|
|
||||||
(test #t = 0 0)
|
(test #t = 0 0)
|
||||||
(test #f = 0 (expt 2 32))
|
(test #f = 0 (expt 2 32))
|
||||||
|
|
|
@ -78,9 +78,6 @@
|
||||||
sequence-generate
|
sequence-generate
|
||||||
sequence-generate*
|
sequence-generate*
|
||||||
prop:sequence
|
prop:sequence
|
||||||
sequence-length
|
|
||||||
|
|
||||||
-sequence-ref
|
|
||||||
|
|
||||||
define-sequence-syntax
|
define-sequence-syntax
|
||||||
make-do-sequence
|
make-do-sequence
|
||||||
|
@ -2032,29 +2029,4 @@
|
||||||
#true
|
#true
|
||||||
[(next-body l d init-dir use-dir?)])]])))
|
[(next-body l d init-dir use-dir?)])]])))
|
||||||
|
|
||||||
(define-values (sequence-length)
|
|
||||||
(λ (s)
|
|
||||||
(unless (sequence? s) (raise-argument-error 'sequence-length "sequence?" s))
|
|
||||||
(for/fold ([c 0]) ([i (in-values*-sequence s)])
|
|
||||||
(add1 c))))
|
|
||||||
|
|
||||||
(define-values (-sequence-ref)
|
|
||||||
(λ (s i)
|
|
||||||
(unless (sequence? s) (raise-argument-error 'sequence-ref "sequence?" s))
|
|
||||||
(unless (exact-nonnegative-integer? i)
|
|
||||||
(raise-argument-error 'sequence-ref "exact-nonnegative-integer?" i))
|
|
||||||
(let-values ([(v) (for/fold ([c #f]) ([v (in-values*-sequence s)]
|
|
||||||
[j (in-range (add1 i))]
|
|
||||||
#:unless (j . < . i))
|
|
||||||
(or v '(#f)))])
|
|
||||||
(cond
|
|
||||||
[(not v)
|
|
||||||
(raise-arguments-error
|
|
||||||
'sequence-ref
|
|
||||||
"sequence ended before index"
|
|
||||||
"index" i
|
|
||||||
"sequence" s)]
|
|
||||||
[(list? v) (apply values v)]
|
|
||||||
[else v]))))
|
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -113,38 +113,27 @@
|
||||||
(case-lambda
|
(case-lambda
|
||||||
[() (random)] ; no args, random float
|
[() (random)] ; no args, random float
|
||||||
[(x)
|
[(x)
|
||||||
;; one arg, either random float with prng, or random integer, or random
|
;; one arg, either random float with prng, or random integer
|
||||||
;; sequence element
|
|
||||||
(cond [(exact-positive-integer? x)
|
(cond [(exact-positive-integer? x)
|
||||||
(enforce-random-int-range x)
|
(enforce-random-int-range x)
|
||||||
(random x)]
|
(random x)]
|
||||||
[(pseudo-random-generator? x)
|
[(pseudo-random-generator? x)
|
||||||
(random x)]
|
(random x)]
|
||||||
[(sequence? x)
|
|
||||||
(-sequence-ref x (random (sequence-length x)))]
|
|
||||||
[else
|
[else
|
||||||
(raise-argument-error
|
(raise-argument-error
|
||||||
'random
|
'random
|
||||||
"(or/c (integer-in 1 4294967087) sequence? pseudo-random-generator?)"
|
"(or/c (integer-in 1 4294967087) pseudo-random-generator?)"
|
||||||
x)])]
|
x)])]
|
||||||
[(x y)
|
[(x y)
|
||||||
;; two args, either min and prng, or min and max, or sequence and prng
|
;; two args, either min and prng, or min and max
|
||||||
(cond [(exact-positive-integer? y) ; min and max case
|
(cond [(exact-positive-integer? y) ; min and max case
|
||||||
(enforce-random-int-range x)
|
(enforce-random-int-range x)
|
||||||
(enforce-random-int-range y)
|
(enforce-random-int-range y)
|
||||||
(enforce-greater x y)
|
(enforce-greater x y)
|
||||||
(+ x (random (- y x)))]
|
(+ x (random (- y x)))]
|
||||||
[(pseudo-random-generator? y)
|
[(pseudo-random-generator? y) ; int and prng case
|
||||||
(cond [(exact-positive-integer? x) ; int and prng case
|
|
||||||
(enforce-random-int-range x)
|
(enforce-random-int-range x)
|
||||||
(random x y)]
|
(random x y)]
|
||||||
[(sequence? x) ; sequence and prng case
|
|
||||||
(-sequence-ref x (random (sequence-length x) y))]
|
|
||||||
[else
|
|
||||||
(raise-argument-error
|
|
||||||
'random
|
|
||||||
"(or/c (integer-in 1 4294967087) sequence?)"
|
|
||||||
x)])]
|
|
||||||
[else
|
[else
|
||||||
(raise-argument-error
|
(raise-argument-error
|
||||||
'random
|
'random
|
||||||
|
|
|
@ -9,9 +9,7 @@
|
||||||
sequence-ormap
|
sequence-ormap
|
||||||
sequence-for-each
|
sequence-for-each
|
||||||
sequence-fold
|
sequence-fold
|
||||||
sequence-count
|
sequence-count)
|
||||||
sequence-length
|
|
||||||
(rename-out [-sequence-ref sequence-ref]))
|
|
||||||
|
|
||||||
(define (sequence-andmap f s)
|
(define (sequence-andmap f s)
|
||||||
(unless (procedure? f) (raise-argument-error 'sequence-andmap "procedure?" f))
|
(unless (procedure? f) (raise-argument-error 'sequence-andmap "procedure?" f))
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
|
||||||
(require "private/unix-rand.rkt" "private/windows-rand.rkt" racket/contract/base)
|
(require "private/unix-rand.rkt" "private/windows-rand.rkt"
|
||||||
(provide (contract-out [crypto-random-bytes (-> exact-nonnegative-integer? bytes?)]))
|
racket/contract/base racket/sequence racket/set)
|
||||||
|
(provide (contract-out [crypto-random-bytes (-> exact-nonnegative-integer? bytes?)]
|
||||||
|
[random-ref (->* (sequence?) (pseudo-random-generator?) any/c)]
|
||||||
|
[random-sample (->* (sequence? exact-nonnegative-integer?)
|
||||||
|
(pseudo-random-generator?
|
||||||
|
#:replacement? any/c)
|
||||||
|
(listof any/c))]))
|
||||||
|
|
||||||
; (: crypto-random-bytes (-> Positive-Integer Bytes))
|
; (: crypto-random-bytes (-> Positive-Integer Bytes))
|
||||||
; returns n random bytes from the os.
|
; returns n random bytes from the os.
|
||||||
|
@ -12,3 +18,25 @@
|
||||||
[else (raise (make-exn:fail:unsupported
|
[else (raise (make-exn:fail:unsupported
|
||||||
"not supported on the current platform"
|
"not supported on the current platform"
|
||||||
(current-continuation-marks)))]))
|
(current-continuation-marks)))]))
|
||||||
|
|
||||||
|
(define (random-ref seq [prng (current-pseudo-random-generator)])
|
||||||
|
(sequence-ref seq (random (sequence-length seq))))
|
||||||
|
|
||||||
|
(define (random-sample seq n [prng (current-pseudo-random-generator)]
|
||||||
|
#:replacement? [replacement? #t])
|
||||||
|
(cond [replacement?
|
||||||
|
(for/list ([i (in-range n)])
|
||||||
|
(random-ref seq prng))]
|
||||||
|
[else
|
||||||
|
(unless (>= (sequence-length seq) n)
|
||||||
|
(raise-argument-error 'random-sample
|
||||||
|
"integer less than sequence length"
|
||||||
|
n))
|
||||||
|
(define l (sequence-length seq))
|
||||||
|
;; sequences don't necessarily support removal, so instead sample
|
||||||
|
;; indices without replacement, then index into the sequence
|
||||||
|
(let loop ([res-idx (set)])
|
||||||
|
(cond [(= (set-count res-idx) n) ; we have all we need, we're done
|
||||||
|
(for/list ([i (in-set res-idx)]) (sequence-ref seq i))]
|
||||||
|
[else
|
||||||
|
(loop (set-add res-idx (random l)))]))]))
|
||||||
|
|
|
@ -39,6 +39,29 @@
|
||||||
(define (sequence->list s)
|
(define (sequence->list s)
|
||||||
(for/list ([v s]) v))
|
(for/list ([v s]) v))
|
||||||
|
|
||||||
|
(define (sequence-length s)
|
||||||
|
(unless (sequence? s) (raise-argument-error 'sequence-length "sequence?" s))
|
||||||
|
(for/fold ([c 0]) ([i (in-values*-sequence s)])
|
||||||
|
(add1 c)))
|
||||||
|
|
||||||
|
(define (sequence-ref s i)
|
||||||
|
(unless (sequence? s) (raise-argument-error 'sequence-ref "sequence?" s))
|
||||||
|
(unless (exact-nonnegative-integer? i)
|
||||||
|
(raise-argument-error 'sequence-ref "exact-nonnegative-integer?" i))
|
||||||
|
(let ([v (for/fold ([c #f]) ([v (in-values*-sequence s)]
|
||||||
|
[j (in-range (add1 i))]
|
||||||
|
#:unless (j . < . i))
|
||||||
|
(or v '(#f)))])
|
||||||
|
(cond
|
||||||
|
[(not v)
|
||||||
|
(raise-arguments-error
|
||||||
|
'sequence-ref
|
||||||
|
"sequence ended before index"
|
||||||
|
"index" i
|
||||||
|
"sequence" s)]
|
||||||
|
[(list? v) (apply values v)]
|
||||||
|
[else v])))
|
||||||
|
|
||||||
(define (sequence-tail seq i)
|
(define (sequence-tail seq i)
|
||||||
(unless (sequence? seq) (raise-argument-error 'sequence-tail "sequence?" seq))
|
(unless (sequence? seq) (raise-argument-error 'sequence-tail "sequence?" seq))
|
||||||
(unless (exact-nonnegative-integer? i)
|
(unless (exact-nonnegative-integer? i)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user