2020-11-29 00:28:46 +01:00
|
|
|
|
#lang scribble/manual
|
2020-11-29 23:07:40 +01:00
|
|
|
|
@(require scribble/example racket/sandbox
|
2022-02-03 00:09:11 +01:00
|
|
|
|
(for-label typed/racket/base graph
|
2022-03-05 13:41:40 +01:00
|
|
|
|
"../utils.rkt"
|
2022-02-03 16:40:02 +01:00
|
|
|
|
(only-in typed/graph Graph)
|
2022-03-05 00:24:02 +01:00
|
|
|
|
(only-in racket/set set)
|
2022-08-03 01:15:24 +02:00
|
|
|
|
(only-in racket/stream stream->list stream-take)
|
|
|
|
|
(only-in typed/racket/class class super-new new send)))
|
2020-11-29 00:28:46 +01:00
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@(define utils-evaluator
|
|
|
|
|
(parameterize ([sandbox-output 'string]
|
|
|
|
|
[sandbox-error-output 'string]
|
2023-04-03 16:19:57 +02:00
|
|
|
|
[sandbox-memory-limit 500])
|
2022-04-24 23:59:55 +02:00
|
|
|
|
(make-evaluator 'typed/racket #:requires '("utils.rkt"))))
|
|
|
|
|
|
|
|
|
|
@(define-syntax-rule (ex . args)
|
|
|
|
|
(examples #:eval utils-evaluator . args))
|
|
|
|
|
|
2022-04-30 00:11:09 +02:00
|
|
|
|
@(define-syntax-rule (deftypeform . args)
|
2022-04-29 16:32:22 +02:00
|
|
|
|
(defform #:kind "type" . args))
|
|
|
|
|
|
|
|
|
|
@(define-syntax-rule (deftype . args)
|
|
|
|
|
(defidform #:kind "polymorphic type" . args))
|
|
|
|
|
|
2020-11-29 00:28:46 +01:00
|
|
|
|
@title[#:tag "utils"]{dds/utils: Various Utilities}
|
|
|
|
|
|
2020-11-29 21:41:00 +01:00
|
|
|
|
@defmodule[dds/utils]
|
|
|
|
|
|
|
|
|
|
This module defines miscellaneous utilities, supporting the other modules of
|
|
|
|
|
the package: evaluating sexps, manipulating lists,
|
|
|
|
|
@hyperlink["https://orgmode.org/"]{Org-mode} interoperability, etc.
|
|
|
|
|
|
2022-01-16 21:00:41 +01:00
|
|
|
|
@section{Base types}
|
|
|
|
|
|
2022-04-29 16:32:22 +02:00
|
|
|
|
@deftype[Variable]{
|
2022-01-16 21:00:41 +01:00
|
|
|
|
|
|
|
|
|
Any Racket symbol. Designates a variable in a discrete dynamical network.
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-30 00:11:09 +02:00
|
|
|
|
@deftypeform[(VariableMapping A)]{
|
2022-01-16 21:00:41 +01:00
|
|
|
|
|
|
|
|
|
An immutable mapping (a hash table) assigning elements of type @racket[A] to
|
|
|
|
|
the variables.
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-09 23:15:33 +02:00
|
|
|
|
@defform[(NonemptyListof a)]{
|
|
|
|
|
|
|
|
|
|
A @racket[(Listof a)] which contains at least one element.
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-24 14:34:57 +02:00
|
|
|
|
@section{Type utilities}
|
|
|
|
|
|
|
|
|
|
Typed Racket's @racket[cast] should only be used as a last resort, because it
|
|
|
|
|
installs two contracts which may have a significant performance penalty.
|
|
|
|
|
See
|
|
|
|
|
@hyperlink["https://racket.discourse.group/t/managing-cast-performance-penalty/905"]{this
|
|
|
|
|
discussion} for more details. The best approach is to use
|
|
|
|
|
@hyperlink["https://docs.racket-lang.org/ts-guide/occurrence-typing.html"]{occurrence
|
|
|
|
|
typing} either via a direct @racket[if] check using a predicate, or using
|
|
|
|
|
@racket[assert].
|
|
|
|
|
|
|
|
|
|
@defform[(assert-type expr type)]{
|
|
|
|
|
|
|
|
|
|
@racket[assert] that the type of @racket[expr] is @racket[type].
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-04-24 14:34:57 +02:00
|
|
|
|
(define some-number : Any 1)
|
|
|
|
|
(assert-type some-number Integer)
|
|
|
|
|
(assert-type some-number Positive-Integer)
|
|
|
|
|
(eval:error (assert-type some-number Zero1))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-06-21 00:42:56 +02:00
|
|
|
|
@deftogether[(@defform[(for/first/typed type-ann (for-clause ...) expr ...+)])
|
|
|
|
|
@defform[(for*/first/typed type-ann (for-clause ...) expr ...+)]]{
|
|
|
|
|
|
|
|
|
|
Typed versions of @racket[for/first] and @racket[for*/first].
|
|
|
|
|
|
|
|
|
|
@ex[
|
|
|
|
|
(for/first/typed : (Option Integer)
|
|
|
|
|
([i (in-range 1 10)]
|
|
|
|
|
#:when (zero? (modulo i 5)))
|
|
|
|
|
(* i 3))
|
|
|
|
|
(for*/first/typed : (Option (Pairof Integer Integer))
|
|
|
|
|
([i (in-range 1 10)]
|
|
|
|
|
[j (in-range 1 10)]
|
|
|
|
|
#:when (> (+ i j) 5))
|
|
|
|
|
(cons i j))
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
The implementation of these macros is a simplified version the definition of
|
|
|
|
|
@hyperlink["https://github.com/racket/typed-racket/blob/9d3264c97aa63b6a7163a219937b88a612add8ab/typed-racket-lib/typed-racket/base-env/prims.rkt#L512"]{@racket[define-for/acc:-variant]}.
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-03 01:15:24 +02:00
|
|
|
|
@defform[(define/abstract/error method-name args ...)]{
|
|
|
|
|
|
|
|
|
|
In a typed class, defines a public method @racket[method-name] with the
|
|
|
|
|
arguments @racket[args] and with the body announcing that this method
|
|
|
|
|
is abstract.
|
|
|
|
|
|
|
|
|
|
@ex[
|
|
|
|
|
(define my-abstract-class%
|
|
|
|
|
(class object%
|
|
|
|
|
(super-new)
|
|
|
|
|
|
|
|
|
|
(: abstract-increment (-> Integer Integer))
|
|
|
|
|
(define/abstract/error (abstract-increment x))))
|
|
|
|
|
|
|
|
|
|
(define obj (new my-abstract-class%))
|
|
|
|
|
(eval:error (send obj abstract-increment 1))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-09-21 00:53:12 +02:00
|
|
|
|
@defform[(relax-arg-type/any name arg-type)]{
|
|
|
|
|
|
|
|
|
|
Defines a unary anonymous function whose argument type is @racket[Any], and
|
|
|
|
|
which calls @racket[name], with the argument coerced to @racket[arg-type].
|
|
|
|
|
|
|
|
|
|
@ex[
|
|
|
|
|
(relax-arg-type/any add1 Number)
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
The main use of this macro is to allow easily passing different one-argument
|
|
|
|
|
functions as arguments of the type @racket[(-> Any Any)]. See for example
|
|
|
|
|
@racket[update-graph].
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-29 21:41:00 +01:00
|
|
|
|
@section{Hashtable injection}
|
|
|
|
|
|
|
|
|
|
This section defines some utilities to streamline the usage of hash tables
|
|
|
|
|
mapping symbols to values. The goal is essentially to avoid having to write
|
2020-11-29 23:07:40 +01:00
|
|
|
|
explicit @racket[hash-ref] calls.
|
|
|
|
|
|
2022-01-16 23:10:08 +01:00
|
|
|
|
@defproc[(eval-with [ht (VariableMapping Any)] [expr Any]) AnyValues]{
|
2020-11-29 23:07:40 +01:00
|
|
|
|
|
|
|
|
|
Temporarily injects the mappings from the given hash table as bindings in
|
|
|
|
|
a namespace including @racket[racket/base] and then evaluates the expression.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2020-11-29 23:07:40 +01:00
|
|
|
|
(let ([ht (hash 'a 1 'b 1)])
|
|
|
|
|
(eval-with ht '(+ b a 1)))
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
The local bindings from the current lexical scope are not
|
|
|
|
|
conserved. Therefore, the following outputs an error about a
|
|
|
|
|
missing identifier:
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2020-11-29 23:07:40 +01:00
|
|
|
|
(eval:error
|
|
|
|
|
(let ([ht (hash 'a 1 'b 1)]
|
|
|
|
|
[z 1])
|
|
|
|
|
(eval-with ht '(+ b z a 1)))
|
|
|
|
|
)]}
|
2020-11-29 21:41:00 +01:00
|
|
|
|
|
2022-01-16 23:10:08 +01:00
|
|
|
|
@defproc[(eval1-with [ht (VariableMapping Any)] [expr Any]) Any]{
|
|
|
|
|
|
|
|
|
|
Like @racket[eval-with], but returns only the first value computed by
|
|
|
|
|
@racket[expr].
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-01-16 23:10:08 +01:00
|
|
|
|
(let ([ht (hash 'a 1 'b 1)])
|
|
|
|
|
(eval1-with ht '(+ b a 1)))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-01-19 00:32:45 +01:00
|
|
|
|
@defform[(auto-hash-ref/explicit stx)
|
|
|
|
|
#:contracts ([stx (VariableMapping A)])]{
|
|
|
|
|
|
|
|
|
|
Given a @racket[VariableMapping] and a sequence of symbols, binds these symbols
|
|
|
|
|
to the values they are associated with in the hash table, then puts the body in
|
|
|
|
|
the context of these bindings.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-01-19 00:32:45 +01:00
|
|
|
|
(define env #hash((a . 1) (b . 2)))
|
|
|
|
|
(auto-hash-ref/explicit (env a b) (+ a (* 2 b)))
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
Note that only one expression can be supplied in the body.
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@defform[(auto-hash-ref/: stx)
|
|
|
|
|
#:contracts ([stx (VariableMapping A)])]{
|
|
|
|
|
|
|
|
|
|
Given an expression and a @racket[VariableMapping], looks up the symbols with
|
|
|
|
|
a leading semicolon and binds them to the value they are associated with in the
|
|
|
|
|
hash table.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-01-19 00:32:45 +01:00
|
|
|
|
(define env #hash((a . 1) (b . 2)))
|
|
|
|
|
(auto-hash-ref/: env (+ :a (* 2 :b)))
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
Thus the symbol @racket[:a] is matched to the key @racket['a] in the
|
|
|
|
|
hash table.
|
|
|
|
|
|
|
|
|
|
Note that only one expression can be supplied in the body.
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-06 22:25:27 +01:00
|
|
|
|
@deftogether[(@defform*[((lambda/: body) (lambda/: type body))]
|
|
|
|
|
@defform*[((λ/: body) (λ/: type body))])]{
|
|
|
|
|
|
|
|
|
|
Defines an anonymous function with the body @racket[body], taking a hash table
|
|
|
|
|
as an argument, and applying @racket[auto-hash-ref/:] to @racket[body] in the
|
|
|
|
|
context of this hash table.
|
|
|
|
|
|
|
|
|
|
@ex[
|
|
|
|
|
(let ([ht (hash 'a 1 'b 2)])
|
|
|
|
|
((λ/: (+ :a :b)) ht))
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
If the optional annotation @racket[type] is specified, the only argument of the
|
|
|
|
|
resulting lambda will be of type @racket[type].
|
|
|
|
|
|
|
|
|
|
@ex[
|
|
|
|
|
(let ([ht (hash 'a 1 'b 2)])
|
|
|
|
|
((λ/: (HashTable Symbol Natural) (+ :a :b)) ht))
|
|
|
|
|
]}
|
|
|
|
|
|
|
|
|
|
@defform*[((define/: name body)
|
|
|
|
|
(define/: name type body))]{
|
|
|
|
|
|
|
|
|
|
A shortcut for @racket[(define name (lambda/: body))], with the optional
|
|
|
|
|
type annotation.
|
|
|
|
|
|
|
|
|
|
@ex[
|
|
|
|
|
(let ([ht (hash 'a 1 'b 2)])
|
|
|
|
|
(: f (-> (HashTable Symbol Natural) Natural))
|
|
|
|
|
(define/: f (+ :a :b))
|
|
|
|
|
(f ht))
|
|
|
|
|
]}
|
|
|
|
|
|
2020-11-29 21:41:00 +01:00
|
|
|
|
@section{Analysis of quoted expressions}
|
|
|
|
|
|
2022-01-19 00:40:41 +01:00
|
|
|
|
@defproc[(extract-symbols [form Any]) (Listof Symbol)]{
|
|
|
|
|
|
|
|
|
|
Produces a list of symbols appearing in the quoted expression
|
|
|
|
|
passed in the first argument.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-01-19 00:40:41 +01:00
|
|
|
|
(extract-symbols '(1 (2 3) x (y z 3)))
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-29 21:41:00 +01:00
|
|
|
|
@section{Org-mode interoperability}
|
|
|
|
|
|
|
|
|
|
Org-mode supports laying out the output of code blocks as tables, which is very
|
|
|
|
|
practical for various variable mappings (e.g., states). However, when the hash
|
|
|
|
|
table maps variables to lists, Org-mode will create a column per list element,
|
|
|
|
|
which may or may not be the desired effect. This section defines some
|
|
|
|
|
utilities for nicer interoperation with Org-mode tables. It also defines some
|
|
|
|
|
shortcuts to reduce the number of words to type when using dds with Org-mode.
|
|
|
|
|
See
|
|
|
|
|
@hyperlink["https://git.marvid.fr/scolobb/dds/src/branch/master/example/example.org"]{example.org}
|
|
|
|
|
for examples of usage.
|
|
|
|
|
|
2022-01-20 19:58:06 +01:00
|
|
|
|
@defproc[(any->string [x Any]) String]{
|
|
|
|
|
|
|
|
|
|
Converts any value to string by calling @racket[display] on it and capturing
|
|
|
|
|
the result in a string.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-01-20 19:58:06 +01:00
|
|
|
|
(any->string '(a 1 (x y)))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-01-23 13:47:36 +01:00
|
|
|
|
@defproc[(stringify-variable-mapping [ht (VariableMapping Any)]) (VariableMapping String)]{
|
|
|
|
|
|
|
|
|
|
Converts all the values of a @racket[VariableMapping] to string.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-01-23 13:47:36 +01:00
|
|
|
|
(stringify-variable-mapping (hash 'a '(and a b) 'b '(not b)))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-01-23 13:53:41 +01:00
|
|
|
|
@defproc[(string->any [str String]) Any]{
|
|
|
|
|
|
|
|
|
|
Reads any value from string.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-01-23 13:53:41 +01:00
|
|
|
|
(string->any "(or b (not a))")
|
|
|
|
|
]}
|
|
|
|
|
|
2022-01-23 15:50:06 +01:00
|
|
|
|
@defproc[(map-sexp [func (-> Any Any)] [sexp Any]) Any]{
|
|
|
|
|
|
|
|
|
|
Given a @racket[Sexp], applies the @racket[func] to any object which is not
|
|
|
|
|
a list.
|
|
|
|
|
|
|
|
|
|
@racket[map-sexp] will not check whether @racket[func] is indeed applicable to
|
|
|
|
|
every non-list element of @racket[sexp]. If this is not the case, a contract
|
|
|
|
|
violation for func will be generated.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-01-23 15:50:06 +01:00
|
|
|
|
(map-sexp (λ (x) (add1 (cast x Number))) '(1 2 (4 10) 3))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-01-23 15:57:50 +01:00
|
|
|
|
@defproc*[([(read-org-sexp [str String]) Any]
|
|
|
|
|
[(unorg [str String]) Any])]{
|
|
|
|
|
|
|
|
|
|
Reads a @racket[sexp] from a string produced by Org-mode for a named table.
|
|
|
|
|
|
|
|
|
|
@racket[unorg] is a shortcut for @racket[read-org-sexp].
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-01-23 15:57:50 +01:00
|
|
|
|
(unorg "(#t \"#t\" \"#t \" '(1 2 \"#f\"))")
|
|
|
|
|
]}
|
|
|
|
|
|
2022-01-25 00:46:12 +01:00
|
|
|
|
@defform[(GeneralPair A B)]{
|
|
|
|
|
|
|
|
|
|
A @racket[(Pair A B)] or a @racket[(List A B)].
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@defproc[(unstringify-pairs [pairs (Listof (GeneralPair String Any))])
|
|
|
|
|
(Listof (GeneralPair Symbol Any))]{
|
|
|
|
|
|
|
|
|
|
Given a list of pairs of strings and some other values (possibly strings),
|
|
|
|
|
converts the first element of each pair to a string, and reads the second
|
|
|
|
|
element with @racket[string->any] or keeps it as is if it is not a string.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-01-25 00:46:12 +01:00
|
|
|
|
(unstringify-pairs '(("a" . 1) ("b" . "(and a (not b))")))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-01-27 21:10:13 +01:00
|
|
|
|
@defproc*[([(read-org-variable-mapping [str String]) (VariableMapping Any)]
|
|
|
|
|
[(unorgv [str String]) (VariableMapping Any)])]{
|
|
|
|
|
|
|
|
|
|
Reads a @racket[VariableMapping] from a string, such as the one which Org-mode
|
|
|
|
|
produces from tables.
|
|
|
|
|
|
|
|
|
|
@racket[unorgv] is a synonym of @racket[read-org-variable-mapping].
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-01-27 21:10:13 +01:00
|
|
|
|
(read-org-variable-mapping
|
|
|
|
|
"((\"a\" . \"(and a b)\") (\"b\" . \"(or b (not a))\"))")
|
|
|
|
|
]}
|
|
|
|
|
|
2022-02-03 10:48:51 +01:00
|
|
|
|
@defproc[(read-symbol-list (str String)) (Listof Symbol)]{
|
|
|
|
|
|
|
|
|
|
Reads a list of symbols from a string.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-02-03 10:48:51 +01:00
|
|
|
|
(read-symbol-list "a b c")
|
|
|
|
|
]}
|
|
|
|
|
|
2022-02-03 10:55:34 +01:00
|
|
|
|
@defproc[(drop-first-last (str String)) String]{
|
|
|
|
|
|
|
|
|
|
Removes the first and the last symbol of a given string.
|
|
|
|
|
|
|
|
|
|
Useful for removing the parentheses in string representations of lists.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-02-03 10:55:34 +01:00
|
|
|
|
(drop-first-last "(a b)")
|
|
|
|
|
]}
|
|
|
|
|
|
2022-02-03 16:40:02 +01:00
|
|
|
|
@defproc[(list-sets->list-strings (lst (Listof (Setof Any)))) (Listof String)]{
|
|
|
|
|
|
|
|
|
|
Converts a list of sets of symbols to a list of strings containing
|
|
|
|
|
those symbols.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-02-03 16:40:02 +01:00
|
|
|
|
(list-sets->list-strings (list (set 'x 'y) (set 'z) (set) (set 't)))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-09-18 01:20:00 +02:00
|
|
|
|
@section[#:tag "utils_Pretty_printing"]{Pretty printing}
|
2022-02-04 00:08:22 +01:00
|
|
|
|
|
2023-08-17 16:58:21 +02:00
|
|
|
|
@defproc[(pretty-print-set [s (U (Setof Any) (Listof Any))]) String]{
|
2022-02-03 23:56:28 +01:00
|
|
|
|
|
|
|
|
|
Pretty prints a set by listing its elements in alphabetic order.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-02-03 23:56:28 +01:00
|
|
|
|
(pretty-print-set (set 'a 'b 1))
|
2023-08-17 16:58:21 +02:00
|
|
|
|
(pretty-print-set (list 'a 'b 1))
|
2022-02-03 23:56:28 +01:00
|
|
|
|
]}
|
|
|
|
|
|
2023-08-17 11:33:47 +02:00
|
|
|
|
@defproc[(pretty-print-set-sets [ms (U (Listof (Setof Any)) (Setof (Setof Any)))])
|
|
|
|
|
String]{
|
2022-02-04 00:06:19 +01:00
|
|
|
|
|
|
|
|
|
Pretty-prints a set of sets of symbols.
|
|
|
|
|
|
|
|
|
|
Typically used for pretty-printing the annotations on the edges of
|
|
|
|
|
a state graph.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-02-04 00:06:19 +01:00
|
|
|
|
(pretty-print-set-sets (set (set 'a 'b) (set 'c)))
|
2023-08-17 11:33:47 +02:00
|
|
|
|
(pretty-print-set-sets (list (set 'a 'b) (set 'c)))
|
2022-02-04 00:06:19 +01:00
|
|
|
|
]}
|
|
|
|
|
|
2022-02-04 00:09:39 +01:00
|
|
|
|
@section{Additional graph utilities}
|
2022-02-08 00:10:49 +01:00
|
|
|
|
|
|
|
|
|
All examples in this section depend on @racket[typed/graph]:
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-02-08 00:10:49 +01:00
|
|
|
|
(require typed/graph)
|
|
|
|
|
]
|
|
|
|
|
|
2022-02-04 00:09:39 +01:00
|
|
|
|
@defproc[(dotit [graph Graph]) Void]{
|
|
|
|
|
|
2022-02-06 23:50:40 +01:00
|
|
|
|
Typesets the graph via @racket[graphviz] and @racket[display]s it.
|
2022-02-04 00:09:39 +01:00
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-02-04 00:09:39 +01:00
|
|
|
|
(dotit (weighted-graph/directed '((1 a b) (2 b c))))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-02-06 23:44:03 +01:00
|
|
|
|
@defproc[(update-vertices/unweighted [graph Graph] [func (-> Any Any)]) Graph]{
|
|
|
|
|
|
|
|
|
|
Applies a transformation to every vertex in the unweighted graph and returns
|
|
|
|
|
the new graph.
|
|
|
|
|
|
|
|
|
|
If the transformation function maps two vertices to the same values, these
|
|
|
|
|
vertices will be merged in the resulting graph. The transformation function
|
|
|
|
|
may be called multiple times for the same vertex.
|
|
|
|
|
|
|
|
|
|
This function does not rely on @racket[rename-vertex!], so it can be used to
|
|
|
|
|
permute vertex labels.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-02-06 23:44:03 +01:00
|
|
|
|
(define g (directed-graph '((a b) (b c))))
|
|
|
|
|
(define (double-labels [x : Any])
|
|
|
|
|
(define x-str (symbol->string (cast x Symbol)))
|
|
|
|
|
(string->symbol (string-append x-str x-str)))
|
|
|
|
|
(dotit (update-vertices/unweighted g double-labels))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-02-08 00:08:42 +01:00
|
|
|
|
@defproc[(update-graph [graph Graph]
|
|
|
|
|
[#:v-func v-func (-> Any Any) identity]
|
|
|
|
|
[#:e-func e-func (-> Any Any) identity])
|
|
|
|
|
Graph]{
|
|
|
|
|
|
|
|
|
|
Given a (directed) graph, apply the transformation @racket[v-func] to every
|
|
|
|
|
vertex label and, if the graph is a weighted graph, the transformation
|
|
|
|
|
@racket[e-func] to every edge label. Both transformations default to identity
|
|
|
|
|
functions. If @racket[graph] is an weighted graph, the result is a weighted
|
|
|
|
|
graph. If @racket[graph] is an unweighted graph, the result is an
|
|
|
|
|
unweighted graph.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-02-08 00:08:42 +01:00
|
|
|
|
(define g (weighted-graph/directed '((10 a b) (11 b c))))
|
|
|
|
|
(define (double-labels [x : Any])
|
|
|
|
|
(define x-str (symbol->string (cast x Symbol)))
|
|
|
|
|
(string->symbol (string-append x-str x-str)))
|
|
|
|
|
(define (double-edges [x : Any])
|
|
|
|
|
(* 2 (cast x Number)))
|
|
|
|
|
(dotit (update-graph g #:v-func double-labels #:e-func double-edges))
|
|
|
|
|
]}
|
2022-02-06 23:44:03 +01:00
|
|
|
|
|
2020-11-29 21:41:00 +01:00
|
|
|
|
@section{Additional list and hash map utilities}
|
|
|
|
|
|
2022-02-10 00:12:50 +01:00
|
|
|
|
@defproc[(collect-by-key [keys (Listof a)] [vals (Listof b)])
|
|
|
|
|
(Values (Listof a) (Listof (Listof b)))]{
|
2022-02-09 01:07:37 +01:00
|
|
|
|
|
|
|
|
|
Given a list of keys and the corresponding values, collects all the values
|
|
|
|
|
associated to any given key and returns a list of keys without duplicates, and
|
|
|
|
|
a list containing the corresponding list of values.
|
|
|
|
|
|
|
|
|
|
If @racket[keys] can be treated as edges (i.e. pairs of vertices), the results
|
|
|
|
|
produced by this function are suitable for graph constructors.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-02-09 01:07:37 +01:00
|
|
|
|
(collect-by-key '(a b a) '(1 2 3))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-02-10 00:12:50 +01:00
|
|
|
|
@defproc[(collect-by-key/sets [keys (Listof a)] [vals (Listof b)])
|
|
|
|
|
(Values (Listof a) (Listof (Setof b)))]{
|
2022-02-09 23:55:20 +01:00
|
|
|
|
|
|
|
|
|
Like @racket[collect-by-key], but produce a list of sets instead of a list
|
|
|
|
|
of lists.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-02-09 23:55:20 +01:00
|
|
|
|
(collect-by-key/sets '(a b a) '(1 2 3))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-02-10 00:08:49 +01:00
|
|
|
|
@defproc[(ht-values/list->set [ht (HashTable a (Listof b))])
|
|
|
|
|
(HashTable a (Setof b))]{
|
|
|
|
|
|
|
|
|
|
Converts the values of a hash table from lists to sets.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-02-10 00:08:49 +01:00
|
|
|
|
(ht-values/list->set #hash((a . (1 1))))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-02-10 23:37:40 +01:00
|
|
|
|
@defproc[(hash->list/ordered [ht (HashTable a b)])
|
|
|
|
|
(Listof (Pairof a b))]{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Returns the key-value pairs of a given hash table in the order in which the
|
|
|
|
|
hash table orders them for @racket[hash-map].
|
|
|
|
|
|
2022-03-05 13:44:48 +01:00
|
|
|
|
@bold{TODO:} Remove after Typed Racket has caught up with Racket 8.4, in which
|
|
|
|
|
@racket[hash->list] gets a new optional argument @racket[try-order?].
|
2022-02-10 23:37:40 +01:00
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-02-10 23:37:40 +01:00
|
|
|
|
(hash->list/ordered #hash((b . 1) (a . 1)))
|
|
|
|
|
]}
|
2022-02-09 23:55:20 +01:00
|
|
|
|
|
2023-03-12 20:54:25 +01:00
|
|
|
|
@defproc[(hash-replace-keys/ordered [ht (Immutable-HashTable (K1 V))]
|
|
|
|
|
[new-keys (Listof K2)])
|
|
|
|
|
(Immutable-HashTable K2 V)]{
|
|
|
|
|
|
|
|
|
|
Replaces the keys in @racket[ht] by the keys in @racket[new-keys].
|
|
|
|
|
|
|
|
|
|
The key-value pairs of the hash table @racket[ht] are processed in the
|
|
|
|
|
order produced by @racket[hash-map] with @racket[#:try-order?] set to
|
|
|
|
|
@racket[#t].
|
|
|
|
|
|
|
|
|
|
@ex[
|
|
|
|
|
(hash-replace-keys/ordered (hash 'a 1 'b 2) '(x y))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-02-11 00:01:07 +01:00
|
|
|
|
@defproc[(multi-split-at [lists (Listof (Listof a))]
|
|
|
|
|
[pos Integer])
|
|
|
|
|
(Values (Listof (Listof a)) (Listof (Listof a)))]{
|
|
|
|
|
|
|
|
|
|
Given a list of lists, splits every single list at the given position, and then
|
|
|
|
|
returns two lists of lists: one consisting of the first halves, and the one
|
|
|
|
|
consisting of the second halves.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-02-11 00:01:07 +01:00
|
|
|
|
(multi-split-at '((1 2 3) (a b c)) 2)
|
|
|
|
|
]}
|
|
|
|
|
|
2022-02-13 19:33:04 +01:00
|
|
|
|
@defproc[(lists-transpose [lists (List (Listof a) ... a)])
|
|
|
|
|
(Listof (List a ... a))]{
|
|
|
|
|
|
|
|
|
|
Transposes a list of lists. The length of the resulting list is the length of
|
|
|
|
|
the shortest list in @racket[lists].
|
|
|
|
|
|
|
|
|
|
This function is essentially @racket[in-parallel], wrapped in
|
|
|
|
|
a couple conversions.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-02-13 19:33:04 +01:00
|
|
|
|
(lists-transpose '((a b) (1 2)))
|
|
|
|
|
(lists-transpose '((a b) (1 2 3) (#t)))
|
2022-04-01 00:09:42 +02:00
|
|
|
|
]
|
|
|
|
|
|
2022-04-07 00:21:48 +02:00
|
|
|
|
As of 2022-04-07, Typed Racket cannot convert the type of
|
2022-04-07 00:40:46 +02:00
|
|
|
|
@racket[lists-transpose] to a contract. The @seclink["utils/untyped"]{untyped
|
2022-04-07 00:21:48 +02:00
|
|
|
|
submodule} provides a version of this function which can be used in
|
|
|
|
|
untyped code.
|
2022-04-01 00:09:42 +02:00
|
|
|
|
|
|
|
|
|
}
|
2022-02-13 19:33:04 +01:00
|
|
|
|
|
2022-03-06 20:52:33 +01:00
|
|
|
|
@defproc[(append-lists [lsts (Listof (List (Listof a) (Listof a)))])
|
|
|
|
|
(Listof (Listof a))]{
|
|
|
|
|
|
|
|
|
|
@racket[lsts] is a list of rows, in which each row is split in two halves.
|
|
|
|
|
The function returns the list of the same rows, with the two halves appended.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-03-06 20:52:33 +01:00
|
|
|
|
(append-lists '(((1 2) (a b))
|
|
|
|
|
((3 4) (c d))))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-03-04 18:12:19 +01:00
|
|
|
|
@section{Randomness}
|
|
|
|
|
|
2022-02-15 00:14:52 +01:00
|
|
|
|
@defproc*[([(in-random) (Sequenceof Flonum)]
|
|
|
|
|
[(in-random [k Integer]) (Sequenceof Nonnegative-Fixnum)]
|
|
|
|
|
[(in-random [min Integer] [max Integer]) (Sequenceof Nonnegative-Fixnum)])]{
|
|
|
|
|
|
|
|
|
|
Generates a stream of (inexact) random numbers. The meaning of the arguments
|
|
|
|
|
is the same as for the function @racket[random]:
|
|
|
|
|
|
|
|
|
|
@itemlist[
|
|
|
|
|
|
|
|
|
|
@item{@racket[(in-random)] — a stream of random inexact numbers between
|
|
|
|
|
0 and 1,}
|
|
|
|
|
|
|
|
|
|
@item{@racket[(in-random k)] — a stream of random exact integers in the range
|
|
|
|
|
@racket[0] to @racket[k]-1.}
|
|
|
|
|
|
|
|
|
|
@item{@racket[(in-random min max)] — a stream of random exact integers the
|
|
|
|
|
range @racket[min] to @racket[max]-1.}
|
|
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-02-15 00:14:52 +01:00
|
|
|
|
(require typed/racket/stream)
|
|
|
|
|
(stream->list (stream-take (in-random) 5))
|
|
|
|
|
(stream->list (stream-take (in-random 10) 5))
|
|
|
|
|
(stream->list (stream-take (in-random 5 10) 5))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-03-04 18:12:19 +01:00
|
|
|
|
@section{Additional stream utilities}
|
|
|
|
|
|
2022-03-04 17:24:18 +01:00
|
|
|
|
@defproc[(cartesian-product-2/stream [s1 (Sequenceof a)]
|
|
|
|
|
[s2 (Sequenceof b)])
|
|
|
|
|
(Sequenceof (Pair a b))]{
|
|
|
|
|
|
|
|
|
|
Generates a stream containing all the pairs of the elements from @racket[s1]
|
|
|
|
|
and @racket[s2]. The elements of @racket[s2] are enumerated in order for every
|
|
|
|
|
element of the @racket[s1], taken in order as well.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-03-04 17:24:18 +01:00
|
|
|
|
(require typed/racket/stream)
|
|
|
|
|
(stream->list (cartesian-product-2/stream (in-range 1 5) '(a b)))
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
The streams can be infinite. If the second stream is infinite, only the first
|
|
|
|
|
element of @racket[s1] will be enumerated.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-03-04 17:24:18 +01:00
|
|
|
|
(stream->list (stream-take (cartesian-product-2/stream '(a b) (in-naturals)) 10))
|
|
|
|
|
]}
|
|
|
|
|
|
2022-03-04 18:08:00 +01:00
|
|
|
|
@defproc[(cartesian-product/stream [ss (Listof (Sequenceof a))])
|
|
|
|
|
(Sequenceof (Listof a))]{
|
|
|
|
|
|
|
|
|
|
Generates a stream containing all the elements of the Cartesian product between
|
|
|
|
|
the streams of @racket[ss].
|
|
|
|
|
|
|
|
|
|
This function relies on @racket[cartesian-product-2/stream] to build the
|
|
|
|
|
Cartesian product, so it has the same properties with respect to the order in
|
|
|
|
|
which the streams are enumerated.
|
|
|
|
|
|
|
|
|
|
Union types can be used to build the Cartesian product of streams containing
|
|
|
|
|
values of different types.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-03-04 18:08:00 +01:00
|
|
|
|
(stream->list (cartesian-product/stream (list (in-range 3) (in-range 4 6) '(a b))))
|
|
|
|
|
]}
|
|
|
|
|
|
2020-11-29 21:41:00 +01:00
|
|
|
|
@section{Boolean operations}
|
2022-03-05 00:14:38 +01:00
|
|
|
|
|
|
|
|
|
@defproc[(boolean-power [n Integer])
|
|
|
|
|
(Listof (Listof Boolean))]{
|
|
|
|
|
|
|
|
|
|
Returns the @racket[n]-th Cartesian power of the Boolean domain.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-03-05 00:14:38 +01:00
|
|
|
|
(boolean-power 2)
|
2022-03-05 00:20:51 +01:00
|
|
|
|
]}
|
|
|
|
|
|
|
|
|
|
@defproc[(boolean-power/stream [n Integer])
|
|
|
|
|
(Sequenceof (Listof Boolean))]{
|
|
|
|
|
|
|
|
|
|
Like @racket[boolean-power], but returns a stream.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-03-05 00:20:51 +01:00
|
|
|
|
(stream->list (boolean-power/stream 2))
|
|
|
|
|
]}
|
2022-03-05 12:55:23 +01:00
|
|
|
|
|
|
|
|
|
@defproc[(any->01 [x Any]) (U Zero One)]{
|
|
|
|
|
|
|
|
|
|
Converts any non-@racket[#f] value to 1 and @racket[#f] to 0.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-03-05 12:55:23 +01:00
|
|
|
|
(any->01 #t)
|
|
|
|
|
(any->01 #f)
|
|
|
|
|
(any->01 'hello)
|
|
|
|
|
]}
|
2022-03-05 13:07:44 +01:00
|
|
|
|
|
|
|
|
|
@defproc[(01->boolean [x (U Zero One)]) Boolean]{
|
|
|
|
|
|
|
|
|
|
Converts 0 to @racket[#f] and 1 to @racket[#t].
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex[
|
2022-03-05 13:07:44 +01:00
|
|
|
|
(01->boolean 0)
|
|
|
|
|
(01->boolean 1)
|
|
|
|
|
]}
|
2022-04-07 00:21:48 +02:00
|
|
|
|
|
2022-04-07 00:41:35 +02:00
|
|
|
|
@section[#:tag "utils/untyped"]{Untyped definitions}
|
2022-04-07 00:21:48 +02:00
|
|
|
|
|
|
|
|
|
@defmodule[(submod dds/utils untyped)]
|
|
|
|
|
|
|
|
|
|
@(require (for-label (only-in racket/contract/base listof any/c)))
|
|
|
|
|
|
|
|
|
|
This submodule contains some functions whose types cannot be converted to
|
|
|
|
|
contracts by Typed Racket.
|
|
|
|
|
|
|
|
|
|
@(define utils-evaluator/untyped
|
|
|
|
|
(parameterize ([sandbox-output 'string]
|
|
|
|
|
[sandbox-error-output 'string]
|
|
|
|
|
[sandbox-memory-limit 50])
|
|
|
|
|
(make-evaluator 'racket #:requires '((submod "utils.rkt" untyped)))))
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@(define-syntax-rule (ex/untyped . args)
|
|
|
|
|
(examples #:eval utils-evaluator/untyped . args))
|
|
|
|
|
|
2022-04-07 00:21:48 +02:00
|
|
|
|
@defproc[(lists-transpose [lists (listof (listof any/c))])
|
|
|
|
|
(listof (listof any/c))]{
|
|
|
|
|
|
|
|
|
|
Transposes a list of lists. The length of the resulting list is the length of
|
|
|
|
|
the shortest list in @racket[lists].
|
|
|
|
|
|
|
|
|
|
This function is essentially @racket[in-parallel], wrapped in
|
|
|
|
|
a couple conversions.
|
|
|
|
|
|
2022-04-24 23:59:55 +02:00
|
|
|
|
@ex/untyped[
|
2022-04-07 00:21:48 +02:00
|
|
|
|
(lists-transpose '((a b) (1 2)))
|
|
|
|
|
(lists-transpose '((a b) (1 2 3) (#t)))
|
|
|
|
|
]}
|