#lang racket #| To use functions provided here from another file, put this file into the same directory, and in that file add `(require "student-extras.rkt")`. Note that you should *not* cut/paste this into a blank beginner-student tab; just download this file and put it into the same directory. |# ; See full racket-documentation for these functions/keywords: ; (provide (all-defined-out) provide require all-defined-out match-define let let* letrec module+ struct ; example below. exn? exn:fail? with-handlers exn-message exn:fail? eprintf ; print to stderr (side-effect) for-each ;TODO: (rename-out (only-in lang/beginning-student check-expect c-e)) ; There are more regexp functions in racket/base ; We only re-export the most common ones. pregexp ; constructor, or use `#px"..."` as a literal-regexp. regexp? pregexp? regexp-match regexp-match* regexp-match? regexp-split regexp-replace regexp-replace* regexp-quote string-upcase string-downcase string-titlecase file-exists? ; hash-tables: see https://docs.racket-lang.org/reference/hashtables.html?q=hash%20tables hash ; constructor hash-ref ; lookup by key hash-set ; add a key/value pair (functional -- returns a *new* hash-table) hash? ; the following are much less common, but are handy in their narrow circumstance: make-immutable-hash ; constructor (variant) hash-count hash-empty? hash-has-key? hash-set* ; set many key/values "at once" hash-update ; change a value, eg `(hash-update word-counts word add1 0)` hash-remove hash->list ; a list of key/value cons-pairs, e.g. '(("en" . "hi") ("fr" . "bonjour") ("de" . "tag")) hash-keys ; a list of the keys (equivalent to `(map first (hash->list ...))` ) hash-values ; a list of the values (equivalent to `(map rest (hash->list ...))` ) hash-map ; equivalent to `(map .. (hash->list ...))` procedure-arity-includes? degrees->radians radians->degrees ) ; (The student languages don't allow `only-in`, ; and requiring the entire library gives a conflict.) ; (require (only-in racket/gui play-sound)) (define-syntax-rule (define/c header contract body) (define/contract header contract body)) ; convenience: make a struct that is contracted, and #:transparent. (define-syntax-rule (define-struct/c struct-name fields+types) (define-struct/contract struct-name fields+types #:transparent)) ; play-sound-then : filename any/c -> any/c ; Play `snd`, and return whatever `expr` evaluates to. ; This is a hack for beginning-student, which doesn't have `begin`. (define (play-sound-then snd expr) (begin (play-sound snd #true) expr)) ; modulo/real : real (and real (non zero?)) . -> . real ; Like modulo, extended to real numbers (not just integers). ; (define (modulo/real x y) (* y (- (/ x y) (floor (/ x y))))) (module+ test ; This is a "submodule"; it is *not* run when this file is `require`d from elsewhere, ; but it *is* run if this is run as the main (top-level) file. (require rackunit) (check-equal? (modulo/real 0 20) 0) (check-equal? (modulo/real 20 20) 0) (check-equal? (modulo/real 60 20) 0) (check-equal? (modulo/real -20 20) 0) (check-equal? (modulo/real -60 20) 0) (check-within (modulo/real 60.1 20) 0.1 0.001) (check-within (modulo/real 20.1 20) 0.1 0.001) (check-within (modulo/real 19.9 20) 19.9 0.001) (check-within (modulo/real +0.1 20) +0.1 0.001) (check-within (modulo/real -0.1 20) 19.9 0.001) (check-within (modulo/real 12.5 20) 12.5 0.001) (check-within (modulo/real 12.5 20) 12.5 0.001) (check-within (modulo/real 12.4 20) 12.4 0.001) (check-within (modulo/real 12.6 20) 12.6 0.001) ) ;;;;;;;;;;;;;;;;;; convenient type-predicates (for contracts) ;;;;;;;;;;;;;;;;;; (define any? any/c) ; something to put in our templates, where the contract's return-type goes: (define ...? any/c) ; useful contract-generators: ; `(in-range 3 7)` returns a *function*, ; so: ((in-range 3 7) 5) is #true, ; and a signature can be something like: ; (-> (in-range 0 1) string? boolean?) (define/contract (integer-in-range a b) (-> number? number? (-> any/c boolean?)) ; return the function: is a <= n < b ? (Note the half-open interval.) (λ(n) (and (integer? n) (<= a n) (< n b)))) (define in-range integer-in-range) ; a useful contract type: (define non-negative-integer? (integer-in-range 0 +inf.0)) (define natural? non-negative-integer?) (define integer>=0? non-negative-integer?) ; for consistency to `real>=0?` ; useful contract-generators: ; `(real-in-range 3 7)` returns a *function*, ; so: ((real-in-range 3 7) 5) is #true, ; and a signature can be something like: ; (-> (real-in-range 0 1) string? boolean?) (define/contract (real-in-range a b) (-> real? real? (-> any/c boolean?)) ; return the function: is a <= x < b ? (λ(x) (and (real? x) (<= a x) (< x b)))) (define/contract (real-in-open a b) (-> real? real? (-> any/c boolean?)) ; return the function: is a < x < b ? (λ(x) (and (real? x) (< a x b)))) (define/contract (real-in-closed a b) (-> real? real? (-> any/c boolean?)) ; return the function: is a <= x <= b ? (λ(x) (and (real? x) (<= a x b)))) ; a useful contract type; also exported as `real>=0?` (define (non-negative-real? x) ;(-> any/c boolean?) (and (number? x) (real? x) (<= 0 x))) (define real>=0? non-negative-real?) ; N.B. takes in a `real?`, not `any/c`, to match racket's built-in `infinite?` (define/contract (finite? x) (-> real? boolean?) (and (not (infinite? x)) (not (nan? x)))) (define/contract (empty-string? s) (-> any/c boolean?) (and (string? s) (string=? s ""))) (module+ test (check-equal? (non-negative-real? "string") #false) (check-equal? (non-negative-real? 0) #true) (check-equal? (non-negative-real? 1) #true) (check-equal? (non-negative-real? 37) #true) (check-equal? (non-negative-real? #e3.7) #true) (check-equal? (non-negative-real? #i3.7) #true) (check-equal? (non-negative-real? -1) #false) (check-equal? (non-negative-real? -37) #false) (check-equal? (non-negative-real? #e-3.7) #false) (check-equal? (non-negative-real? #i-3.7) #false) (check-equal? (natural? "string") #false) (check-equal? (natural? 0) #true) (check-equal? (natural? 1) #true) (check-equal? (natural? 37) #true) (check-equal? (natural? #e3.7) #false) (check-equal? (natural? #i3.7) #false) (check-equal? (natural? -1) #false) (check-equal? (natural? -37) #false) (check-equal? (natural? #e-3.7) #false) (check-equal? (natural? #i-3.7) #false) (check-equal? (non-empty-string? 37) #false) (check-equal? (non-empty-string? "") #false) (check-equal? (non-empty-string? "a") #true) (check-equal? (non-empty-string? "howdy") #true) ) #| ; `struct` is like `define-struct` but allows inheritance: ; When using this, include args `#:transparent` and possibly `#:consructor-name make-...` ). ; *** struct-inheritance requires intermediate-student or higher (not beginning-student, alas). ; ; Note: in student-level, (struct foo (...)) objects always print as `make-foo` even though ; the actual constructor-name is just `foo`. ; (Of course, if you want the `make-` constructor names, you can use `(define make-foo foo)` ; or even provide the keyword-arg #:constructor-name when defining the `struct`.) ; Usage example: (require "student-extras.rkt") (struct soup (a b) #:transparent) (define make-soup soup) ; We can bind the traditional name to the *actual* constructor. (soup 4 5) (make-soup 2 3) ;; inherit: (struct hotsoup soup (c d e) #:transparent) ; inherit `soup`s fields, and add new ones c,d,e. (hotsoup 2 3 4 5 6) ; N.B. this prints as `(make-hotsoup ...)`, in student levels. (soup-a (hotsoup 2 3 4 5 6)) ;(hotsoup-a (make-hotsoup 2 3 4 5 6)) ;error! ;(soup-e (make-hotsoup 2 3 4 5 6)) ;error (of course) (hotsoup-e (hotsoup 2 3 4 5 6)) |# #| @license CC0 -- public domain. You may freely copy, modify, or distribute this code, without restriction. https://creativecommons.org/publicdomain/zero/1.0/ |#