miller/doc/cookbook2.html

841 lines
33 KiB
HTML

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<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>
<!-- ================================================================ -->
<script type="text/javascript">
function toggle_div(div) {
if (div != null) {
if (div.id.startsWith("section_toggle_")) {
var state = div.style.display;
if (state == "block") {
div.style.display = "none";
} else {
div.style.display = "block";
}
}
}
}
function expand_div(div) {
if (div != null) {
if (div.id.startsWith("section_toggle_")) {
div.style.display = "block";
}
}
}
function collapse_div(div) {
if (div != null) {
if (div.id.startsWith("section_toggle_")) {
div.style.display = "none";
}
}
}
function toggle_by_name(divName) {
toggle_div(document.getElementById(divName));
}
function expand_by_name(divName) {
expand_div(document.getElementById(divName));
}
function collapse_by_name(divName) {
collapse_div(document.getElementById(divName));
}
function expand_all() {
var divs = document.getElementsByTagName("div");
for(var i = 0; i < divs.length; i++) {
expand_div(divs[i]);
}
}
function collapse_all() {
var divs = document.getElementsByTagName("div");
for(var i = 0; i < divs.length; i++){
collapse_div(divs[i]);
}
}
</script>
<!--
The background image is from a screenshot of a Google search for "data analysis
tools", lightened and sepia-toned. Over this was placed a Mac Terminal app with
very light-grey font and translucent background, in which a few statistical
Miller commands were run with pretty-print-tabular output format.
<body background="pix/sepia-overlay.jpg">
-->
<body bgcolor="#ffffff">
<!-- ================================================================ -->
<table width="100%">
<tr>
<!-- navbar -->
<td width="15%">
<!--
<img src="pix/mlr.jpg" />
<img style="border-width:1px; color:black;" src="pix/mlr.jpg" />
-->
<div class="pokinav">
<center><titleinbody>Miller</titleinbody></center>
<!-- PAGE LIST GENERATED FROM template.html BY poki -->
<br/><b>Overview:</b>
<br/>&bull;&nbsp;<a href="index.html">About Miller</a>
<br/>&bull;&nbsp;<a href="10-min.html">Miller in 10 minutes</a>
<br/>&bull;&nbsp;<a href="file-formats.html">File formats</a>
<br/>&bull;&nbsp;<a href="feature-comparison.html">Miller features in the context of the Unix toolkit</a>
<br/>&bull;&nbsp;<a href="record-heterogeneity.html">Record-heterogeneity</a>
<br/>&bull;&nbsp;<a href="internationalization.html">Internationalization</a>
<br/><b>Using Miller:</b>
<br/>&bull;&nbsp;<a href="faq.html">FAQ</a>
<br/>&bull;&nbsp;<a href="data-sharing.html">Sharing data with other languages</a>
<br/>&bull;&nbsp;<a href="cookbook.html">Cookbook part 1</a>
<br/>&bull;&nbsp;<a href="cookbook2.html"><b>Cookbook part 2</b></a>
<br/>&bull;&nbsp;<a href="cookbook3.html">Cookbook part 3</a>
<br/>&bull;&nbsp;<a href="data-examples.html">Data-diving examples</a>
<br/>&bull;&nbsp;<a href="manpage.html">Manpage</a>
<br/>&bull;&nbsp;<a href="reference.html">Reference</a>
<br/>&bull;&nbsp;<a href="reference-verbs.html">Reference: Verbs</a>
<br/>&bull;&nbsp;<a href="reference-dsl.html">Reference: DSL</a>
<br/>&bull;&nbsp;<a href="release-docs.html">Documents by release</a>
<br/>&bull;&nbsp;<a href="build.html">Installation, portability, dependencies, and testing</a>
<br/><b>Background:</b>
<br/>&bull;&nbsp;<a href="why.html">Why?</a>
<br/>&bull;&nbsp;<a href="whyc.html">Why C?</a>
<br/>&bull;&nbsp;<a href="etymology.html">Why call it Miller?</a>
<br/>&bull;&nbsp;<a href="originality.html">How original is Miller?</a>
<br/>&bull;&nbsp;<a href="performance.html">Performance</a>
<br/><b>Repository:</b>
<br/>&bull;&nbsp;<a href="to-do.html">Things to do</a>
<br/>&bull;&nbsp;<a href="contact.html">Contact information</a>
<br/>&bull;&nbsp;<a href="https://github.com/johnkerl/miller">GitHub repo</a>
<br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/>
<br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/>
<br/> <br/> <br/> <br/> <br/> <br/>
</div>
</td>
<!-- page body -->
<td>
<!--
This is a visually gorgeous feature (here & in the CSS): it allows for
independent scroll of the nav and body panels. In particular the nav
stays on-screen as you scroll the body.
However, two problems:
(1) In Firefox & Chrome both I get janky end-of-body scrolls: there is
more content but I can't scroll down to it unless I repeatedly retry the
scrolldown. Which is weird.
(2) Worse, only the first page renders in PDF (again, Firefox & Chrome).
For now I'm disabling this separate-scroll feature. A frontender, I am
not ... maybe someday I'll find a config which gets *all* the features
I want; for now, it's a tradeoff.
-->
<!-- Implementation details: one bit is right here:
div style="overflow-y:scroll;height:1500px"
and the other bit is in css/poki-callbacks.css:
.pokinav {
display: inline-block;
background: #e8d9bc;
border: 1;
box-shadow: 0px 0px 3px 3px #C9C9C9;
margin: 10px;
padding-top: 10px;
padding-bottom: 10px;
padding-left: 10px;
padding-right: 10px;
overflow-y: scroll; < - - - - - - here
height: 1500px;
}
-->
<div>
<center> <titleinbody> Cookbook part 2 </titleinbody> </center>
<p/>
<!-- BODY COPIED FROM content-for-cookbook2.html BY poki -->
<p/>
<center>
<boldmaroon>Random things, and some math</boldmaroon>
</center>
<div class="pokitoc">
<center><b>Contents:</b></center>
&bull;&nbsp;<a href="#Randomly_selecting_words_from_a_list">Randomly selecting words from a list</a><br/>
&bull;&nbsp;<a href="#Randomly_generating_jabberwocky_words">Randomly generating jabberwocky words</a><br/>
&bull;&nbsp;<a href="#Program_timing">Program timing</a><br/>
&bull;&nbsp;<a href="#Computing_interquartile_ranges">Computing interquartile ranges</a><br/>
&bull;&nbsp;<a href="#Computing_weighted_means">Computing weighted means</a><br/>
&bull;&nbsp;<a href="#Generating_random_numbers_from_various_distributions">Generating random numbers from various distributions</a><br/>
&bull;&nbsp;<a href="#Sieve_of_Eratosthenes">Sieve of Eratosthenes</a><br/>
&bull;&nbsp;<a href="#Mandelbrot-set_generator">Mandelbrot-set generator</a><br/>
</div>
<p/>
<p/>
<button style="font-weight:bold;color:maroon;border:0" onclick="expand_all();" href="javascript:;">Expand all sections</button>
<button style="font-weight:bold;color:maroon;border:0" onclick="collapse_all();" 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="toggle_by_name('section_toggle_generating_random_words');" href="javascript:;">Toggle section visibility</button>
<div id="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 '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="toggle_by_name('section_toggle_jabberwocky');" href="javascript:;">Toggle section visibility</button>
<div id="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 &mdash; 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="toggle_by_name('section_toggle_program_timing');" href="javascript:;">Toggle section visibility</button>
<div id="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&rsquo;s own
runtime. The <tt>delta</tt> function computes the difference between successive
timestamps.
<p/>
<div class="pokipanel">
<pre>
$ ruby -e '10000.times{|i|puts "i=#{i+1}"}' &gt; 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&gt;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="toggle_by_name('section_toggle_iqr');" href="javascript:;">Toggle section visibility</button>
<div id="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 <tt>p25</tt> 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="toggle_by_name('section_toggle_weighted_mean');" href="javascript:;">Toggle section visibility</button>
<div id="section_toggle_weighted_mean" style="display: block">
<p> This might be more elegantly implemented as an option within the <tt>stats1</tt> verb. Meanwhile, it&rsquo;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="toggle_by_name('section_toggle_generating_random_variables');" href="javascript:;">Toggle section visibility</button>
<div id="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 <tt>seqgen</tt> to produce 100,000 records <tt>i=0</tt>, <tt>i=1</tt>, etc.
<li/> Send those to a <tt>put</tt> 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="toggle_by_name('section_toggle_sieve');" href="javascript:;">Toggle section visibility</button>
<div id="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 <tt>begin</tt> and <tt>end</tt> statements; there is no file
input (so we use <tt>mlr -n</tt> 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 &lt;= @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 &lt;= @n; i += 1) {
for (int j = i+i; j &lt;= @n; j += i) {
@s[j] = false;
}
}
# Print survivors
for (int i = 0; i &lt;= @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="toggle_by_name('section_toggle_mand');" href="javascript:;">Toggle section visibility</button>
<div id="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&rsquo;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 &mdash; a test
case for the expressiveness of the language.
<p/> The (approximate) computation of points in the complex plane which are and
aren&rsquo;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 &gt;= 0; ii -= 1) {
num pi = @icorn + (ii/@iheight) * @side;
for (int ir = 0; ir &lt; @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 &lt; maxits; iti += 1) {
num mag = zr*zr + zi+zi;
if (mag &gt; 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>
</div>
</td>
</table>
</body>
</html>