dds/example/example.org
2020-02-22 20:34:24 +01:00

319 lines
8.9 KiB
Org Mode

#+TITLE: Examples of usage of =dds=
#+PROPERTY: header-args:racket :prologue "#lang racket\n(require graph (file \"~/Candies/prj/racket/dds/networks.rkt\") (file \"~/Candies/prj/racket/dds/utils.rkt\"))"
* Introduction
This document shows some examples of usage of the modules in =dds=
with Org-mode. It relies on [[https://github.com/hasu/emacs-ob-racket][emacs-ob-racket]].
The [[intro][following section]] describes how Org-mode can interact with
Racket, and how this interaction can be used for a fluid workflow
with =dds=. In particular, the code block =munch-table= is [[tabread][defined]]
in this section.
The subsequent sections show off some the functionalities of the
submodules of =dds=.
* Org-mode, Racket, and =dds= <<intro>>
** Importing a module from file
:PROPERTIES:
:header-args:racket: :prologue "#lang racket\n(require (file \"~/Candies/prj/racket/dds/networks.rkt\"))"
:END:
To require the modules from the files of =dds=, you can use the
following code (I only reset the prelude here because I set at the
top of this file):
#+BEGIN_SRC racket :results output :prologue ""
#lang racket
(require (file "~/Candies/prj/racket/dds/networks.rkt"))
(require (file "~/Candies/prj/racket/dds/utils.rkt"))
#+END_SRC
#+RESULTS:
Note that this code will not work with =:results value=. I think it
is because in this case the code is not really evaluated at top
level.
These initialisation lines can be put into the prologue of every
code block in a subtree by setting =:prologue= via
=:header-args:racket:= in the properties drawer. Check out the
properties drawer of this section for an example.
Alternatively, this property can be set via a =#+PROPERTY= line at
the top the file. For example, this file has such a line. Whenever
this property line changes, refresh the setup of the file by hitting
=C-c C-c= on the property line. This will update the prologue for
all racket code blocks.
Finally, you can also set =:prologue= (and other properties with
long values) in the following way:
#+HEADER: :prologue "#lang racket\n(require (file \"~/Candies/prj/racket/dds/networks.rkt\"))"
#+BEGIN_SRC racket :results output drawer
(st '((a . 1)))
#+END_SRC
#+RESULTS:
:RESULTS:
'#hash((a . 1))
:END:
** Output formats for results of evaluation of code blocks
[[https://orgmode.org/manual/Results-of-Evaluation.html#Results-of-Evaluation][This section]] of the Org manual describes various different formats
for presenting the results of code blocks. I find the following
three particularly useful as of [2020-02-22 Sat]: =output=, =list=,
and =table=.
The =output= result format is the simplest and the most natural
ones. It works as if the code block were inserted into a module
which would then be evaluated.
#+BEGIN_SRC racket :results output drawer
(println "This is the first line of output.")
(println (+ 1 2))
(println "This the third line of output.")
#+END_SRC
#+RESULTS:
:RESULTS:
"This is the first line of output."
3
"This the third line of output."
:END:
The =list= result format typesets the result of the last line in the
code block as a list:
#+BEGIN_SRC racket :results list
'(1 "hello" (and x y))
#+END_SRC
#+RESULTS:
- 1
- "hello"
- (and x y)
Note how nested lists are not recursively shown as nested Org-mode
lists.
For some reason, the =list= output format does not work with the
result drawer:
#+BEGIN_SRC racket :results list drawer
'(1 "hello" (and x y))
#+END_SRC
#+RESULTS:
:RESULTS:
- (1 "\"hello\"" (and x y))
:END:
Finally, the =table= result format typesets the output as a table:
#+BEGIN_SRC racket :results table drawer
'((a . #t) (b . #f))
#+END_SRC
#+RESULTS:
:RESULTS:
| a | #t |
| b | #f |
:END:
This is clearly very useful for printing states (and hash tables,
more generally):
#+BEGIN_SRC racket :results table drawer
(st '((a . 1) (b . #f) (c . "hello")))
#+END_SRC
#+RESULTS:
:RESULTS:
| a | 1 |
| b | #f |
| c | "hello" |
:END:
*** A note about printing update function forms
Automatic table typesetting may go in the way of readability for
hash tables whose values are lists, as the following example shows:
#+BEGIN_SRC racket :results table drawer
#hash((a . (and a b)) (b . (not b)))
#+END_SRC
#+RESULTS:
:RESULTS:
| a | and | a | b |
| b | not | b | |
:END:
To tackle this issue, [[../utils.rkt][=dds/utils=]] provides
=stringify-variable-mapping= (with the shortcut =sgfy=) which
converts all the values of a given variable mapping to strings:
#+BEGIN_SRC racket :results table drawer
(sgfy #hash((a . (and a b)) (b . (not b))))
#+END_SRC
#+RESULTS:
:RESULTS:
| a | "(and a b)" |
| b | "(not b)" |
:END:
** Reading Org-mode tables<<tabread>>
Org-mode allows supplying tables as arguments for code blocks.
#+NAME: test-table
| a | "(and a b)" |
| b | (or b (not a)) |
#+BEGIN_SRC elisp :var tab=test-table :results output drawer
tab
#+END_SRC
#+RESULTS:
:RESULTS:
:END:
((a (and a b)) (b (or b (not a))))
Unfortunately, the same trick does not work with Racket directly,
because Racket interprets the first elements in the parentheses as
function applications:
#+BEGIN_SRC racket :results output drawer :var tab=test-table
tab
#+END_SRC
#+RESULTS:
:RESULTS:
application: not a procedure;
expected a procedure that can be applied to arguments
given: "a"
arguments...:
"(and a b)"
context...:
"/tmp/babel-qkvrRR/org-babel-c4wuju.rkt": [running body]
temp37_0
for-loop
run-module-instance!125
perform-require!78
:END:
Fortunately, we can easily remedy this problem by creating a named
parameterised Elisp source block which will explicitly convert the
table to a string:
#+NAME: munch-table
#+BEGIN_SRC elisp :results output drawer :var tab=test-table
(prin1 tab)
#+END_SRC
#+RESULTS: munch-table
:RESULTS:
(("a" "(and a b)") ("b" "(or b (not a))"))
:END:
We can now correctly receive this table in a Racket source code
block by threading it through =munch-table=:
#+BEGIN_SRC racket :results output drawer :var tab=munch-table(tab=test-table)
(println tab)
#+END_SRC
#+RESULTS:
:RESULTS:
"((\"a\" \"(and a b)\") (\"b\" \"(or b (not a))\"))"
:END:
[[../utils.rkt][=dds/utils=]] has several functions for parsing such strings, and
notably =read-org-variable-mapping=, with the shortcut =unorg=:
#+BEGIN_SRC racket :results output drawer :var tab=munch-table(tab=test-table)
(unorg tab)
#+END_SRC
#+RESULTS:
:RESULTS:
'#hash((a . (and a b)) (b . (or b (not a))))
:END:
Of course, we can use =munch-table= to prepare any other table than
=test-table= for use with Racket:
#+NAME: another-test-table
| a | (not a) |
| b | (and a c) |
| c | (and a (not b)) |
#+BEGIN_SRC racket :results output drawer :var tab=munch-table(tab=another-test-table)
(unorg tab)
#+END_SRC
#+RESULTS:
:RESULTS:
'#hash((a . (not a)) (b . (and a c)) (c . (and a (not b))))
:END:
** Inline graph visualisation with Graphviz
Some functions in =dds= build graphs:
#+BEGIN_SRC racket :results output drawer :var bf=munch-table(another-test-table)
(build-interaction-graph (unorg bf))
#+END_SRC
#+RESULTS:
:RESULTS:
#<unweighted-graph>
:END:
The =graph= library allows building a Graphviz description of the
constructed graph. (Note that you have to install the =graph=
library by running =raco pkg install graph= and require it. The
long property line at the top of this file defining the prologue for
racket source code blocks takes care of requiring =graph=.)
#+NAME: igraph
#+BEGIN_SRC racket :results output drawer :var bf=munch-table(another-test-table)
(display (graphviz (build-interaction-graph (unorg bf))))
#+END_SRC
#+RESULTS: igraph
:RESULTS:
digraph G {
node0 [label="c"];
node1 [label="b"];
node2 [label="a"];
subgraph U {
edge [dir=none];
node0 -> node1;
node2 -> node2;
}
subgraph D {
node2 -> node0;
node2 -> node1;
}
}
:END:
You can have an inline drawing of this graph by calling the previous
code block (=igraph=) via a noweb reference in Graphviz/DOT source
block:
#+BEGIN_SRC dot :file dots/exampleBQNp7Z.svg :results raw drawer :cmd sfdp :noweb yes
<<igraph()>>
#+END_SRC
#+RESULTS:
:RESULTS:
[[file:dots/exampleBQNp7Z.svg]]
:END:
Note that the =graph= library draws self-loops as undirected edges.
It also draws double-sided edges as undirected edges (e.g., in the
preceding graph, b depends on c and c depends on b).
* Local Variables :noexport:
# Local Variables:
# eval: (auto-fill-mode)
# ispell-local-dictionary: "en"
# org-link-file-path-type: relative
# End: