mirror of
https://github.com/johnkerl/miller.git
synced 2026-07-18 00:45:47 +00:00
721 lines
30 KiB
HTML
721 lines
30 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<!-- PAGE GENERATED FROM template.html and content-for-cookbook2.html BY poki. -->
|
|
<!-- PLEASE MAKE CHANGES THERE AND THEN RE-RUN poki. -->
|
|
<head>
|
|
<meta http-equiv="Content-type" content="text/html;charset=UTF-8"/>
|
|
<meta name="description" content="Miller documentation"/>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <!-- mobile-friendly -->
|
|
<meta name="keywords"
|
|
content="John Kerl, Kerl, Miller, miller, mlr, OLAP, data analysis software, regression, correlation, variance, data tools, " />
|
|
|
|
<title> Cookbook part 2 </title>
|
|
<link rel="stylesheet" type="text/css" href="css/miller.css"/>
|
|
<link rel="stylesheet" type="text/css" href="css/poki-callbacks.css"/>
|
|
</head>
|
|
|
|
<!-- ================================================================ -->
|
|
<script type="text/javascript">
|
|
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
|
|
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
|
</script>
|
|
<script type="text/javascript">
|
|
try {
|
|
var pageTracker = _gat._getTracker("UA-15651652-1");
|
|
pageTracker._trackPageview();
|
|
} catch(err) {}
|
|
</script>
|
|
<!-- ================================================================ -->
|
|
|
|
<body bgcolor="#ffffff">
|
|
|
|
<!-- ================================================================ -->
|
|
|
|
<!-- navbar -->
|
|
<div class="pokinav">
|
|
<center><titleinbody>Miller</titleinbody></center>
|
|
|
|
<!-- NAVBAR GENERATED FROM template.html BY poki -->
|
|
<br/>
|
|
<a class="poki-navbar-element" href="index.html">Overview</a>
|
|
|
|
<a class="poki-navbar-element" href="faq.html"><b>Using</b></a>
|
|
|
|
<a class="poki-navbar-element" href="reference.html">Reference</a>
|
|
|
|
<a class="poki-navbar-element" href="why.html">Background</a>
|
|
|
|
<a class="poki-navbar-element" href="contact.html">Repository</a>
|
|
|
|
<br/>
|
|
<br/><a href="faq.html">FAQ</a>
|
|
<br/><a href="customization.html">Customization: .mlrrc</a>
|
|
<br/><a href="data-sharing.html">Mixing with other languages</a>
|
|
<br/><a href="cookbook.html">Cookbook part 1</a>
|
|
<br/><a href="cookbook2.html"><b>Cookbook part 2</b></a>
|
|
<br/><a href="cookbook3.html">Cookbook part 3</a>
|
|
<br/><a href="data-examples.html">Data-diving examples</a>
|
|
</div>
|
|
|
|
<!-- page body -->
|
|
<p/>
|
|
|
|
<!-- BODY COPIED FROM content-for-cookbook2.html BY poki -->
|
|
<div class="pokitoc">
|
|
<center><titleinbody>Cookbook part 2: Random things, and some math</titleinbody></center>
|
|
• <a href="#Randomly_selecting_words_from_a_list">Randomly selecting words from a list</a><br/>
|
|
• <a href="#Randomly_generating_jabberwocky_words">Randomly generating jabberwocky words</a><br/>
|
|
• <a href="#Program_timing">Program timing</a><br/>
|
|
• <a href="#Computing_interquartile_ranges">Computing interquartile ranges</a><br/>
|
|
• <a href="#Computing_weighted_means">Computing weighted means</a><br/>
|
|
• <a href="#Generating_random_numbers_from_various_distributions">Generating random numbers from various distributions</a><br/>
|
|
• <a href="#Sieve_of_Eratosthenes">Sieve of Eratosthenes</a><br/>
|
|
• <a href="#Mandelbrot-set_generator">Mandelbrot-set generator</a><br/>
|
|
</div>
|
|
<p/>
|
|
|
|
<p/>
|
|
<button style="font-weight:bold;color:maroon;border:0" onclick="bodyToggler.expandAll();" href="javascript:;">Expand all sections</button>
|
|
<button style="font-weight:bold;color:maroon;border:0" onclick="bodyToggler.collapseAll();" href="javascript:;">Collapse all sections</button>
|
|
|
|
<!-- ================================================================ -->
|
|
<a id="Randomly_selecting_words_from_a_list"/><h1>Randomly selecting words from a list</h1>
|
|
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="bodyToggler.toggle('body_section_toggle_generating_random_words');" href="javascript:;">Toggle section visibility</button>
|
|
<div id="body_section_toggle_generating_random_words" style="display: block">
|
|
|
|
<p/> Given this <a href="data/english-words.txt">word list</a>, first take a
|
|
look to see what the first few lines look like:
|
|
|
|
<p/>
|
|
<div class="pokipanel"> <pre>
|
|
$ head data/english-words.txt
|
|
a
|
|
aa
|
|
aal
|
|
aalii
|
|
aam
|
|
aardvark
|
|
aardwolf
|
|
aba
|
|
abac
|
|
abaca
|
|
</pre> </div>
|
|
|
|
<p/> Then the following will randomly sample ten words with four to eight characters in them:
|
|
<p/>
|
|
<div class="pokipanel"> <pre>
|
|
$ mlr --from data/english-words.txt --nidx filter -S 'n=strlen($1);4<=n&&n<=8' then sample -k 10
|
|
thionine
|
|
birchman
|
|
mildewy
|
|
avigate
|
|
addedly
|
|
abaze
|
|
askant
|
|
aiming
|
|
insulant
|
|
coinmate
|
|
</pre> </div>
|
|
|
|
</div>
|
|
<!-- ================================================================ -->
|
|
<a id="Randomly_generating_jabberwocky_words"/><h1>Randomly generating jabberwocky words</h1>
|
|
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="bodyToggler.toggle('body_section_toggle_jabberwocky');" href="javascript:;">Toggle section visibility</button>
|
|
<div id="body_section_toggle_jabberwocky" style="display: block">
|
|
|
|
<p/> These are simple <i>n</i>-grams as described
|
|
<a href="http://johnkerl.org/randspell/randspell-slides-ts.pdf">here</a>. Some common functions are
|
|
<a href="ngrams/ngfuncs.mlr.txt">here</a>.
|
|
Then here are scripts for
|
|
<a href="ngrams/ng1.mlr.txt">1-grams</a>,
|
|
<a href="ngrams/ng2.mlr.txt">2-grams</a>,
|
|
<a href="ngrams/ng3.mlr.txt">3-grams</a>,
|
|
<a href="ngrams/ng4.mlr.txt">4-grams</a>, and
|
|
<a href="ngrams/ng5.mlr.txt">5-grams</a>.
|
|
|
|
<p/>The idea is that words from the input file are consumed, then taken apart and pasted back together
|
|
in ways which imitate the letter-to-letter transitions found in the word list — giving us automatically
|
|
generated words in the same vein as <i>bromance</i> and <i>spork</i>:
|
|
<p/>
|
|
<div class="pokipanel"> <pre>
|
|
$ mlr --nidx --from ./ngrams/gsl-2000.txt put -q -f ./ngrams/ngfuncs.mlr -f ./ngrams/ng5.mlr
|
|
beard
|
|
plastinguish
|
|
politicially
|
|
noise
|
|
loan
|
|
country
|
|
controductionary
|
|
suppery
|
|
lose
|
|
lessors
|
|
dollar
|
|
judge
|
|
rottendence
|
|
lessenger
|
|
diffendant
|
|
suggestional
|
|
</pre> </div>
|
|
|
|
|
|
</div>
|
|
<!-- ================================================================ -->
|
|
<a id="Program_timing"/><h1>Program timing</h1>
|
|
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="bodyToggler.toggle('body_section_toggle_program_timing');" href="javascript:;">Toggle section visibility</button>
|
|
<div id="body_section_toggle_program_timing" style="display: block">
|
|
|
|
This admittedly artificial example demonstrates using Miller time and stats
|
|
functions to introspectively acquire some information about Miller’s own
|
|
runtime. The <code>delta</code> function computes the difference between successive
|
|
timestamps.
|
|
|
|
<p/>
|
|
<div class="pokipanel">
|
|
<pre>
|
|
$ ruby -e '10000.times{|i|puts "i=#{i+1}"}' > lines.txt
|
|
|
|
$ head -n 5 lines.txt
|
|
i=1
|
|
i=2
|
|
i=3
|
|
i=4
|
|
i=5
|
|
|
|
mlr --ofmt '%.9le' --opprint put '$t=systime()' then step -a delta -f t lines.txt | head -n 7
|
|
i t t_delta
|
|
1 1430603027.018016 1.430603027e+09
|
|
2 1430603027.018043 2.694129944e-05
|
|
3 1430603027.018048 5.006790161e-06
|
|
4 1430603027.018052 4.053115845e-06
|
|
5 1430603027.018055 2.861022949e-06
|
|
6 1430603027.018058 3.099441528e-06
|
|
|
|
mlr --ofmt '%.9le' --oxtab \
|
|
put '$t=systime()' then \
|
|
step -a delta -f t then \
|
|
filter '$i>1' then \
|
|
stats1 -a min,mean,max -f t_delta \
|
|
lines.txt
|
|
t_delta_min 2.861022949e-06
|
|
t_delta_mean 4.077508505e-06
|
|
t_delta_max 5.388259888e-05
|
|
</pre>
|
|
</div>
|
|
<p/>
|
|
|
|
</div>
|
|
<!-- ================================================================ -->
|
|
<a id="Computing_interquartile_ranges"/><h1>Computing interquartile ranges</h1>
|
|
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="bodyToggler.toggle('body_section_toggle_iqr');" href="javascript:;">Toggle section visibility</button>
|
|
<div id="body_section_toggle_iqr" style="display: block">
|
|
|
|
<p> For one or more specified field names, simply compute p25 and p75, then write the IQR as the difference of p75 and p25:
|
|
|
|
<p/>
|
|
<div class="pokipanel">
|
|
<pre>
|
|
$ mlr --oxtab stats1 -f x -a p25,p75 \
|
|
then put '$x_iqr = $x_p75 - $x_p25' \
|
|
data/medium
|
|
x_p25 0.246670
|
|
x_p75 0.748186
|
|
x_iqr 0.501516
|
|
</pre>
|
|
</div>
|
|
<p/>
|
|
|
|
<p> For wildcarded field names, first compute p25 and p75, then loop over field names with <code>p25</code> in them:
|
|
|
|
<p/>
|
|
<div class="pokipanel">
|
|
<pre>
|
|
$ mlr --oxtab stats1 --fr '[i-z]' -a p25,p75 \
|
|
then put 'for (k,v in $*) {
|
|
if (k =~ "(.*)_p25") {
|
|
$["\1_iqr"] = $["\1_p75"] - $["\1_p25"]
|
|
}
|
|
}' \
|
|
data/medium
|
|
i_p25 2501
|
|
i_p75 7501
|
|
x_p25 0.246670
|
|
x_p75 0.748186
|
|
y_p25 0.252137
|
|
y_p75 0.764003
|
|
i_iqr 5000
|
|
x_iqr 0.501516
|
|
y_iqr 0.511866
|
|
</pre>
|
|
</div>
|
|
<p/>
|
|
|
|
</div>
|
|
<!-- ================================================================ -->
|
|
<a id="Computing_weighted_means"/><h1>Computing weighted means</h1>
|
|
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="bodyToggler.toggle('body_section_toggle_weighted_mean');" href="javascript:;">Toggle section visibility</button>
|
|
<div id="body_section_toggle_weighted_mean" style="display: block">
|
|
|
|
<p> This might be more elegantly implemented as an option within the <code>stats1</code> verb. Meanwhile, it’s
|
|
expressible within the DSL:
|
|
|
|
<p/>
|
|
<div class="pokipanel">
|
|
<pre>
|
|
$ mlr --from data/medium put -q '
|
|
# Using the y field for weighting in this example
|
|
weight = $y;
|
|
|
|
# Using the a field for weighted aggregation in this example
|
|
@sumwx[$a] += weight * $i;
|
|
@sumw[$a] += weight;
|
|
|
|
@sumx[$a] += $i;
|
|
@sumn[$a] += 1;
|
|
|
|
end {
|
|
map wmean = {};
|
|
map mean = {};
|
|
for (a in @sumwx) {
|
|
wmean[a] = @sumwx[a] / @sumw[a]
|
|
}
|
|
for (a in @sumx) {
|
|
mean[a] = @sumx[a] / @sumn[a]
|
|
}
|
|
#emit wmean, "a";
|
|
#emit mean, "a";
|
|
emit (wmean, mean), "a";
|
|
}'
|
|
a=pan,wmean=4979.563722,mean=5028.259010
|
|
a=eks,wmean=4890.381593,mean=4956.290076
|
|
a=wye,wmean=4946.987746,mean=4920.001017
|
|
a=zee,wmean=5164.719685,mean=5123.092330
|
|
a=hat,wmean=4925.533162,mean=4967.743946
|
|
</pre>
|
|
</div>
|
|
<p/>
|
|
|
|
</div>
|
|
<!-- ================================================================ -->
|
|
<a id="Generating_random_numbers_from_various_distributions"/><h1>Generating random numbers from various distributions</h1>
|
|
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="bodyToggler.toggle('body_section_toggle_generating_random_variables');" href="javascript:;">Toggle section visibility</button>
|
|
<div id="body_section_toggle_generating_random_variables" style="display: block">
|
|
|
|
<p/>Here we can chain together a few simple building blocks:
|
|
|
|
<p/>
|
|
<div class="pokipanel">
|
|
<pre>
|
|
$ cat expo-sample.sh
|
|
# Generate 100,000 pairs of independent and identically distributed
|
|
# exponentially distributed random variables with the same rate parameter
|
|
# (namely, 2.5). Then compute histograms of one of them, along with
|
|
# histograms for their sum and their product.
|
|
#
|
|
# See also https://en.wikipedia.org/wiki/Exponential_distribution
|
|
#
|
|
# Here I'm using a specified random-number seed so this example always
|
|
# produces the same output for this web document: in everyday practice we
|
|
# wouldn't do that.
|
|
|
|
mlr -n \
|
|
--seed 0.25 \
|
|
--opprint \
|
|
seqgen --stop 100000 \
|
|
then put '
|
|
# https://en.wikipedia.org/wiki/Inverse_transform_sampling
|
|
func expo_sample(lambda) {
|
|
return -log(1-urand())/lambda
|
|
}
|
|
$u = expo_sample(2.5);
|
|
$v = expo_sample(2.5);
|
|
$s = $u + $v;
|
|
$p = $u * $v;
|
|
' \
|
|
then histogram -f u,s,p --lo 0 --hi 2 --nbins 50 \
|
|
then bar -f u_count,s_count,p_count --auto -w 20
|
|
</pre>
|
|
</div>
|
|
<p/>
|
|
|
|
<p/>Namely:
|
|
|
|
<ul>
|
|
<li/> Set the Miller random-number seed so this webdoc looks the same every time I regenerate it.
|
|
<li/> Use pretty-printed tabular output.
|
|
<li/> Use pretty-printed tabular output.
|
|
<li/> Use <code>seqgen</code> to produce 100,000 records <code>i=0</code>, <code>i=1</code>, etc.
|
|
<li/> Send those to a <code>put</code> step which defines an inverse-transform-sampling function and
|
|
calls it twice, then computes the sum and product of samples.
|
|
<li/> Send those to a histogram, and from there to a bar-plotter. This is just for visualization; you
|
|
could just as well output CSV and send that off to your own plotting tool, etc.
|
|
</ul>
|
|
|
|
The output is as follows:
|
|
|
|
<p/>
|
|
<div class="pokipanel">
|
|
<pre>
|
|
$ sh expo-sample.sh
|
|
bin_lo bin_hi u_count s_count p_count
|
|
0.000000 0.040000 [78]*******************#[9497] [353]#...................[3732] [20]*******************#[39755]
|
|
0.040000 0.080000 [78]******************..[9497] [353]*****...............[3732] [20]*******.............[39755]
|
|
0.080000 0.120000 [78]****************....[9497] [353]*********...........[3732] [20]****................[39755]
|
|
0.120000 0.160000 [78]**************......[9497] [353]************........[3732] [20]***.................[39755]
|
|
0.160000 0.200000 [78]*************.......[9497] [353]**************......[3732] [20]**..................[39755]
|
|
0.200000 0.240000 [78]************........[9497] [353]****************....[3732] [20]*...................[39755]
|
|
0.240000 0.280000 [78]**********..........[9497] [353]******************..[3732] [20]*...................[39755]
|
|
0.280000 0.320000 [78]**********..........[9497] [353]******************..[3732] [20]*...................[39755]
|
|
0.320000 0.360000 [78]*********...........[9497] [353]*******************.[3732] [20]#...................[39755]
|
|
0.360000 0.400000 [78]********............[9497] [353]*******************.[3732] [20]#...................[39755]
|
|
0.400000 0.440000 [78]*******.............[9497] [353]*******************#[3732] [20]#...................[39755]
|
|
0.440000 0.480000 [78]******..............[9497] [353]******************..[3732] [20]#...................[39755]
|
|
0.480000 0.520000 [78]*****...............[9497] [353]******************..[3732] [20]#...................[39755]
|
|
0.520000 0.560000 [78]*****...............[9497] [353]******************..[3732] [20]#...................[39755]
|
|
0.560000 0.600000 [78]****................[9497] [353]*****************...[3732] [20]#...................[39755]
|
|
0.600000 0.640000 [78]****................[9497] [353]*****************...[3732] [20]#...................[39755]
|
|
0.640000 0.680000 [78]****................[9497] [353]****************....[3732] [20]#...................[39755]
|
|
0.680000 0.720000 [78]***.................[9497] [353]****************....[3732] [20]#...................[39755]
|
|
0.720000 0.760000 [78]***.................[9497] [353]**************......[3732] [20]#...................[39755]
|
|
0.760000 0.800000 [78]**..................[9497] [353]**************......[3732] [20]#...................[39755]
|
|
0.800000 0.840000 [78]**..................[9497] [353]*************.......[3732] [20]#...................[39755]
|
|
0.840000 0.880000 [78]**..................[9497] [353]************........[3732] [20]#...................[39755]
|
|
0.880000 0.920000 [78]**..................[9497] [353]***********.........[3732] [20]#...................[39755]
|
|
0.920000 0.960000 [78]*...................[9497] [353]***********.........[3732] [20]#...................[39755]
|
|
0.960000 1.000000 [78]*...................[9497] [353]**********..........[3732] [20]#...................[39755]
|
|
1.000000 1.040000 [78]*...................[9497] [353]*********...........[3732] [20]#...................[39755]
|
|
1.040000 1.080000 [78]*...................[9497] [353]*********...........[3732] [20]#...................[39755]
|
|
1.080000 1.120000 [78]*...................[9497] [353]********............[3732] [20]#...................[39755]
|
|
1.120000 1.160000 [78]*...................[9497] [353]********............[3732] [20]#...................[39755]
|
|
1.160000 1.200000 [78]#...................[9497] [353]*******.............[3732] [20]#...................[39755]
|
|
1.200000 1.240000 [78]#...................[9497] [353]******..............[3732] [20]#...................[39755]
|
|
1.240000 1.280000 [78]#...................[9497] [353]*****...............[3732] [20]#...................[39755]
|
|
1.280000 1.320000 [78]#...................[9497] [353]*****...............[3732] [20]#...................[39755]
|
|
1.320000 1.360000 [78]#...................[9497] [353]*****...............[3732] [20]#...................[39755]
|
|
1.360000 1.400000 [78]#...................[9497] [353]****................[3732] [20]#...................[39755]
|
|
1.400000 1.440000 [78]#...................[9497] [353]****................[3732] [20]#...................[39755]
|
|
1.440000 1.480000 [78]#...................[9497] [353]***.................[3732] [20]#...................[39755]
|
|
1.480000 1.520000 [78]#...................[9497] [353]***.................[3732] [20]#...................[39755]
|
|
1.520000 1.560000 [78]#...................[9497] [353]***.................[3732] [20]#...................[39755]
|
|
1.560000 1.600000 [78]#...................[9497] [353]**..................[3732] [20]#...................[39755]
|
|
1.600000 1.640000 [78]#...................[9497] [353]**..................[3732] [20]#...................[39755]
|
|
1.640000 1.680000 [78]#...................[9497] [353]*...................[3732] [20]#...................[39755]
|
|
1.680000 1.720000 [78]#...................[9497] [353]*...................[3732] [20]#...................[39755]
|
|
1.720000 1.760000 [78]#...................[9497] [353]*...................[3732] [20]#...................[39755]
|
|
1.760000 1.800000 [78]#...................[9497] [353]*...................[3732] [20]#...................[39755]
|
|
1.800000 1.840000 [78]#...................[9497] [353]#...................[3732] [20]#...................[39755]
|
|
1.840000 1.880000 [78]#...................[9497] [353]#...................[3732] [20]#...................[39755]
|
|
1.880000 1.920000 [78]#...................[9497] [353]#...................[3732] [20]#...................[39755]
|
|
1.920000 1.960000 [78]#...................[9497] [353]#...................[3732] [20]#...................[39755]
|
|
1.960000 2.000000 [78]#...................[9497] [353]#...................[3732] [20]#...................[39755]
|
|
</pre>
|
|
</div>
|
|
<p/>
|
|
|
|
</div>
|
|
<!-- ================================================================ -->
|
|
<a id="Sieve_of_Eratosthenes"/><h1>Sieve of Eratosthenes</h1>
|
|
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="bodyToggler.toggle('body_section_toggle_sieve');" href="javascript:;">Toggle section visibility</button>
|
|
<div id="body_section_toggle_sieve" style="display: block">
|
|
|
|
<p/> The <a href="http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes">Sieve_of_Eratosthenes</a>
|
|
is a standard introductory programming topic. The idea is to find all primes up
|
|
to some <i>N</i> by making a list of the numbers 1 to <i>N</i>, then striking
|
|
out all multiples of 2 except 2 itself, all multiples of 3 except 3 itself, all
|
|
multiples of 4 except 4 itself, and so on. Whatever survives that without
|
|
getting marked is a prime. This is easy enough in Miller. Notice that here all
|
|
the work is in <code>begin</code> and <code>end</code> statements; there is no file
|
|
input (so we use <code>mlr -n</code> to keep Miller from waiting for input data).
|
|
|
|
<p/>
|
|
<div class="pokipanel">
|
|
<pre>
|
|
$ cat programs/sieve.mlr
|
|
# ================================================================
|
|
# Sieve of Eratosthenes: simple example of Miller DSL as programming language.
|
|
# ================================================================
|
|
|
|
# Put this in a begin-block so we can do either
|
|
# mlr -n put -q -f name-of-this-file.mlr
|
|
# or
|
|
# mlr -n put -q -f name-of-this-file.mlr -e '@n = 200'
|
|
# i.e. 100 is the default upper limit, and another can be specified using -e.
|
|
begin {
|
|
@n = 100;
|
|
}
|
|
|
|
end {
|
|
for (int i = 0; i <= @n; i += 1) {
|
|
@s[i] = true;
|
|
}
|
|
@s[0] = false; # 0 is neither prime nor composite
|
|
@s[1] = false; # 1 is neither prime nor composite
|
|
# Strike out multiples
|
|
for (int i = 2; i <= @n; i += 1) {
|
|
for (int j = i+i; j <= @n; j += i) {
|
|
@s[j] = false;
|
|
}
|
|
}
|
|
# Print survivors
|
|
for (int i = 0; i <= @n; i += 1) {
|
|
if (@s[i]) {
|
|
print i;
|
|
}
|
|
}
|
|
}
|
|
</pre>
|
|
</div>
|
|
<p/>
|
|
<p/>
|
|
<div class="pokipanel">
|
|
<pre>
|
|
$ mlr -n put -f programs/sieve.mlr
|
|
2
|
|
3
|
|
5
|
|
7
|
|
11
|
|
13
|
|
17
|
|
19
|
|
23
|
|
29
|
|
31
|
|
37
|
|
41
|
|
43
|
|
47
|
|
53
|
|
59
|
|
61
|
|
67
|
|
71
|
|
73
|
|
79
|
|
83
|
|
89
|
|
97
|
|
</pre>
|
|
</div>
|
|
<p/>
|
|
|
|
</div>
|
|
<!-- ================================================================ -->
|
|
<a id="Mandelbrot-set_generator"/><h1>Mandelbrot-set generator</h1>
|
|
<button style="font-weight:bold;color:maroon;border:0" padding=0 onclick="bodyToggler.toggle('body_section_toggle_mand');" href="javascript:;">Toggle section visibility</button>
|
|
<div id="body_section_toggle_mand" style="display: block">
|
|
|
|
<p/> The <a href="http://en.wikipedia.org/wiki/Mandelbrot_set">Mandelbrot
|
|
set</a> is also easily expressed. This isn’t an important case of
|
|
data-processing in the vein for which Miller was designed, but it is an
|
|
example of Miller as a general-purpose programming language — a test
|
|
case for the expressiveness of the language.
|
|
|
|
<p/> The (approximate) computation of points in the complex plane which are and
|
|
aren’t members is just a few lines of complex arithmetic (see the
|
|
Wikipedia article); how to render them is another task. Using graphics
|
|
libraries you can create PNG or JPEG files, but another fun way to do this is
|
|
by printing various characters to the screen:
|
|
|
|
<p/>
|
|
<div class="pokipanel">
|
|
<pre>
|
|
$ cat programs/mand.mlr
|
|
# Mandelbrot set generator: simple example of Miller DSL as programming language.
|
|
begin {
|
|
# Set defaults
|
|
@rcorn = -2.0;
|
|
@icorn = -2.0;
|
|
@side = 4.0;
|
|
@iheight = 50;
|
|
@iwidth = 100;
|
|
@maxits = 100;
|
|
@levelstep = 5;
|
|
@chars = "@X*o-."; # Palette of characters to print to the screen.
|
|
@verbose = false;
|
|
@do_julia = false;
|
|
@jr = 0.0; # Real part of Julia point, if any
|
|
@ji = 0.0; # Imaginary part of Julia point, if any
|
|
}
|
|
|
|
# Here, we can override defaults from an input file (if any). In Miller's
|
|
# put/filter DSL, absent-null right-hand sides result in no assignment so we
|
|
# can simply put @rcorn = $rcorn: if there is a field in the input like
|
|
# 'rcorn = -1.847' we'll read and use it, else we'll keep the default.
|
|
@rcorn = $rcorn;
|
|
@icorn = $icorn;
|
|
@side = $side;
|
|
@iheight = $iheight;
|
|
@iwidth = $iwidth;
|
|
@maxits = $maxits;
|
|
@levelstep = $levelstep;
|
|
@chars = $chars;
|
|
@verbose = $verbose;
|
|
@do_julia = $do_julia;
|
|
@jr = $jr;
|
|
@ji = $ji;
|
|
|
|
end {
|
|
if (@verbose) {
|
|
print "RCORN = ".@rcorn;
|
|
print "ICORN = ".@icorn;
|
|
print "SIDE = ".@side;
|
|
print "IHEIGHT = ".@iheight;
|
|
print "IWIDTH = ".@iwidth;
|
|
print "MAXITS = ".@maxits;
|
|
print "LEVELSTEP = ".@levelstep;
|
|
print "CHARS = ".@chars;
|
|
}
|
|
|
|
# Iterate over a matrix of rows and columns, printing one character for each cell.
|
|
for (int ii = @iheight-1; ii >= 0; ii -= 1) {
|
|
num pi = @icorn + (ii/@iheight) * @side;
|
|
for (int ir = 0; ir < @iwidth; ir += 1) {
|
|
num pr = @rcorn + (ir/@iwidth) * @side;
|
|
printn get_point_plot(pr, pi, @maxits, @do_julia, @jr, @ji);
|
|
}
|
|
print;
|
|
}
|
|
}
|
|
|
|
# This is a function to approximate membership in the Mandelbrot set (or Julia
|
|
# set for a given Julia point if do_julia == true) for a given point in the
|
|
# complex plane.
|
|
func get_point_plot(pr, pi, maxits, do_julia, jr, ji) {
|
|
num zr = 0.0;
|
|
num zi = 0.0;
|
|
num cr = 0.0;
|
|
num ci = 0.0;
|
|
|
|
if (!do_julia) {
|
|
zr = 0.0;
|
|
zi = 0.0;
|
|
cr = pr;
|
|
ci = pi;
|
|
} else {
|
|
zr = pr;
|
|
zi = pi;
|
|
cr = jr;
|
|
ci = ji;
|
|
}
|
|
|
|
int iti = 0;
|
|
bool escaped = false;
|
|
num zt = 0;
|
|
for (iti = 0; iti < maxits; iti += 1) {
|
|
num mag = zr*zr + zi+zi;
|
|
if (mag > 4.0) {
|
|
escaped = true;
|
|
break;
|
|
}
|
|
# z := z^2 + c
|
|
zt = zr*zr - zi*zi + cr;
|
|
zi = 2*zr*zi + ci;
|
|
zr = zt;
|
|
}
|
|
if (!escaped) {
|
|
return ".";
|
|
} else {
|
|
# The // operator is Miller's (pythonic) integer-division operator
|
|
int level = (iti // @levelstep) % strlen(@chars);
|
|
return substr(@chars, level, level);
|
|
}
|
|
}
|
|
</pre>
|
|
</div>
|
|
<p/>
|
|
|
|
<p/> At standard resolution this makes a nice little ASCII plot:
|
|
|
|
<p/>
|
|
<div class="pokipanel">
|
|
<pre>
|
|
$ mlr -n put -f ./programs/mand.mlr
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXX.XXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXooXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXX**o..*XXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXX*-....-oXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXX@XXXXXXXXXX*......o*XXXXXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXXX**oo*-.-........oo.XXXXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXXXXXX....................X..o-XXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@XXXXXXXXXXXXXXX*oo......................oXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@XXX*XXXXXXXXXXXX**o........................*X*X@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@XXXXXXooo***o*.*XX**X..........................o-XX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@XXXXXXXX*-.......-***.............................oXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@XXXXXXXX*@..........Xo............................*XX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@XXXX@XXXXXXXX*o@oX...........@...........................oXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
.........................................................o*XXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@XXXXXXXXX*-.oX...........@...........................oXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@XXXXXXXXXX**@..........*o............................*XXXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@XXXXXXXXXXXXX-........***.............................oXXXXXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@XXXXXXXXXXXXoo****o*.XX***@..........................o-XXXXXXXXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@XXXXX*XXXX*XXXXXXX**-........................***XXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@XXXXXXXXXXXXX*o*.....................@o*XXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXXXXX*....................*..o-XX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXX*ooo*-.o........oo.X*XXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXX**@.....*XXXXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXX*o....-o*XXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXXXo*o..*XXXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXXXXXX*o*XXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXXXXX@XXXXXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXXXXXX@@XXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@XXXXX@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
</pre>
|
|
</div>
|
|
<p/>
|
|
|
|
<p/> But using a very small font size (as small as my Mac will let me go), and by choosing the
|
|
coordinates to zoom in on a particular part of the complex plane, we can get a nice little
|
|
picture:
|
|
|
|
<div class=pokipanel> <pre>
|
|
#!/bin/bash
|
|
# Get the number of rows and columns from the terminal window dimensions
|
|
iheight=$(stty size | mlr --nidx --fs space cut -f 1)
|
|
iwidth=$(stty size | mlr --nidx --fs space cut -f 2)
|
|
echo "rcorn=-1.755350,icorn=+0.014230,side=0.000020,maxits=10000,iheight=$iheight,iwidth=$iwidth" \
|
|
| mlr put -f programs/mand.mlr
|
|
</pre></div>
|
|
|
|
<center>
|
|
<img src="pix/mand.png"/>
|
|
</center>
|
|
|
|
</div>
|
|
|
|
<!-- ================================================================ -->
|
|
<script type="text/javascript" src="js/miller-doc-toggler.js"></script>
|
|
<!-- wtf -->
|
|
<script type="text/javascript">
|
|
// Put this at the bottom of the page since its constructor scans the
|
|
// document's div tags to find the toggleables.
|
|
const bodyToggler = new MillerDocToggler(
|
|
"body_section_toggle_",
|
|
'maroon',
|
|
'maroon',
|
|
);
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|