305 lines
10 KiB
Racket
305 lines
10 KiB
Racket
#lang racket/base
|
|
|
|
(require racket/list
|
|
"../main.rkt")
|
|
|
|
(provide usecase-random-number
|
|
usecase-unique-values
|
|
usecase-falsey-values
|
|
usecase-currying
|
|
usecase-object-destructuring
|
|
usecase-timer-interval
|
|
usecase-object-props
|
|
usecase-string-concat-order
|
|
usecase-freeze-vs-seal
|
|
usecase-switch
|
|
usecase-class-constructor
|
|
usecase-sort-objects-by-property
|
|
usecase-delete-array-elements
|
|
usecase-bubble-sort
|
|
usecase-binary-search
|
|
usecase-map-count-occurrences
|
|
usecase-get-html-three-ways
|
|
usecase-anagram
|
|
usecase-pairs-equal-target
|
|
usecase-fetch-api
|
|
all-js-usecases
|
|
show-js-usecases
|
|
write-js-usecases-file)
|
|
|
|
;; Use case 01: generate a random integer between 1 and 5.
|
|
(define usecase-random-number
|
|
(js
|
|
(define (randomBetween1And5)
|
|
(return (+ (send Math floor (* (send Math random) 5)) 1)))))
|
|
|
|
;; Use case 02: get unique values from an array with duplicates using Set.
|
|
(define usecase-unique-values
|
|
(js
|
|
(define (uniqueValues xs)
|
|
(return (send Array from (new Set xs))))))
|
|
|
|
;; Use case 03: the six JavaScript falsey values.
|
|
(define usecase-falsey-values
|
|
(js
|
|
(define (falseyValues)
|
|
(return (array #f 0 "" js-null js-undefined js-NaN)))))
|
|
|
|
;; Use case 04: currying, simple example.
|
|
(define usecase-currying
|
|
(js
|
|
(define (add x)
|
|
(return (lambda (y)
|
|
(return (+ x y)))))))
|
|
|
|
;; Use case 05: object destructuring.
|
|
(define usecase-object-destructuring
|
|
(js
|
|
(define (describePerson person)
|
|
(let-object ([name 'name]
|
|
[age 'age 0])
|
|
person
|
|
(return (string-append name ":" (number->string age)))))))
|
|
|
|
;; Use case 06: get out of a timer interval with setInterval/clearInterval.
|
|
(define usecase-timer-interval
|
|
(js
|
|
(define (startTimer)
|
|
(let* ([ticks 0]
|
|
[intervalId #f])
|
|
(set! intervalId
|
|
(setInterval (lambda ()
|
|
(set! ticks (+ ticks 1))
|
|
(when (= ticks 3)
|
|
(clearInterval intervalId)))
|
|
10))
|
|
(return (object 'id intervalId
|
|
'getTicks (lambda () (return ticks))))))))
|
|
|
|
;; Use case 07: get/set/delete object properties. The value of a is read via
|
|
;; dot access, bracket access, and destructuring.
|
|
(define usecase-object-props
|
|
(js
|
|
(define (objectProps)
|
|
(let* ([obj (object 'a 1)]
|
|
[a1 obj.a]
|
|
[a2 (js-ref obj "a")])
|
|
(let-object ([a3 'a]) obj
|
|
(set! obj.b 2)
|
|
(set-prop! obj "c" 3)
|
|
(delete-prop! obj "a")
|
|
(return (array a1 a2 a3 obj.b (js-ref obj "c")
|
|
(send Object hasOwn obj "a"))))))))
|
|
|
|
;; Use case 08: string concatenation; order matters with JavaScript +.
|
|
(define usecase-string-concat-order
|
|
(js
|
|
(define (concatOrder)
|
|
(return (array (+ 1 2 "3")
|
|
(+ "1" 2 3))))))
|
|
|
|
;; Use case 09: Object.freeze() vs Object.seal().
|
|
(define usecase-freeze-vs-seal
|
|
(js
|
|
(define (freezeVsSeal)
|
|
(let* ([frozen (send Object freeze (object 'a 1))]
|
|
[sealed (send Object seal (object 'a 1))])
|
|
(set! frozen.a 9)
|
|
(set! sealed.a 9)
|
|
(delete-prop! sealed "a")
|
|
(return (array frozen.a
|
|
sealed.a
|
|
(send Object isFrozen frozen)
|
|
(send Object isSealed sealed)
|
|
(send Object hasOwn sealed "a")))))))
|
|
|
|
;; Use case 10: switch example. The Racket surface form is case.
|
|
(define usecase-switch
|
|
(js
|
|
(define (switchExample n)
|
|
(case n
|
|
[(1) (return "one")]
|
|
[(2 3) (return "two-or-three")]
|
|
[else (return "other")]))))
|
|
|
|
;; Use case 11: class constructor with a default value.
|
|
(define usecase-class-constructor
|
|
(js
|
|
(define-class Greeter
|
|
(constructor ([name "world"])
|
|
(set! this.name name))
|
|
(method greet ()
|
|
(return (string-append "Hello " this.name))))
|
|
|
|
(define (classExample)
|
|
(let* ([a (new Greeter)]
|
|
[b (new Greeter "Ada")])
|
|
(return (array (send a greet) (send b greet)))))))
|
|
|
|
;; Use case 12: sort an array of objects by a given property.
|
|
(define usecase-sort-objects-by-property
|
|
(js
|
|
(define (sortByProperty xs prop)
|
|
(return (send (send xs slice)
|
|
sort
|
|
(lambda (a b)
|
|
(return (- (js-ref a prop) (js-ref b prop)))))))))
|
|
|
|
;; Use case 13: four ways to delete/remove an element from an array.
|
|
(define usecase-delete-array-elements
|
|
(js
|
|
(define (deleteArrayWays xs)
|
|
(let* ([a1 (send xs slice)]
|
|
[a2 (send xs slice)]
|
|
[a3 (send xs slice)]
|
|
[a4 (send xs slice)])
|
|
;; 1. Mutating removal with splice.
|
|
(send a1 splice 1 1)
|
|
;; 2. Functional removal with filter.
|
|
(set! a2 (send a2 filter (lambda (x i) (return (not (= i 1))))))
|
|
;; 3. Rebuild with slice + concat.
|
|
(set! a3 (send (send a3 slice 0 1) concat (send a3 slice 2)))
|
|
;; 4. delete leaves a hole and preserves length.
|
|
(delete-prop! a4 1)
|
|
(return (array a1 a2 a3 (array (send Object hasOwn a4 "1") (length a4))))))))
|
|
|
|
;; Use case 14: Bubble Sort.
|
|
(define usecase-bubble-sort
|
|
(js
|
|
(define (bubbleSort xs)
|
|
(let* ([a (send xs slice)]
|
|
[n (length a)])
|
|
(while (> n 1)
|
|
(let* ([i 1])
|
|
(while (< i n)
|
|
(when (> (list-ref a (- i 1)) (list-ref a i))
|
|
(let* ([tmp (list-ref a (- i 1))])
|
|
(vector-set! a (- i 1) (list-ref a i))
|
|
(vector-set! a i tmp)))
|
|
(set! i (+ i 1))))
|
|
(set! n (- n 1)))
|
|
(return a)))))
|
|
|
|
;; Use case 15: Binary Search using recursion.
|
|
(define usecase-binary-search
|
|
(js
|
|
(define (binarySearch xs target low high)
|
|
(if (> low high)
|
|
(return -1)
|
|
(let* ([mid (send Math floor (/ (+ low high) 2))]
|
|
[value (list-ref xs mid)])
|
|
(cond
|
|
[(= value target) (return mid)]
|
|
[(< value target) (return (binarySearch xs target (+ mid 1) high))]
|
|
[else (return (binarySearch xs target low (- mid 1)))]))))))
|
|
|
|
;; Use case 16: use Map to count how often each element occurs in an array.
|
|
(define usecase-map-count-occurrences
|
|
(js
|
|
(define (countOccurrences xs)
|
|
(let* ([counts (new Map)])
|
|
(for ([x (in-list xs)])
|
|
(if (send counts has x)
|
|
(send counts set x (+ (send counts get x) 1))
|
|
(send counts set x 1)))
|
|
(return (send Array from (send counts entries)))))))
|
|
|
|
;; Use case 17: get HTML in three different ways via the DOM.
|
|
(define usecase-get-html-three-ways
|
|
(js
|
|
(define (getHtmlThreeWays)
|
|
(return (array document.body.innerHTML
|
|
(js-dot (send document querySelector "body") innerHTML)
|
|
(js-ref (send document getElementById "root") "innerHTML"))))))
|
|
|
|
;; Use case 18: determine if stringA can be arranged into stringB.
|
|
(define usecase-anagram
|
|
(js
|
|
(define (sortChars s)
|
|
(return (send (send (send s split "") sort) join "")))
|
|
|
|
(define (canArrange stringA stringB)
|
|
(return (string=? (sortChars stringA) (sortChars stringB))))))
|
|
|
|
;; Use case 19: determine what pairs in an array equal a given value, with no
|
|
;; repeated numbers in the result pairs.
|
|
(define usecase-pairs-equal-target
|
|
(js
|
|
(define (pairsEqualTarget xs target)
|
|
(let* ([seen (new Set)]
|
|
[used (new Set)]
|
|
[out (array)])
|
|
(for ([x (in-list xs)])
|
|
(let* ([y (- target x)])
|
|
(if (and (send seen has y)
|
|
(not (send used has x))
|
|
(not (send used has y)))
|
|
(begin
|
|
(send out push (array y x))
|
|
(send used add x)
|
|
(send used add y))
|
|
(send seen add x))))
|
|
(return out)))))
|
|
|
|
;; Use case 20: fetch API, handling results and errors. The function returns a
|
|
;; Promise, which the test framework awaits.
|
|
(define usecase-fetch-api
|
|
(js
|
|
(define (loadTitle url)
|
|
(return
|
|
(send
|
|
(send
|
|
(send (fetch url)
|
|
then
|
|
(lambda (response)
|
|
(return (send response json))))
|
|
then
|
|
(lambda (data)
|
|
(return (object 'ok #t 'title data.title))))
|
|
catch
|
|
(lambda (err)
|
|
(return (object 'ok #f 'message err.message))))))))
|
|
|
|
(define all-js-usecases
|
|
`((random-number . ,usecase-random-number)
|
|
(unique-values . ,usecase-unique-values)
|
|
(falsey-values . ,usecase-falsey-values)
|
|
(currying . ,usecase-currying)
|
|
(object-destructuring . ,usecase-object-destructuring)
|
|
(timer-interval . ,usecase-timer-interval)
|
|
(object-props . ,usecase-object-props)
|
|
(string-concat-order . ,usecase-string-concat-order)
|
|
(freeze-vs-seal . ,usecase-freeze-vs-seal)
|
|
(switch . ,usecase-switch)
|
|
(class-constructor . ,usecase-class-constructor)
|
|
(sort-objects-by-property . ,usecase-sort-objects-by-property)
|
|
(delete-array-elements . ,usecase-delete-array-elements)
|
|
(bubble-sort . ,usecase-bubble-sort)
|
|
(binary-search . ,usecase-binary-search)
|
|
(map-count-occurrences . ,usecase-map-count-occurrences)
|
|
(get-html-three-ways . ,usecase-get-html-three-ways)
|
|
(anagram . ,usecase-anagram)
|
|
(pairs-equal-target . ,usecase-pairs-equal-target)
|
|
(fetch-api . ,usecase-fetch-api)))
|
|
|
|
(define (show-js-usecases)
|
|
(for ([entry (in-list all-js-usecases)])
|
|
(displayln (format "// ~a" (car entry)))
|
|
(displayln (cdr entry))
|
|
(newline)))
|
|
|
|
(define (write-js-usecases-file path)
|
|
(call-with-output-file path
|
|
#:exists 'replace
|
|
(lambda (out)
|
|
(displayln "// Generated by demo/js-usecases.rkt" out)
|
|
(displayln "// Each use case is wrapped in a function so snippets with return are valid." out)
|
|
(for ([entry (in-list all-js-usecases)])
|
|
(fprintf out "\n// ~a\n" (car entry))
|
|
(fprintf out "function run_~a() {\n~a\n}\n"
|
|
(regexp-replace* #rx"[^A-Za-z0-9_$]" (symbol->string (car entry)) "_")
|
|
(cdr entry))))))
|
|
|
|
(module+ main
|
|
(show-js-usecases))
|