Literate Programming in org (2/3)
Still diving into org babel..
Notes:
- This is a pretty deep dive into elisp. I could’ve used Python for the below, but as of now I don’t have all necessary packages on all my platforms and elisp is the most portable solution
- In order to facilitate evaluation, modify the variable
org-confirm-babel-evaluate
withC-h v
tonil
- Code blocks are an excellent way to learn programming
Very instructive example from stackoverflow:
#+name: mytable
| p | q |
|---+---|
| a | 5 |
| b | 2 |
| c | 3 |
#+begin_src emacs-lisp :var table=mytable :colnames yes
(let* ((total (apply '+ (mapcar 'car (mapcar 'cdr table)))) )
(append table `(hline ("" ,total))))
#+end_src
#+RESULTS:
| p | q |
|---+----|
| a | 5 |
| b | 2 |
| c | 3 |
|---+----|
| | 10 |
So from analyzing the above, we can see the syntax that org babel interprets as a table. So, we can build one with elisp as follows:
#+name: mytab
#+begin_src emacs-lisp :var table=mytable
'((5) (2) (4) (10) (6))
#+end_src
#+RESULTS: mytab
| 5 |
| 2 |
| 4 |
| 10 |
| 6 |
now we need to operate on these lists and find the sum and mean. These are vector operations and I will use calc.
Calc has a special syntax for vectors that was barely mentioned once in the whole documentation. This stackoverflow post mentioned that it uses a special “internal” syntax for vectors:
(vec 1 2 3)
Ok, so for the simple case, this how to calculate the sum and mean by calling calc from lisp:
;; finding the mean using calc
(setq v1 '(vec 10 20 30 40))
(calc-eval (calcFunc-vmean v1))
;; converting from a list
(setq d '(10 20 30 40))
;; to a vector
(setq v2 (cons 'vec d))
(calc-eval (calcFunc-vmean v2))
Now we can form code blocks and calculate the sum and the mean using elisp.
#+name:mysum
#+begin_src emacs-lisp :var tab=mytab
(apply '+ (mapcar 'car tab))
#+end_src
#+RESULTS:
: 27
#+name:mymean
#+begin_src emacs-lisp :var tab=mytab
(calc-eval (calcFunc-vmean (cons 'vec (mapcar 'car tab))))
#+end_src
#+RESULTS: mymean
: 5.4
Producing a table
Let’s calculate the sum and the mean for this data and put them in a table
#+name: newdata
| v |
|----|
| 45 |
| 45 |
| 52 |
| 57 |
| 43 |
| 65 |
| 25 |
code block
#+name: stats
#+begin_src emacs-lisp :var v=newdata :exports results :noweb yes
(setq u (mapcar 'car v))
(setq sum (apply '+ u))
(setq sumt (number-to-string sum))
(setq meant (calc-eval (calcFunc-vmean (cons 'vec u))))
(setq mean (format "%6.2f" (string-to-number meant)))
(append '((Sum Mean)) `(hline (,sum ,mean)))
#+end_src
result
#+RESULTS: stats
| Sum | Mean |
|-----+-------|
| 332 | 47.43 |
Mappig values
#+name: mapping
#+begin_src emacs-lisp :var v=newdata :exports results :noweb yes
(setq u (mapcar 'car v))
(setq doubled (mapcar '(lambda (d) (* d 2)) u))
(setq doubledl (mapcar 'list doubled))
(setq med (append '((v*2)) '(hline)))
(append med doubledl)
#+end_src
And the result
#+RESULTS: mapping
| v*2 |
|------|
| 90 |
| 90 |
| 1050 |
| 114 |
| 86 |
| 130 |
| 50 |