409 lines
12 KiB
Racket
409 lines
12 KiB
Racket
#lang scribble/manual
|
|
@(require scribble/example racket/sandbox
|
|
(for-label typed/racket/base graph
|
|
(submod "../utils.rkt" typed)
|
|
(only-in typed/graph Graph)
|
|
(only-in racket/set set)))
|
|
|
|
@title[#:tag "utils"]{dds/utils: Various Utilities}
|
|
|
|
@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.
|
|
|
|
@bold{Note:} I am currently migrating this module to Typed Racket.
|
|
This documentation only lists the functions and the types which have already
|
|
been converted. However, the typed part is currently hidden in an untyped
|
|
module, so you cannot yet use the types directly.
|
|
|
|
@bold{TODO:} Hyperlinks are currently broken in this document because it
|
|
actually documents a submodule. Fix them once the migration to Typed Racket
|
|
has reached a fixed point.
|
|
|
|
@(define utils-evaluator
|
|
(parameterize ([sandbox-output 'string]
|
|
[sandbox-error-output 'string]
|
|
[sandbox-memory-limit 50])
|
|
(make-evaluator 'typed/racket #:requires '((submod "utils.rkt" typed)))))
|
|
|
|
@section{Base types}
|
|
|
|
@defidform[Variable]{
|
|
|
|
Any Racket symbol. Designates a variable in a discrete dynamical network.
|
|
|
|
}
|
|
|
|
@defform[(VariableMapping A)]{
|
|
|
|
An immutable mapping (a hash table) assigning elements of type @racket[A] to
|
|
the variables.
|
|
|
|
}
|
|
|
|
@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
|
|
explicit @racket[hash-ref] calls.
|
|
|
|
@defproc[(eval-with [ht (VariableMapping Any)] [expr Any]) AnyValues]{
|
|
|
|
Temporarily injects the mappings from the given hash table as bindings in
|
|
a namespace including @racket[racket/base] and then evaluates the expression.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(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:
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(eval:error
|
|
(let ([ht (hash 'a 1 'b 1)]
|
|
[z 1])
|
|
(eval-with ht '(+ b z a 1)))
|
|
)]}
|
|
|
|
@defproc[(eval1-with [ht (VariableMapping Any)] [expr Any]) Any]{
|
|
|
|
Like @racket[eval-with], but returns only the first value computed by
|
|
@racket[expr].
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(let ([ht (hash 'a 1 'b 1)])
|
|
(eval1-with ht '(+ b a 1)))
|
|
]}
|
|
|
|
@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.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(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.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(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.
|
|
|
|
}
|
|
|
|
@section{Analysis of quoted expressions}
|
|
|
|
@defproc[(extract-symbols [form Any]) (Listof Symbol)]{
|
|
|
|
Produces a list of symbols appearing in the quoted expression
|
|
passed in the first argument.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(extract-symbols '(1 (2 3) x (y z 3)))
|
|
]
|
|
|
|
}
|
|
|
|
@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.
|
|
|
|
@defproc[(any->string [x Any]) String]{
|
|
|
|
Converts any value to string by calling @racket[display] on it and capturing
|
|
the result in a string.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(any->string '(a 1 (x y)))
|
|
]}
|
|
|
|
@defproc[(stringify-variable-mapping [ht (VariableMapping Any)]) (VariableMapping String)]{
|
|
|
|
Converts all the values of a @racket[VariableMapping] to string.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(stringify-variable-mapping (hash 'a '(and a b) 'b '(not b)))
|
|
]}
|
|
|
|
@defproc[(string->any [str String]) Any]{
|
|
|
|
Reads any value from string.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(string->any "(or b (not a))")
|
|
]}
|
|
|
|
@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.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(map-sexp (λ (x) (add1 (cast x Number))) '(1 2 (4 10) 3))
|
|
]}
|
|
|
|
@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].
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(unorg "(#t \"#t\" \"#t \" '(1 2 \"#f\"))")
|
|
]}
|
|
|
|
@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.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(unstringify-pairs '(("a" . 1) ("b" . "(and a (not b))")))
|
|
]}
|
|
|
|
@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].
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(read-org-variable-mapping
|
|
"((\"a\" . \"(and a b)\") (\"b\" . \"(or b (not a))\"))")
|
|
]}
|
|
|
|
@defproc[(read-symbol-list (str String)) (Listof Symbol)]{
|
|
|
|
Reads a list of symbols from a string.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(read-symbol-list "a b c")
|
|
]}
|
|
|
|
@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.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(drop-first-last "(a b)")
|
|
]}
|
|
|
|
@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.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(list-sets->list-strings (list (set 'x 'y) (set 'z) (set) (set 't)))
|
|
]}
|
|
|
|
@section{Pretty printing}
|
|
|
|
@defproc[(pretty-print-set (s (Setof Any))) String]{
|
|
|
|
Pretty prints a set by listing its elements in alphabetic order.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(pretty-print-set (set 'a 'b 1))
|
|
]}
|
|
|
|
@defproc[(pretty-print-set-sets (ms (Setof (Setof Any)))) String]{
|
|
|
|
Pretty-prints a set of sets of symbols.
|
|
|
|
Typically used for pretty-printing the annotations on the edges of
|
|
a state graph.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(pretty-print-set-sets (set (set 'a 'b) (set 'c)))
|
|
]}
|
|
|
|
@section{Additional graph utilities}
|
|
|
|
All examples in this section depend on @racket[typed/graph]:
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(require typed/graph)
|
|
]
|
|
|
|
@defproc[(dotit [graph Graph]) Void]{
|
|
|
|
Typesets the graph via @racket[graphviz] and @racket[display]s it.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(dotit (weighted-graph/directed '((1 a b) (2 b c))))
|
|
]}
|
|
|
|
@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.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(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))
|
|
]}
|
|
|
|
@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.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(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))
|
|
]}
|
|
|
|
@section{Additional list and hash map utilities}
|
|
|
|
@defproc[(collect-by-key [keys (Listof a)] [vals (Listof b)])
|
|
(Values (Listof a) (Listof (Listof b)))]{
|
|
|
|
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.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(collect-by-key '(a b a) '(1 2 3))
|
|
]}
|
|
|
|
@defproc[(collect-by-key/sets [keys (Listof a)] [vals (Listof b)])
|
|
(Values (Listof a) (Listof (Setof b)))]{
|
|
|
|
Like @racket[collect-by-key], but produce a list of sets instead of a list
|
|
of lists.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(collect-by-key/sets '(a b a) '(1 2 3))
|
|
]}
|
|
|
|
@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.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(ht-values/list->set #hash((a . (1 1))))
|
|
]}
|
|
|
|
@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].
|
|
|
|
@bold{TODO:} Remove after Racket 8.4, in which @racket[hash->list] gets a new
|
|
optional argument @racket[try-order?].
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(hash->list/ordered #hash((b . 1) (a . 1)))
|
|
]}
|
|
|
|
@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.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(multi-split-at '((1 2 3) (a b c)) 2)
|
|
]}
|
|
|
|
@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.
|
|
|
|
@examples[#:eval utils-evaluator
|
|
(lists-transpose '((a b) (1 2)))
|
|
(lists-transpose '((a b) (1 2 3) (#t)))
|
|
]}
|
|
|
|
@section{Functions and procedures}
|
|
|
|
@section{Randomness}
|
|
|
|
@section{Additional stream utilities}
|
|
|
|
@section{Boolean operations}
|