2020-11-29 00:28:46 +01:00
|
|
|
#lang scribble/manual
|
2020-11-29 23:07:40 +01:00
|
|
|
@(require scribble/example racket/sandbox
|
2020-12-12 00:46:50 +01:00
|
|
|
(for-label racket/base graph "../utils.rkt"
|
2020-12-04 02:25:19 +01:00
|
|
|
(only-in typed/racket/base
|
2020-12-12 00:46:50 +01:00
|
|
|
Any AnyValues Listof String Sexp Number Pair
|
|
|
|
List ->
|
2020-12-06 21:59:28 +01:00
|
|
|
cast)))
|
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.
|
|
|
|
|
2020-11-29 23:07:40 +01:00
|
|
|
|
|
|
|
@(define utils-evaluator
|
|
|
|
(parameterize ([sandbox-output 'string]
|
|
|
|
[sandbox-error-output 'string]
|
|
|
|
[sandbox-memory-limit 50])
|
2020-12-04 02:25:19 +01:00
|
|
|
(make-evaluator 'typed/racket/base #:requires '("utils.rkt"))))
|
|
|
|
|
|
|
|
@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.
|
|
|
|
|
|
|
|
}
|
2020-11-29 23:07:40 +01:00
|
|
|
|
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.
|
|
|
|
|
2020-12-04 02:25:19 +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.
|
|
|
|
|
|
|
|
@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)))
|
|
|
|
)]}
|
2020-11-29 21:41:00 +01:00
|
|
|
|
2020-12-04 02:25:19 +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].
|
|
|
|
|
|
|
|
@examples[#:eval utils-evaluator
|
|
|
|
(let ([ht (hash 'a 1 'b 1)])
|
|
|
|
(eval1-with ht '(+ b a 1)))
|
|
|
|
]}
|
2020-12-04 03:16:38 +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.
|
|
|
|
|
|
|
|
@examples[#:eval utils-evaluator
|
|
|
|
(let ([ht #hash((a . 1) (b . 2))])
|
|
|
|
(auto-hash-ref/explicit (ht 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
|
|
|
|
(let ([ht #hash((a . 1) (b . 2))])
|
|
|
|
(auto-hash-ref/: ht (+ :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.
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-11-29 21:41:00 +01:00
|
|
|
@section{Analysis of quoted expressions}
|
|
|
|
|
2020-12-04 04:03:22 +01:00
|
|
|
@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)))
|
|
|
|
]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
|
2020-12-04 23:43:53 +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.
|
|
|
|
|
|
|
|
@examples[#:eval utils-evaluator
|
|
|
|
(any->string '(a 1 (x y)))
|
|
|
|
]}
|
|
|
|
|
2020-12-05 00:22:49 +01:00
|
|
|
@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)))
|
|
|
|
]}
|
|
|
|
|
2020-12-05 00:33:14 +01:00
|
|
|
@defproc[(string->any [str String]) Any]{
|
|
|
|
|
|
|
|
Reads any value from string.
|
|
|
|
|
|
|
|
@examples[#:eval utils-evaluator
|
|
|
|
(string->any "(or b (not a))")
|
|
|
|
]}
|
|
|
|
|
2020-12-06 21:59:28 +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.
|
|
|
|
|
|
|
|
@examples[#:eval utils-evaluator
|
|
|
|
(map-sexp (λ (x) (add1 (cast x Number))) '(1 2 (4 10) 3))
|
|
|
|
]}
|
2020-12-06 22:28:45 +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].
|
|
|
|
|
|
|
|
@examples[#:eval utils-evaluator
|
|
|
|
(unorg "(#t \"#t\" \"#t \" '(1 2 \"#f\"))")
|
|
|
|
]}
|
|
|
|
|
2020-12-06 23:42:59 +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.
|
|
|
|
|
|
|
|
@examples[#:eval utils-evaluator
|
|
|
|
(unstringify-pairs '(("a" . 1) ("b" . "(and a (not b))")))
|
|
|
|
]}
|
2020-12-06 22:28:45 +01:00
|
|
|
|
2020-12-12 23:24:46 +01:00
|
|
|
@defproc[(compose-n [proc (-> a a)] ...) (-> a a)]{
|
|
|
|
|
|
|
|
Compose an arbitrary number of functions of type @racket[(-> a a)].
|
|
|
|
|
|
|
|
@examples[#:eval utils-evaluator
|
|
|
|
((compose-n add1 add1 add1) 3)
|
|
|
|
]}
|
|
|
|
|
|
|
|
@defproc*[([(compose-3 [proc1 (-> c d)] [proc2 (-> b c)] [proc3 (-> a b)]) (-> a d)]
|
|
|
|
[(compose-4 [proc1 (-> d e)] [proc2 (-> c d)] [proc3 (-> b c)] [proc4 (-> a b)]) (-> a e)])]{
|
|
|
|
|
|
|
|
@racket[compose-i] composes @racket[i] functions. The rightmost function is
|
|
|
|
applied first.
|
|
|
|
|
|
|
|
@examples[#:eval utils-evaluator
|
|
|
|
(define (s->n [x : String]) (cast (string->number x) Number))
|
|
|
|
(define fancy-add1 (compose-3 print add1 s->n))
|
|
|
|
fancy-add1
|
|
|
|
(fancy-add1 "1")
|
|
|
|
]}
|
2020-11-29 21:41:00 +01:00
|
|
|
@section{Additional graph utilities}
|
|
|
|
|
|
|
|
@section{Pretty printing}
|
|
|
|
|
|
|
|
@section{Additional list and hash map utilities}
|
|
|
|
|
|
|
|
@section{Functions and procedures}
|
|
|
|
|
|
|
|
@section{Randomness}
|
|
|
|
|
|
|
|
@section{Additional stream utilities}
|
|
|
|
|
|
|
|
@section{Boolean operations}
|