jwebsite/code/eval-tricks.md

418 lines
9.0 KiB
Markdown
Raw Normal View History

2020-06-14 04:15:14 +00:00
<!--
reviewed: 22/12/2019
-->
@def hascode=true
# Tricks with code evaluation
\blurb{Franklin's recursive nature coupled with code evaluation allows for neat and useful tricks.}
\lineskip
\toc
The basic idea is to exploit the fact that the output of a Julia code block evaluated by Franklin can be re-processed as Franklin Markdow when using the `\textoutput` command; this offers a wide range of possibilities best shown through a few examples (more or less in increasing degree of sophistication).
## Generating a table
### Preview
```julia:table
#hideall
names = (:Taimur, :Catherine, :Maria, :Arvind, :Jose, :Minjie)
numbers = (1525, 5134, 4214, 9019, 8918, 5757)
println("@@simple-table")
println("Name | Number")
println(":--- | :---")
println.("$name | $number" for (name, number) in zip(names, numbers))
println("@@")
raw"""
~~~
<style>
.simple-table tr {
padding:0;
line-height:1em;
}
</style>
~~~
""" |> println
```
\textoutput{table}
### Code
That can be obtained with:
`````plaintext
```julia:table
#hideall
names = (:Taimur, :Catherine, :Maria, :Arvind, :Jose, :Minjie)
numbers = (1525, 5134, 4214, 9019, 8918, 5757)
println("Name | Number")
println(":--- | :---")
println.("$name | $number" for (name, number) in zip(names, numbers))
```
\textoutput{table}
`````
The code block will be executed and not shown (`#hideall`) generating a table line by line.
In practice, the code generates the markdown
```markdown
Name | Number
:--- | :---
Bob | 1525
...
Minjie | 5757
```
which is captured and reprocessed by the `\textoutput` command.
This can be used effectively when combined with reading data files etc. and of course you could do further CSS styling of the table.
## Colourful circles
The trick can be used to generate SVG code too.
### Preview
\newcommand{\circle}[1]{~~~<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 4 4"><circle cx="2" cy="2" r="1.5" fill="#1"/></svg>~~~}
```julia:circles
#hideall
colors=(:pink, :lightpink, :hotpink, :deeppink, :mediumvioletred, :palevioletred, :coral, :tomato, :orangered, :darkorange, :orange, :gold, :yellow)
print("@@ccols ")
print.("\\circle{$c}" for c in colors)
println("@@")
```
\textoutput{circles}
### Code
That can obtained with (see detailed explanations further below)
```html
\newcommand{\circle}[1]{
~~~
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 4 4">
<circle cx="2" cy="2" r="1.5" fill="#1"/></svg>
~~~
}
```
and
`````plaintext
```julia:circles
#hideall
colors=(:pink, :lightpink, :hotpink, :deeppink,
:mediumvioletred, :palevioletred, :coral,
:tomato, :orangered, :darkorange, :orange,
:gold, :yellow)
print("@@ccols ")
print.("\\circle{$c}" for c in colors)
println("@@")
```
\textoutput{circles}
`````
The first part defines a command `\circle` which takes one argument for the fill colour and inserts SVG code for a circle with that colour.
The second part is a Julia code block which will be evaluated but not displayed on the page (since there is a `#hideall`).
The code loops over the each colour `c` and prints `\circle{c}` so that the code block effectively generates:
```plaintext
@@ccols \circle{pink}...\circle{yellow}@@
```
this output is then captured and reprocessed with the `\textoutput{snippet}` command.
The last thing to do is to style the `colors` div appropriately:
```css
.ccols {
margin-top:1.5em;
margin-bottom:1.5em;
margin-left:auto;
margin-right:auto;
width: 60%;
text-align: center;}
.ccols svg {
width:30px;}
```
## Team cards
You may want to have a page with responsive team cards for instance where every card would follow the same layout but the content would be different.
There are multiple ways you can do this with Franklin and a simple one below (adapted from [this tutorial](https://www.w3schools.com/howto/howto_css_team.asp)).
The advantage of doing something like this is that it can help separate the content from the layout making both arguably easier more maintainable.
### Preview
\newcommand{\card}[5]{
@@card
![#1](/assets/img/team/!#2.jpg)
@@container
~~~
<h2>#1</h2>
~~~
@@title #3 @@
@@vitae #4 @@
@@email #5 @@
~~~
<p><button class="button">Contact</button></p>
~~~
@@
@@
}
```julia:teamcards
#hideall
team = [
(name="Jane Doe", pic="beth", title="CEO & Founder", vitae="Phasellus eget enim eu lectus faucibus vestibulum", email="example@example.com"),
(name="Mike Ross", pic="rick", title="Art Director", vitae="Phasellus eget enim eu lectus faucibus vestibulum", email="example@example.com"),
(name="John Doe", pic="meseeks", title="Designer", vitae="Phasellus eget enim eu lectus faucibus vestibulum", email="example@example.com")
]
"@@cards @@row" |> println
for person in team
"""
@@column
\\card{$(person.name)}{$(person.pic)}{$(person.title)}{$(person.vitae)}{$(person.email)}
@@
""" |> println
end
println("@@ @@") # end of cards + row
raw"""
~~~
<style>
.column {
float:left;
width:30%;
margin-bottom:16px;
padding:0 8px;
}
@media (max-width:62rem) {
.column {
width:45%;
display:block;
}
}
@media (max-width:30rem){
.column {
width:95%;
display:block;
}
}
.card{
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
}
.card img {
padding-left:0;
width: 100%;
}
.container {
padding: 0 16px;
}
.container::after, .row::after{
content:"";
clear:both;
display:table;
}
.title {
color:grey;
}
.vitae {
margin-top:0.5em;
}
.email {
font-family:courier;
margin-top:0.5em;
margin-bottom:0.5em;
}
.button{
border:none;
outline:0;
display:inline-block;
padding:8px;
color:white;
background-color:#000;
text-align:center;
cursor:pointer;
width:100%;
}
.button:hover{
background-color:#555;
}
</style>
~~~
""" |> println
```
\textoutput{teamcards}
### Code
In order to do this you could first define a `\card` command:
```html
\newcommand{\card}[5]{
@@card
![#1](/assets/img/team/!#2.jpg)
@@container
~~~
<h2>#1</h2>
~~~
@@title #3 @@
@@vitae #4 @@
@@email #5 @@
~~~
<p><button class="button">Contact</button></p>
~~~
@@
@@
}
```
And then use it in a loop that goes over each person:
`````plaintext
```julia:teamcards
#hideall
team = [
(name="Jane Doe", pic="beth", title="CEO & Founder", vitae="Phasellus eget enim eu lectus faucibus vestibulum", email="example@example.com"),
(name="Mike Ross", pic="rick", title="Art Director", vitae="Phasellus eget enim eu lectus faucibus vestibulum", email="example@example.com"),
(name="John Doe", pic="meseeks", title="Designer", vitae="Phasellus eget enim eu lectus faucibus vestibulum", email="example@example.com")
]
"@@cards @@row" |> println
for person in team
"""
@@column
\\card{$(person.name)}{$(person.pic)}{$(person.title)}{$(person.vitae)}{$(person.email)}
@@
""" |> println
end
println("@@ @@") # end of cards + row
```
\textoutput{teamcards}
`````
That's about it! though of course a bit of CSS styling is needed such as:
```css
.column {
float:left;
width:30%;
margin-bottom:16px;
padding:0 8px; }
@media (max-width:62rem) {
.column {
width:45%;
display:block; }
}
@media (max-width:30rem){
.column {
width:95%;
display:block;}
}
.card { box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); }
.card img {
padding-left:0;
width: 100%; }
.container { padding: 0 16px; }
.container::after, .row::after{
content: "";
clear: both;
display: table; }
.title { color: grey; }
.vitae { margin-top: 0.5em; }
.email {
font-family: courier;
margin-top: 0.5em;
margin-bottom: 0.5em; }
.button{
border: none;
outline: 0;
display: inline-block;
padding: 8px;
color: white;
background-color: #000;
text-align: center;
cursor: pointer;
width: 100%; }
.button:hover{ background-color: #555; }
```
## Python code blocks
Using [PyCall.jl](https://github.com/JuliaPy/PyCall.jl) you can evaluate Python code in Julia, and so you can do that in Franklin too.
The code below could definitely be improved and generalised but the point here is to show how things can work together.
You could adapt this to work with something like [RCall.jl](https://github.com/JuliaInterop/RCall.jl) as well.
\newcommand{\pycode}[2]{
```julia:!#1
#hideall
using PyCall
lines = replace("""!#2""", r"(^|\n)([^\n]+)\n?$" => s"\1res = \2")
py"""
$$lines
"""
println(py"res")
```
```python
#2
```
\codeoutput{!#1}
}
\pycode{py1}{
import numpy as np
np.random.seed(2)
x = np.random.randn(5)
r = np.linalg.norm(x) / len(x)
np.round(r, 2)
}
### Code
We first define a `\pycode` command that takes lines of python code, adds a `res = ` before the last line, runs the lines and finally show `res`.
It also inputs the lines of code in a fenced block.
`````plaintext
\newcommand{\pycode}[2]{
```julia:!#1
#hideall
using PyCall
lines = replace("""!#2""", r"(^|\n)([^\n]+)\n?$" => s"\1res = \2")
py"""
$$lines
"""
println(py"res")
```
```python
#2
```
\codeoutput{!#1}
}
`````
calling the command is straightforward:
`````
\pycode{py1}{
import numpy as np
np.random.seed(2)
x = np.random.randn(5)
r = np.linalg.norm(x) / len(x)
np.round(r, 2)
}
`````