Re: Factor
https://re.factorcode.org/index.html
Recent content on Re: FactorHugo -- gohugo.ioen-usWed, 04 Feb 2026 08:00:00 -0700Standard Deviation
https://re.factorcode.org/2026/02/standard-deviation.html
Wed, 04 Feb 2026 08:00:00 -0700https://re.factorcode.org/2026/02/standard-deviation.html<p><a href="https://en.wikipedia.org/wiki/Standard_deviation">Standard deviation</a> is
“<em>a measure of the amount of variation of the values of a variable about its
mean.</em>”. It’s a useful measure from statistics, and
<a href="https://docs.factorcode.org/content/word-std,math.statistics.html">std</a> in
the <a href="https://docs.factorcode.org/content/article-math.statistics.html">math.statistics
vocabulary</a>
included with <a href="https://factorcode.org">Factor</a>.</p>
<p>I bumped into this – um, are they still called tweets? – today:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Word with the lowest standard deviation of letter position in the alphabet, for each length <a href="https://t.co/caHYDDpyBx">pic.twitter.com/caHYDDpyBx</a></p>— Adam Aaronson (@aaaronson) <a href="https://twitter.com/aaaronson/status/2019068865066529138?ref_src=twsrc%5Etfw">February 4, 2026</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Of course, I wondered how that looks for the <code>/usr/share/dict/words</code> on my computer:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/usr/share/dict/words"</span> utf8 file-lines
</span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>] collect-by
</span></span><span class="line"><span class="cl"> [ [ std ] minimum-by ] <span class="nb">assoc-map
</span></span></span><span class="line"><span class="cl"> sort-keys <span class="nb">values
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>std <span class="s">"%s: %s\n"</span> printf ] <span class="nb">each
</span></span></span><span class="line"><span class="cl">A: <span class="m">0.0
</span></span></span><span class="line"><span class="cl">aa: <span class="m">0.0
</span></span></span><span class="line"><span class="cl">aba: <span class="m">0.5773502691896257
</span></span></span><span class="line"><span class="cl">baba: <span class="m">0.5773502691896257
</span></span></span><span class="line"><span class="cl">abaca: <span class="m">0.8944271909999159
</span></span></span><span class="line"><span class="cl">bacaba: <span class="m">0.816496580927726
</span></span></span><span class="line"><span class="cl">deedeed: <span class="m">0.5345224838248488
</span></span></span><span class="line"><span class="cl">poroporo: <span class="m">1.3093073414159542
</span></span></span><span class="line"><span class="cl">susurrous: <span class="m">1.9364916731037085
</span></span></span><span class="line"><span class="cl">beefheaded: <span class="m">1.9578900207451218
</span></span></span><span class="line"><span class="cl">cabbagehead: <span class="m">2.46429041972071
</span></span></span><span class="line"><span class="cl">fiddledeedee: <span class="m">2.424621182533032
</span></span></span><span class="line"><span class="cl">promonopolist: <span class="m">2.911075226502911
</span></span></span><span class="line"><span class="cl">monogonoporous: <span class="m">3.1830595551871363
</span></span></span><span class="line"><span class="cl">prophototropism: <span class="m">3.5023801430836525
</span></span></span><span class="line"><span class="cl">philophilosophos: <span class="m">3.855731664245668
</span></span></span><span class="line"><span class="cl">sulphophosphorous: <span class="m">4.227013964824759
</span></span></span><span class="line"><span class="cl">chemicoengineering: <span class="m">4.556472090169843
</span></span></span><span class="line"><span class="cl">plutonometamorphism: <span class="m">5.220293285659733
</span></span></span><span class="line"><span class="cl">encephalomeningocele: <span class="m">4.871776937249466
</span></span></span><span class="line"><span class="cl">philosophicoreligious: <span class="m">5.014740177478695
</span></span></span><span class="line"><span class="cl">philosophicohistorical: <span class="m">5.485320828241917
</span></span></span><span class="line"><span class="cl">philosophicotheological: <span class="m">5.2090678006509625
</span></span></span><span class="line"><span class="cl">scientificophilosophical: <span class="m">5.538894097749744
</span></span></span><span class="line"><span class="cl">antidisestablishmentarianism: <span class="m">6.7081053397123425
</span></span></span></code></pre></div><p>Interesting, both similar <em>and</em> different. Well, it’s pretty close and also
pretty obvious we are using slightly different dictionaries. I’m not sure
what <em>deedeed</em> or <em>poroporo</em> mean and they aren’t in the <a href="https://scrabble.merriam.com/">SCRABBLE Players
Dictionary</a>.</p>
<p>Anyway, fun!</p>
PBRT
https://re.factorcode.org/2026/02/pbrt.html
Tue, 03 Feb 2026 08:00:00 -0700https://re.factorcode.org/2026/02/pbrt.html<p><a href="https://pbrt.org">PBRT</a> is an impressive photorealistic rendering system:</p>
<blockquote>
<p>From movies to video games, computer-rendered images are pervasive today.
<strong>Physically Based Rendering</strong> introduces the concepts and theory of
photorealistic rendering hand in hand with the source code for a
sophisticated renderer.</p>
</blockquote>
<p>The fourth edition of their book is now available <a href="https://www.amazon.com/Physically-Based-Rendering-fourth-Implementation/dp/0262048027?keywords=physically+based+rendering+4th+edition&qid=1671730412&sprefix=physically+based%2Caps%2C145&sr=8-1&linkCode=ll1&tag=pharr-20&linkId=81a816d90f0c7e872617f1f930a51fd6&language=en_US&ref_=as_li_ss_tl">on
Amazon</a>
as well as freely available <a href="https://pbr-book.org/">online</a>.</p>
<p>
<img src="https://re.factorcode.org/images/2026-02-03-bookcover-4ed.jpg" alt="" width="384" height="495" />
</p>
<p>I thought it would be fun to explore the <a href="https://pbrt.org/fileformat-v4">PBRT v4 file
format</a> using
<a href="https://factorcode.org">Factor</a>.</p>
<p>Here’s a short example <code>pbrt</code> file from their website:</p>
<pre tabindex="0"><code class="language-pbrt" data-lang="pbrt">LookAt 3 4 1.5 # eye
.5 .5 0 # look at point
0 0 1 # up vector
Camera "perspective" "float fov" 45
Sampler "halton" "integer pixelsamples" 128
Integrator "volpath"
Film "rgb" "string filename" "simple.png"
"integer xresolution" [400] "integer yresolution" [400]
WorldBegin
# uniform blue-ish illumination from all directions
LightSource "infinite" "rgb L" [ .4 .45 .5 ]
# approximate the sun
LightSource "distant" "point3 from" [ -30 40 100 ]
"blackbody L" 3000 "float scale" 1.5
AttributeBegin
Material "dielectric"
Shape "sphere" "float radius" 1
AttributeEnd
AttributeBegin
Texture "checks" "spectrum" "checkerboard"
"float uscale" [16] "float vscale" [16]
"rgb tex1" [.1 .1 .1] "rgb tex2" [.8 .8 .8]
Material "diffuse" "texture reflectance" "checks"
Translate 0 0 -1
Shape "bilinearmesh"
"point3 P" [ -20 -20 0 20 -20 0 -20 20 0 20 20 0 ]
"point2 uv" [ 0 0 1 0 1 1 0 1 ]
AttributeEnd
</code></pre><p>And this is what it might look like:</p>
<p>
<img src="https://re.factorcode.org/images/2026-02-03-simple-v4.png" alt="" width="200" height="200" />
</p>
<p>Using our new <a href="https://github.com/factor/factor/blob/master/extra/pbrt/pbrt.factor">pbrt
vocabulary</a>,
we can convert that text into a set of
<a href="https://docs.factorcode.org/content/article-tuples.html">tuples</a> that we
could do computations on, or potentially look into rendering or processing.
And, of course, it also supports round-tripping back and forth from text to
tuples.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> T{ pbrt-look-at
</span></span><span class="line"><span class="cl"> { eye-x <span class="m">3 </span>}
</span></span><span class="line"><span class="cl"> { eye-y <span class="m">4 </span>}
</span></span><span class="line"><span class="cl"> { eye-z <span class="m">1.5 </span>}
</span></span><span class="line"><span class="cl"> { look-x <span class="m">0.5 </span>}
</span></span><span class="line"><span class="cl"> { look-y <span class="m">0.5 </span>}
</span></span><span class="line"><span class="cl"> { look-z <span class="m">0 </span>}
</span></span><span class="line"><span class="cl"> { up-x <span class="m">0 </span>}
</span></span><span class="line"><span class="cl"> { up-y <span class="m">0 </span>}
</span></span><span class="line"><span class="cl"> { up-z <span class="m">1 </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-camera
</span></span><span class="line"><span class="cl"> { type <span class="s">"perspective"</span> }
</span></span><span class="line"><span class="cl"> { params
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"float"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"fov"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="m">45 </span>} }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-sampler
</span></span><span class="line"><span class="cl"> { type <span class="s">"halton"</span> }
</span></span><span class="line"><span class="cl"> { params
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"integer"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"pixelsamples"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="m">128 </span>} }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-integrator { type <span class="s">"volpath"</span> } { params { } } }
</span></span><span class="line"><span class="cl"> T{ pbrt-film
</span></span><span class="line"><span class="cl"> { type <span class="s">"rgb"</span> }
</span></span><span class="line"><span class="cl"> { params
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"string"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"filename"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="s">"simple.png"</span> } }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"integer"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"xresolution"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="m">400 </span>} }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"integer"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"yresolution"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="m">400 </span>} }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-world-begin }
</span></span><span class="line"><span class="cl"> T{ pbrt-light-source
</span></span><span class="line"><span class="cl"> { type <span class="s">"infinite"</span> }
</span></span><span class="line"><span class="cl"> { params
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"rgb"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"L"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="m">0.4 0.45 0.5 </span>} }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-light-source
</span></span><span class="line"><span class="cl"> { type <span class="s">"distant"</span> }
</span></span><span class="line"><span class="cl"> { params
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"point3"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"from"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="m">-30 40 100 </span>} }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"blackbody"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"L"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="m">3000 </span>} }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"float"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"scale"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="m">1.5 </span>} }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-attribute-begin }
</span></span><span class="line"><span class="cl"> T{ pbrt-material { type <span class="s">"dielectric"</span> } { params { } } }
</span></span><span class="line"><span class="cl"> T{ pbrt-shape
</span></span><span class="line"><span class="cl"> { type <span class="s">"sphere"</span> }
</span></span><span class="line"><span class="cl"> { params
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"float"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"radius"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="m">1 </span>} }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-attribute-end }
</span></span><span class="line"><span class="cl"> T{ pbrt-attribute-begin }
</span></span><span class="line"><span class="cl"> T{ pbrt-texture
</span></span><span class="line"><span class="cl"> { name <span class="s">"checks"</span> }
</span></span><span class="line"><span class="cl"> { value-type <span class="s">"spectrum"</span> }
</span></span><span class="line"><span class="cl"> { class <span class="s">"checkerboard"</span> }
</span></span><span class="line"><span class="cl"> { params
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"float"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"uscale"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="m">16 </span>} }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"float"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"vscale"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="m">16 </span>} }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"rgb"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"tex1"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="m">0.1 0.1 0.1 </span>} }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"rgb"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"tex2"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="m">0.8 0.8 0.8 </span>} }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-material
</span></span><span class="line"><span class="cl"> { type <span class="s">"diffuse"</span> }
</span></span><span class="line"><span class="cl"> { params
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"texture"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"reflectance"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="s">"checks"</span> } }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-translate { x <span class="m">0 </span>} { y <span class="m">0 </span>} { z <span class="m">-1 </span>} }
</span></span><span class="line"><span class="cl"> T{ pbrt-shape
</span></span><span class="line"><span class="cl"> { type <span class="s">"bilinearmesh"</span> }
</span></span><span class="line"><span class="cl"> { params
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"point3"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"P"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values
</span></span></span><span class="line"><span class="cl"> { <span class="m">-20 -20 0 20 -20 0 -20 20 0 20 20 0 </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-param
</span></span><span class="line"><span class="cl"> { type <span class="s">"point2"</span> }
</span></span><span class="line"><span class="cl"> { name <span class="s">"uv"</span> }
</span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="m">0 0 1 0 1 1 0 1 </span>} }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ pbrt-attribute-end }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>This is available now in the <a href="https://github.com/factor/factor">development version of
Factor</a>!</p>
Migrating to GTK3
https://re.factorcode.org/2025/12/migrating-to-gtk3.html
Wed, 17 Dec 2025 08:00:00 -0700https://re.factorcode.org/2025/12/migrating-to-gtk3.html<p><a href="https://factorcode.org">Factor</a> has a native
<a href="https://docs.factorcode.org/content/word-ui-backend,ui.backend.html">ui-backend</a>
that allows us to render our <a href="https://docs.factorcode.org/content/article-ui.html">UI
framework</a> using OpenGL
on top of platform-specific APIs for our primary targets of Linux, macOS,
and Windows.</p>
<p>On Linux, for a long time that has meant using the
<a href="https://www.gtk.org/">GTK2</a> library, which has also meant using
<a href="https://en.wikipedia.org/wiki/X_Window_System">X11</a> and an old library
called <code>libgtkglext</code> which provides a way to use OpenGL within GTK
windows. Well, Linux has moved on and is now pushing
<a href="https://wayland.freedesktop.org/">Wayland</a> as the “<em>replacement for the X11
window system protocol and architecture with the aim to be easier to
develop, extend, and maintain</em>”. Most modern Linux distributions have moved
to GTK3 or GTK4 and abstraction libraries like
<a href="https://github.com/anholt/libepoxy">libepoxy</a> for working with OpenGL and
others for supporting both X11 and Wayland renderers.</p>
<p>I was reminded of this after our recent <a href="https://re.factorcode.org/2025/12/factor-0-101-now-available.html">Factor
0.101</a> release when someone <a href="https://github.com/factor/factor/issues/3131#issuecomment-3638609485">asked
the
question</a>:</p>
<blockquote>
<p>Does that message mean that Factor still relies on GTK2? IIRC it was EOL:ed around 2020.</p>
</blockquote>
<p>Well, this is embarassing – yeah it sure does! Or rather – yes it sure did.</p>
<p>I got motivated to look into what it would take to support GTK3 or GTK4. We
had a pull request that was working through adding <a href="https://github.com/factor/factor/pull/3100">support for
GTK4</a>. After merging that, and
modifying it to also provide GTK3 support, I re-discovered that our OpenGL
rendering was generally using OpenGL 1.x pipelines and that would not work
in a GTK3+ world.</p>
<p>So, after adding OpenGL 3.x support for most of the things our user
interface needs, and <a href="https://docs.gtk.org/gtk3/migrating-2to3.html">migrating from GTK 2.x to
GTK3</a>, we now have
experimental nightly builds using the GTK3 backend:</p>
<p>
<img src="https://re.factorcode.org/images/2025-12-17-gtk3.png" alt="" width="845" height="866" />
</p>
<p>You can revert to the older GTK2 backend by applying this diff and then
performing a fresh bootstrap:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"><span class="gh">diff --git a/basis/bootstrap/ui/ui.factor b/basis/bootstrap/ui/ui.factor
</span></span></span><span class="line"><span class="cl"><span class="gh">index 2974e530f9..416704ce29 100644
</span></span></span><span class="line"><span class="cl"><span class="gd">--- a/basis/bootstrap/ui/ui.factor
</span></span></span><span class="line"><span class="cl"><span class="gi">+++ b/basis/bootstrap/ui/ui.factor
</span></span></span><span class="line"><span class="cl"><span class="gu">@@ -12,6 +12,6 @@ IN: bootstrap.ui
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ os macos? ] [ "ui.backend.cocoa" ] }
</span></span><span class="line"><span class="cl"> { [ os windows? ] [ "ui.backend.windows" ] }
</span></span><span class="line"><span class="cl"><span class="gd">- { [ os unix? ] [ "ui.backend.gtk3" ] }
</span></span></span><span class="line"><span class="cl"><span class="gi">+ { [ os unix? ] [ "ui.backend.gtk2" ] }
</span></span></span><span class="line"><span class="cl"> } cond
</span></span><span class="line"><span class="cl"> ] if* require
</span></span><span class="line"><span class="cl"><span class="gh">diff --git a/basis/opengl/gl/extensions/extensions.factor b/basis/opengl/gl/extensions/extensions.factor
</span></span></span><span class="line"><span class="cl"><span class="gh">index 2d408e93bb..51394eeb4a 100644
</span></span></span><span class="line"><span class="cl"><span class="gd">--- a/basis/opengl/gl/extensions/extensions.factor
</span></span></span><span class="line"><span class="cl"><span class="gi">+++ b/basis/opengl/gl/extensions/extensions.factor
</span></span></span><span class="line"><span class="cl"><span class="gu">@@ -7,7 +7,7 @@ ERROR: unknown-gl-platform ;
</span></span></span><span class="line"><span class="cl"> << {
</span></span><span class="line"><span class="cl"> { [ os windows? ] [ "opengl.gl.windows" ] }
</span></span><span class="line"><span class="cl"> { [ os macos? ] [ "opengl.gl.macos" ] }
</span></span><span class="line"><span class="cl"><span class="gd">- { [ os unix? ] [ "opengl.gl.gtk3" ] }
</span></span></span><span class="line"><span class="cl"><span class="gi">+ { [ os unix? ] [ "opengl.gl.gtk2" ] }
</span></span></span><span class="line"><span class="cl"> [ unknown-gl-platform ]
</span></span><span class="line"><span class="cl"> } cond use-vocab >>
</span></span></code></pre></div><p>It seems like the newer OpenGL 3.x functions might introduce some lag which
is visible when scrolling on some installations, perhaps by not caching
certain things that were cached in the OpenGL 1.x code paths. There will
need to be some improvements before we are ready to release it, but it is
plenty usable as-is.</p>
<p>I also migrated our macOS backend to use the OpenGL 3.x functions as well to
allow us to more broadly test and improve these new rendering paths.</p>
<p>This is available in the latest <a href="https://github.com/factor/factor">development version</a>.</p>
DNS LOC Records
https://re.factorcode.org/2025/12/dns-loc-records.html
Wed, 10 Dec 2025 08:00:00 -0700https://re.factorcode.org/2025/12/dns-loc-records.html<p><a href="https://en.wikipedia.org/wiki/Domain_Name_System">DNS</a> is the <em>Domain Name
System</em> and is the backbone of the internet:</p>
<blockquote>
<p>Most prominently, it translates readily memorized domain names to the
numerical <a href="https://en.wikipedia.org/wiki/IP_address">IP addresses</a> needed
for locating and identifying computer services and devices with the
underlying <a href="https://en.wikipedia.org/wiki/Network_protocol">network protocols</a>.
The Domain Name System has been an essential component of the
functionality of the Internet since 1985.</p>
</blockquote>
<p>It is also an oft-cited reason for service outages, with a funny decade-old
<a href="https://www.reddit.com/r/sysadmin/comments/4oj7pv/comment/d4czk91/">r/sysadmin
meme</a>:</p>
<p>
<img src="https://re.factorcode.org/images/2025-12-10-its-always-dns.png" alt="" width="619" height="292" />
</p>
<p><a href="https://factorcode.org">Factor</a> has a <a href="https://docs.factorcode.org/content/vocab-dns.html">DNS
vocabulary</a> that
supports querying and parsing responses from
<a href="https://en.wikipedia.org/wiki/Name_server">nameservers</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">tools.dns</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"google.com"</span> host
</span></span><span class="line"><span class="cl">google.com has address 142.250.142.113
</span></span><span class="line"><span class="cl">google.com has address 142.250.142.138
</span></span><span class="line"><span class="cl">google.com has address 142.250.142.100
</span></span><span class="line"><span class="cl">google.com has address 142.250.142.101
</span></span><span class="line"><span class="cl">google.com has address 142.250.142.102
</span></span><span class="line"><span class="cl">google.com has address 142.250.142.139
</span></span><span class="line"><span class="cl">google.com has IPv6 address 2607:f8b0:4023:1c01:0:0:0:8b
</span></span><span class="line"><span class="cl">google.com has IPv6 address 2607:f8b0:4023:1c01:0:0:0:8a
</span></span><span class="line"><span class="cl">google.com has IPv6 address 2607:f8b0:4023:1c01:0:0:0:64
</span></span><span class="line"><span class="cl">google.com has IPv6 address 2607:f8b0:4023:1c01:0:0:0:65
</span></span><span class="line"><span class="cl">google.com mail is handled by <span class="m">10 </span>smtp.google.com
</span></span></code></pre></div><p>Recently, I bumped into an old post on the <a href="https://blog.cloudflare.com">Cloudflare
blog</a> about <a href="https://blog.cloudflare.com/the-weird-and-wonderful-world-of-dns-loc-records/">The weird and wonderful world of
DNS LOC
records</a>
and realized that we did not properly support parsing <a href="https://www.rfc-editor.org/rfc/rfc1876.txt">RFC
1876</a> which specifies a format
for returning <code>LOC</code> or <em>location</em> record specifying the <em>physical
location</em> of a service.</p>
<p>At the time of the post, Cloudflare indicated they handle <em>“millions of DNS
records; of those just 743 are LOCs.”</em>. I found a webpage that lists <a href="https://www.ckdhr.com/dns-loc/sites.html">sites
supporting DNS LOC</a> and contains
only nine examples.</p>
<p>It is not widely used, but it is very cool.</p>
<p>You can use the <a href="https://en.wikipedia.org/wiki/Dig_(command)">dig command</a>
to query for a <code>LOC</code> record and see what is returned:</p>
<pre tabindex="0"><code>$ dig alink.net LOC
alink.net. 66 IN LOC 37 22 26.000 N 122 1 47.000 W 30.00m 30m 30m 10m
</code></pre><p>The fields that were returned include:</p>
<ul>
<li>latitude (37° 22’ 26.00" N)</li>
<li>longitude (122° 1’ 47.00" W)</li>
<li>altitude (30.00m)</li>
<li>horizontal precision (30m)</li>
<li>vertical precision (30m)</li>
<li>entity size estimate (10m)</li>
</ul>
<p>In <a href="https://re.factorcode.org/2025/12/factor-0-101-now-available.html">Factor 0.101</a>, the field is
available and returned as bytes but not parsed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"alink.net"</span> dns-LOC-query answer-section>> ...
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> T{ rr
</span></span><span class="line"><span class="cl"> { name <span class="s">"alink.net"</span> }
</span></span><span class="line"><span class="cl"> { type LOC }
</span></span><span class="line"><span class="cl"> { class IN }
</span></span><span class="line"><span class="cl"> { ttl <span class="m">300 </span>}
</span></span><span class="line"><span class="cl"> { rdata
</span></span><span class="line"><span class="cl"> B{
</span></span><span class="line"><span class="cl"> <span class="m">0 51 51 19 136 5 2 80 101 208 181 8 0 152 162 56
</span></span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Of course, I love odd uses of technology like <a href="https://re.factorcode.org/2011/04/wikipedia-over-dns.html">Wikipedia over
DNS</a> and I thought
<a href="https://factorcode.org">Factor</a> should probably add proper support for the
<code>LOC</code> record!</p>
<p>First, we define a <a href="https://docs.factorcode.org/content/article-tuples.html">tuple
class</a> to hold the
<code>LOC</code> record fields:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">loc</span> <span class="nv">size</span> <span class="nv">horizontal</span> <span class="nv">vertical</span> <span class="nv">lat</span> <span class="nv">lon</span> <span class="nv">alt</span> <span class="k">;
</span></span></span></code></pre></div><p>Next, we parse the <code>LOC</code> record, converting sizes (in centimeters),
lat/lon (in degrees), and altitude (in centimeters):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-loc</span> <span class="nf">( -- </span><span class="nv">loc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> loc <span class="nb">new
</span></span></span><span class="line"><span class="cl"> <span class="nb">read1 </span><span class="m">0 </span><span class="nb">assert=
</span></span></span><span class="line"><span class="cl"> <span class="nb">read1 </span>[ <span class="m">-4 </span><span class="nb">shift </span>] [ <span class="m">4 </span>bits ] <span class="nb">bi </span>10^ <span class="nb">* </span>>>size
</span></span><span class="line"><span class="cl"> <span class="nb">read1 </span>[ <span class="m">-4 </span><span class="nb">shift </span>] [ <span class="m">4 </span>bits ] <span class="nb">bi </span>10^ <span class="nb">* </span>>>horizontal
</span></span><span class="line"><span class="cl"> <span class="nb">read1 </span>[ <span class="m">-4 </span><span class="nb">shift </span>] [ <span class="m">4 </span>bits ] <span class="nb">bi </span>10^ <span class="nb">* </span>>>vertical
</span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>be> <span class="m">31 </span><span class="nb">2^ - </span><span class="m">3600000 </span><span class="nb">/ </span>>>lat
</span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>be> <span class="m">31 </span><span class="nb">2^ - </span><span class="m">3600000 </span><span class="nb">/ </span>>>lon
</span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>be> <span class="m">10000000 </span><span class="nb">- </span>>>alt <span class="k">;
</span></span></span></code></pre></div><p>We hookup the <code>LOC</code> type to be parsed properly:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">LOC</span> <span class="nf">parse-rdata</span> <span class="nb">2drop </span>parse-loc <span class="k">;
</span></span></span></code></pre></div><p>And then build a word to print the location nicely:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">LOC.</span> <span class="nf">( </span><span class="nv">name</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> dns-LOC-query answer-section>> [
</span></span><span class="line"><span class="cl"> rdata>> {
</span></span><span class="line"><span class="cl"> [ lat>> [ <span class="nb">abs </span><span class="m">1 </span><span class="nb">/mod </span><span class="m">60 </span><span class="nb">* </span><span class="m">1 </span><span class="nb">/mod </span><span class="m">60 </span><span class="nb">* </span>] [ <span class="nb">neg? </span><span class="s">"S"</span> <span class="s">"N"</span> <span class="nb">? </span>] <span class="nb">bi </span>]
</span></span><span class="line"><span class="cl"> [ lon>> [ <span class="nb">abs </span><span class="m">1 </span><span class="nb">/mod </span><span class="m">60 </span><span class="nb">* </span><span class="m">1 </span><span class="nb">/mod </span><span class="m">60 </span><span class="nb">* </span>] [ <span class="nb">neg? </span><span class="s">"W"</span> <span class="s">"E"</span> <span class="nb">? </span>] <span class="nb">bi </span>]
</span></span><span class="line"><span class="cl"> [ alt>> <span class="m">100 </span><span class="nb">/ </span>]
</span></span><span class="line"><span class="cl"> [ size>> <span class="m">100 </span><span class="nb">/i </span>]
</span></span><span class="line"><span class="cl"> [ horizontal>> <span class="m">100 </span><span class="nb">/i </span>]
</span></span><span class="line"><span class="cl"> [ vertical>> <span class="m">100 </span><span class="nb">/i </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span><span class="s">"%d %d %.3f %s %d %d %.3f %s %.2fm %dm %dm %dm\n"</span> printf
</span></span><span class="line"><span class="cl"> ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>And, finally, we can give it a try!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"alink.net"</span> LOC.
</span></span><span class="line"><span class="cl"><span class="m">37 22 26.000 </span>N <span class="m">122 1 47.000 </span>W 30.00m 30m 30m 10m
</span></span></code></pre></div><p>Yay, it matches!</p>
<p>This is available in the latest <a href="https://github.com/factor/factor">development
version</a>.</p>
Factor 0.101 now available
https://re.factorcode.org/2025/12/factor-0-101-now-available.html
Mon, 08 Dec 2025 20:00:00 -0700https://re.factorcode.org/2025/12/factor-0-101-now-available.html<p><em>“Keep thy airspeed up, lest the earth come from below and smite thee.” -
William Kershner</em></p>
<p>I’m very pleased to announce the release of <a href="https://factorcode.org">Factor</a>
0.101!</p>
<table class="downloads" cellspacing="0">
<colgroup>
<col style="width: 25%" />
<col style="width: 25%" />
<col style="width: 25%" />
<col style="width: 25%" />
</colgroup>
<thead>
<tr class="header">
<th class="nobg" style="text-align: center;">OS/CPU</th>
<th class="bg" style="text-align: center;" scope="col">Windows</th>
<th class="bg" style="text-align: center;" scope="col">Mac OS</th>
<th class="bg" style="text-align: center;" scope="col">Linux</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<th class="bg" style="text-align: center;" scope="row">x86</th>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.101/factor-windows-x86-32-0.101.zip">0.101</a>
</td>
<td class="doesnotexist">
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.101/factor-linux-x86-32-0.101.tar.gz">0.101</a>
</td>
</tr>
<tr class="even">
<th class="bg" style="text-align: center;" scope="row">x86-64</th>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.101/factor-windows-x86-64-0.101.zip">0.101</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.101/factor-macos-x86-64-0.101.dmg">0.101</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.101/factor-linux-x86-64-0.101.tar.gz">0.101</a>
</td>
</tr>
</tbody>
</table>
<p><strong>Source code</strong>: <a
href="https://downloads.factorcode.org/releases/0.101/factor-src-0.101.zip"
class="release">0.101</a></p>
<p>This release is brought to you with almost 700 commits by the following individuals:</p>
<blockquote>
<p>Aleksander Sabak, Andy Kluger, Cat Stevens, Dmitry Matveyev, Doug Coleman,
Giftpflanze, John Benediktsson, Jon Harper, Jonas Bernouli, Leo Mehraban, Mike
Stevenson, Nicholas Chandoke, Niklas Larsson, Rebecca Kelly, Samuel Tardieu,
Stefan Schmiedl, <a href="https://github.com/Bruno-366">@Bruno-366</a>,
<a href="https://github.com/bobisageek">@bobisageek</a>,
<a href="https://github.com/coltsingleactionarmyocelot">@coltsingleactionarmyocelot</a>,
<a href="https://github.com/inivekin">@inivekin</a>,
<a href="https://github.com/knottio">@knottio</a>, <a href="https://github.com/timor">@timor</a></p>
</blockquote>
<p>Besides some bug fixes and library improvements, I want to highlight the following changes:</p>
<ul>
<li>Moved the UI to render buttons and scrollbars rather than using images, which
allows easier theming.</li>
<li>Fixed <a href="https://re.factorcode.org/2025/09/hidpi.html">HiDPI</a> scaling on Linux and
Windows, although it currently doesn’t update the window settings when
switching between screens with different scaling factors.</li>
<li>Update to Unicode 17.0.0.</li>
<li>Plugin support for the <a href="https://re.factorcode.org/2025/08/neovim.html">Neovim editor</a>.</li>
</ul>
<p>Some possible backwards compatibility issues:</p>
<ul>
<li>The argument order to <code>ltake</code> was swapped to be more consistent with words like <code>head</code>.</li>
<li>The <code>environment</code> vocabulary on Windows now supports disambiguating <code>f</code> and <code>""</code> (empty) values</li>
<li>The <code>misc/atom</code> folder was removed in favor of the <a href="https://github.com/factor/atom-language-factor">factor/atom-language-factor</a> repo.</li>
<li>The <code>misc/Factor.tmbundle</code> folder was removed in favor of the <a href="https://github.com/factor/factor.tmbundle">factor/factor.tmbundle</a> repo.</li>
<li>The <code>misc/vim</code> folder was removed in favor of the <a href="https://github.com/factor/factor.vim">factor/factor.vim</a> repo.</li>
<li>The <code>http</code> vocabulary <code>request</code> tuple had a slot rename from <code>post-data</code> to <code>data</code>.</li>
<li>The <code>furnace.asides</code> vocabulary had a slot rename from <code>post-data</code> to <code>data</code>, and might require running <code>ALTER TABLE asides RENAME COLUMN "post-data" TO data;</code>.</li>
<li>The <code>html.streams</code> vocabulary was renamed to <code>io.streams.html</code></li>
<li>The <code>pdf.streams</code> vocabulary was renamed to <code>io.streams.pdf</code></li>
</ul>
<h3 id="what-is-factor">What is Factor</h3>
<p>Factor is a <a href="https://www.concatenative.org/">concatenative</a>, stack-based
programming language with <a href="https://concatenative.org/wiki/view/Factor/Features/The%20language">high-level
features</a>
including dynamic types, extensible syntax, macros, and garbage
collection. On a practical side, Factor has a <a href="https://docs.factorcode.org/content/article-vocab-index.html">full-featured
library</a>,
supports many different platforms, and has been extensively documented.</p>
<p>The implementation is <a href="https://concatenative.org/wiki/view/Factor/Optimizing%20compiler">fully
compiled</a>
for performance, while still supporting <a href="https://concatenative.org/wiki/view/Factor/Interactive%20development">interactive
development</a>.
Factor applications are portable between all common platforms. Factor
can <a href="https://concatenative.org/wiki/view/Factor/Deployment">deploy stand-alone
applications</a> on
all platforms. Full source code for the Factor project is available
under a BSD license.</p>
<h3 id="new-libraries">New libraries:</h3>
<ul>
<li><a href="https://docs.factorcode.org/content/vocab-base92.html">base92</a>: adding support for Base92 encoding/decoding</li>
<li><a href="https://docs.factorcode.org/content/vocab-bitcask.html">bitcask</a>: implementing the <a href="https://re.factorcode.org/2025/06/bitcask.html">Bitcask key/value database</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-bluesky.html">bluesky</a>: adding support for the BlueSky protocol</li>
<li><a href="https://docs.factorcode.org/content/vocab-calendar.holidays.world.html">calendar.holidays.world</a>: adding some new holidays including <a href="https://re.factorcode.org/2025/07/world-emoji-day.html">World Emoji Day</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-classes.enumeration.html">classes.enumeration</a>: adding <em>enumeration classes</em> and new <code>ENUMERATION:</code> syntax word</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.oklab.html">colors.oklab</a>: adding support for <a href="https://en.wikipedia.org/wiki/Oklab_color_space">OKLAB color space</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.oklch.html">colors.oklch</a>: adding support for <a href="https://en.wikipedia.org/wiki/Oklab_color_space">OKLCH color space</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.wavelength.html">colors.wavelength</a>: adding <code>wavelength>rgba</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-combinators.syntax.html">combinators.syntax</a>: adding experimental <em>combinator syntax</em> words <code>@[</code>, <code>*[</code>, and <code>&[</code>, and short-circuiting <code>n&&[</code>, <code>n||[</code>, <code>&&[</code> and <code>||[</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-continuations.extras.html">continuations.extras</a>: adding <code>with-datastacks</code> and <code>datastack-states</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-dotenv.html">dotenv</a>: implementing support for <a href="https://re.factorcode.org/2025/06/dotenv.html">Dotenv</a> files</li>
<li><a href="https://docs.factorcode.org/content/vocab-edn.html">edn</a>: implementing support for <a href="https://re.factorcode.org/2025/10/extensible-data-notation.html">Extensible Data Notation</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.cursor.html">editors.cursor</a>: adding support for the <a href="https://www.cursor.com">Cursor</a> editor</li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.rider.html">editors.rider</a>: adding support for the <a href="https://www.jetbrains.com/rider/">JetBrains Rider</a> editor</li>
<li><a href="https://docs.factorcode.org/content/vocab-gitignore.html">gitignore</a>: parser for <code>.gitignore</code> files</li>
<li><a href="https://docs.factorcode.org/content/vocab-http.json.html">http.json</a>: promoted <code>json.http</code> and added some useful words</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.farkup.html">io.streams.farkup</a>: a <a href="https://concatenative.org/wiki/view/Farkup">Farkup</a> formatted stream protocol</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.markdown.html">io.streams.markdowns</a>: a <a href="https://en.wikipedia.org/wiki/Markdown">Markdown</a> formatted stream protocol</li>
<li><a href="https://docs.factorcode.org/content/vocab-locals.lazy.html">locals.lazy</a>: prototype of <a href="https://re.factorcode.org/2024/10/emit.html">emit syntax</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-monadics.html">monadics</a>: alternative vocabulary for using Haskell-style monads, applicatives, and functors</li>
<li><a href="https://docs.factorcode.org/content/vocab-multibase.html">multibase</a>: implementation of <a href="https://github.com/multiformats/multibase">Multibase</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-pickle.html">pickle</a>: support for the <a href="https://re.factorcode.org/2025/08/pickle.html">Pickle</a> serialization format</li>
<li><a href="https://docs.factorcode.org/content/vocab-persistent.hashtables.identity.html">persistent.hashtables.identity</a>: support an <a href="https://docs.factorcode.org/content/word-identity-hashcode,kernel.html">identity-hashcode</a> version of persisent hashtables</li>
<li><a href="https://docs.factorcode.org/content/vocab-raylib.live-coding.html">raylib.live-coding</a>: demo of a vocabulary to do “live coding” of Raylib programs</li>
<li><a href="https://docs.factorcode.org/content/vocab-rdap.html">rdap</a>: support for the <a href="https://re.factorcode.org/2025/03/rdap.html">Registration Data Access Protocol</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-reverse.html">reverse</a>: implementation of the <a href="https://re.factorcode.org/2025/09/std-flip.html">std::flip</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-slides.cli.html">slides.cli</a>: simple text-based command-line interface for slides</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.highlight.html">tools.highlight</a>: command-line syntax-highlighting tool</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.random.html">tools.random</a>: command-line random generator tool</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.pens.rounded.html">ui.pens.rounded</a>: adding rounded corner <a href="https://docs.factorcode.org/content/vocab-ui.pens.html">pen</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.pens.theme.html">ui.pens.theme</a>: experimental themed <a href="https://docs.factorcode.org/content/vocab-ui.pens.html">pen</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.theme.html">ui.tools.theme</a>: some words for updating <a href="https://docs.factorcode.org/content/article-ui-tools.html">UI developer tools</a> themes</li>
</ul>
<h3 id="improved-libraries">Improved libraries:</h3>
<ul>
<li><a href="https://docs.factorcode.org/content/vocab-alien.syntax.html">alien.syntax</a>: added <code>C-LIBRARY:</code> syntax word</li>
<li><a href="https://docs.factorcode.org/content/vocab-assocs.extras.html">assocs.extras</a>: added <code>nzip</code> and <code>nunzip</code>, <code>map-zip</code> and <code>map-unzip</code> macros</li>
<li><a href="https://docs.factorcode.org/content/vocab-base32.html">base32</a>: adding the <a href="http://philzimmermann.com/docs/human-oriented-base-32-encoding.txt">human-oriented Base32</a> encoding via <code>zbase32></code> and <code>>zbase32</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-base64.html">base64</a>: minor performance improvement</li>
<li><a href="https://docs.factorcode.org/content/vocab-benchmark.html">benchmark</a>: adding more benchmarks</li>
<li><a href="https://docs.factorcode.org/content/vocab-bootstrap.assembler.html">bootstrap.assembler</a>: fixes for ARM-64</li>
<li><a href="https://docs.factorcode.org/content/vocab-brainfuck.html">brainfuck</a>: added <code>BRAINFUCK:</code> syntax word and <code>interpret-brainfuck</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-bson.html">bson</a>: use <a href="https://docs.factorcode.org/content/article-linked-assocs.html">linked-assocs</a> to preserve order</li>
<li><a href="https://docs.factorcode.org/content/vocab-cache.html">cache</a>: implement <code>M\ cache-assoc delete-at</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-calendar.html">calendar</a>: adding <code>year<</code>, <code>year<=</code>, <code>year></code>, <code>year>=</code> words</li>
<li><a href="https://docs.factorcode.org/content/vocab-calendar.format.html">calendar.format</a>: parse human-readable and elapsed-time output back into duration objects</li>
<li><a href="https://docs.factorcode.org/content/vocab-cbor.html">cbor</a>: use <a href="https://docs.factorcode.org/content/article-linked-assocs.html">linked-assocs</a> to preserve order</li>
<li><a href="https://docs.factorcode.org/content/vocab-classes.mixin.html">classes.mixin</a>: added <code>definer</code> implementation</li>
<li><a href="https://docs.factorcode.org/content/vocab-classes.singleton.html">classes.singleton</a>: added <code>definer</code> implementation</li>
<li><a href="https://docs.factorcode.org/content/vocab-classes.tuple.html">classes.tuple</a>: added <code>tuple>slots</code>, rename <code>tuple>array</code> to <code>pack-tuple</code> and <code>>tuple</code> to <code>unpack-tuple</code>.</li>
<li><a href="https://docs.factorcode.org/content/vocab-classes.union.html">classes.union</a>: added <code>definer</code> implementation</li>
<li><a href="https://docs.factorcode.org/content/vocab-checksums.sha.html">checksums.sha</a>: some 20-40% performance improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-command-line.html">command-line</a>: allow passing script name of <code>-</code> to use stdin</li>
<li><a href="https://docs.factorcode.org/content/vocab-command-line.parser.html">command-line.parser</a>: support for <a href="https://re.factorcode.org/2025/07/argument-parser-commands.html">Argument Parser Commands</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-command-line.startup.html">command-line.startup</a>: document <code>-q</code> quiet mode flag</li>
<li><a href="https://docs.factorcode.org/content/vocab-concurrency.combinators.html">concurrency.combinators</a>: faster <code>parallel-map</code> and <code>parallel-assoc-map</code> using a <a href="https://docs.factorcode.org/content/article-concurrency.count-downs.html">count-down latch</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-concurrency.promises.html">concurrency.promises</a>: 5-7% performance improvement</li>
<li><a href="https://docs.factorcode.org/content/vocab-continuations.html">continuations</a>: improve docs and fix stack effect for <code>ifcc</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-countries.html">countries</a>: adding <code>CQ</code> country code for <a href="https://en.wikipedia.org/wiki/Sark">Sark</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-cpu.architecture.html">cpu.architecture</a>: fix <code>*-branch</code> stack effects</li>
<li><a href="https://docs.factorcode.org/content/vocab-cpu.arm.html">cpu.arm</a>: fixes for ARM-64</li>
<li><a href="https://docs.factorcode.org/content/vocab-crontab.html">crontab</a>: added <code>parse-crontab</code> which ignores blank lines and comments</li>
<li><a href="https://docs.factorcode.org/content/vocab-db.html">db</a>: making <code>query-each</code> row-polymorphic</li>
<li><a href="https://docs.factorcode.org/content/vocab-delegate.protocols.html">delegate.protocols</a>: adding <code>keys</code> and <code>values</code> to <code>assoc-protocol</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-discord.html">discord</a>: better support for network disconnects, added a configurable retry interval</li>
<li><a href="https://docs.factorcode.org/content/vocab-discord.chatgpt-bot.html">discord.chatgpt-bot</a>: some fixes for <a href="https://lmstudio.ai/">LM Studio</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.html">editors</a>: make the <em>editor restart</em> nicer looking</li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.focus.html">editors.focus</a>: support <em>open-file-to-line-number</em> on newer releases, support Linux and Window</li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.zed.html">editors.zed</a>: support use of <a href="https://zed.dev/">Zed</a> on Linux</li>
<li><a href="https://docs.factorcode.org/content/vocab-endian.html">endian</a>: faster endian conversions of c-ptr-like objects</li>
<li><a href="https://docs.factorcode.org/content/vocab-environment.html">environment</a>: adding <code>os-env?</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-eval.html">eval</a>: move datastack and error messages to stderr</li>
<li><a href="https://docs.factorcode.org/content/vocab-fonts.html">fonts</a>: make <code><font></code> take a name, easier defaults</li>
<li><a href="https://docs.factorcode.org/content/vocab-furnace.asides.html">furnace.asides</a>: rename <code>post-data</code> slot on <code>aside</code> tuples to <code>data</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-generalizations.html">generalizations</a>: moved some <em>dip</em> words to <a href="https://docs.factorcode.org/content/vocab-shuffle.html">shuffle</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-help.tour.html">help.tour</a>: fix some typos/grammar</li>
<li><a href="https://docs.factorcode.org/content/vocab-html.template.chloe.html">html.templates.chloe</a>: improve use of <code>CDATA</code> tags for unescaping output</li>
<li><a href="https://docs.factorcode.org/content/vocab-http.html">http</a>: rename <code>post-data</code> slot on <code>request</code> tuples to <code>data</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-http.json.html">http.json</a>: adding <code>http-json</code> that doesn’t return the response object</li>
<li><a href="https://docs.factorcode.org/content/vocab-http.websockets.html">http.websockets</a>: making <code>read-websocket-loop</code> row-polymorphic</li>
<li><a href="https://docs.factorcode.org/content/vocab-ini-file.html">ini-file</a>: adding <code>ini>file</code>, <code>file>ini</code>, and use <code>LH{ }</code> to preserve configuration order</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.encodings.detect.html">io.encodings.detect</a>: adding <code>utf7</code> detection</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.encodings.utf8.html">io.encodings.utf8</a>: adding <code>utf8-bom</code> to handle optional BOM</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.random.html">io.random</a>: speed up <code>random-line</code> and <code>random-lines</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.ansi.html">io.streams.ansi</a>: adding documentation and tests, support dim foreground on terminals that support it</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.escape-codes.html">io.streams.escape-codes</a>: adding documentation and tests</li>
<li><a href="https://docs.factorcode.org/content/vocab-ip-parser.html">ip-parser</a>: adding IPV4 and IPV6 network words</li>
<li><a href="https://docs.factorcode.org/content/vocab-kernel.html">kernel</a>: adding <code>until*</code>, fix docs for <code>and*</code> and <code>or*</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-linked-sets.html">linked-sets</a>: adding <code>LS{</code> syntax word</li>
<li><a href="https://docs.factorcode.org/content/vocab-lists.lazy.html">lists.lazy</a>: changed the argument order in <code>ltake</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-macho.html">macho</a>: support a few more link edit commands</li>
<li><a href="https://docs.factorcode.org/content/vocab-make.html">make</a>: adding <code>,%</code> for a <code>push-at</code> variant</li>
<li><a href="https://docs.factorcode.org/content/vocab-mason.release.tidy.html">mason.release.tidy</a>: cleanup a few more git artifacts</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.combinatorics.html">math.combinatorics</a>: adding <em>counting</em> words</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.distances.html">math.distances</a>: adding <code>jaro-distance</code> and <code>jaro-winkler-distance</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.extras.html">math.extras</a>: added <code>all-removals</code>, support <a href="https://re.factorcode.org/2025/05/recamans-sequence.html">Recamán’s sequence</a>, and <a href="https://re.factorcode.org/2025/07/tribonacci-numbers.html">Tribonacci Numbers</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.factorials.html">math.factorials</a>: added <code>subfactorial</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.functions.html">math.functions</a>: added “closest to zero” modulus</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.parser.html">math.parser</a>: improve ratio parsing for consistency</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.primes.html">math.primes</a>: make <code>prime?</code> safe from non-integer inputs</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.runge-kutta.html">math.runge-kutta</a>: make generalized improvements to the <a href="https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods">Runge-Kutta</a> solver</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.similarity.html">math.similarity</a>: adding <code>jaro-similarity</code>, <code>jaro-winkler-similarity</code>, and <code>trigram-similarity</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.text.english.html">math.text.english</a>: fix issue with very large and very small floats</li>
<li><a href="https://docs.factorcode.org/content/vocab-metar.html">metar</a>: updated the abbreviations glossary</li>
<li><a href="https://docs.factorcode.org/content/vocab-mime.types.html">mime.types</a>: updating <code>mime.types</code> file</li>
<li><a href="https://docs.factorcode.org/content/vocab-msgpack.html">msgpack</a>: use <a href="https://docs.factorcode.org/content/article-linked-assocs.html">linked-assocs</a> to preserve order</li>
<li><a href="https://docs.factorcode.org/content/vocab-qw.html">qw</a>: adding <code>qw:</code> syntax</li>
<li><a href="https://docs.factorcode.org/content/vocab-path-finding.html">path-finding</a>: added <code>find-path*</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-peg.parsers.html">peg.parsers</a>: faster <code>list-of</code> and <code>list-of-many</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-progress-bars.models.html">progress-bars.models</a>: added <code>with-progress-display</code>, <code>map-with-progress-bar</code>, <code>each-with-progress-bar</code>, and <code>reduce-with-progress-bar</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-raylib.html">raylib</a>: adding <code>trace-log</code> and <code>set-trace-log-level</code>, updated to Raylib 5.5</li>
<li><a href="https://docs.factorcode.org/content/vocab-readline-listener.html">readline-listener</a>: store history across sessions, support color on terminals that support it</li>
<li><a href="https://docs.factorcode.org/content/vocab-robohash.html">robohash</a>: support for <code>"set4"</code>, <code>"set5"</code>, and <code>"set6"</code> types</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.html">sequences</a>: rename <code>midpoint@</code> to <code>midpoint</code>, faster <code>each-from</code> and <code>map-reduce</code> on slices</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.extras.html">sequences.extras</a>: adding <code>find-nth</code>, <code>find-nth-last</code>, <code>subseq-indices</code>, <code>deep-nth</code>, <code>deep-nth-of</code>, <code>2none?</code>, <code>filter-errors</code>, <code>reject-errors</code>, <code>all-same?</code>, <code>adjacent-differences</code>, and <code>partial-sum</code>.</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.generalizations.html">sequences.generalizations</a>: fix <code>?firstn</code> and <code>?lastn</code> for string inputs, removed <code>(nsequence)</code> which duplicates <code>set-firstn-unsafe</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.prefixed.html">sequences.prefixed</a>: swap order of <code><prefixed></code> arguments to match <code>prefix</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.repeating.html">sequences.repeating</a>: adding <code><cycles-from></code> and <code>cycle-from</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.snipped.html">sequences.snipped</a>: fixed out-of-bounds issues</li>
<li><a href="https://docs.factorcode.org/content/vocab-scryfall.html">scryfall</a>: update for duskmourn</li>
<li><a href="https://docs.factorcode.org/content/vocab-shuffle.html">shuffle</a>: improve stack-checking of <code>shuffle(</code> syntax, added <code>SHUFFLE:</code> syntax, <code>nreverse</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-sorting.html">sorting</a>: fix <code>sort-with</code> to apply the quot with access to the stack below</li>
<li><a href="https://docs.factorcode.org/content/vocab-sorting.human.html">sorting.human</a>: implement <a href="https://re.factorcode.org/2025/03/human-sorting-improved.html">human sorting improved</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-system-info.macos.html">system-info.macos</a>: adding “Tahoe” code-name for <a href="https://www.apple.com/newsroom/2025/06/macos-tahoe-26-makes-the-mac-more-capable-productive-and-intelligent-than-ever/">macOS 26</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-terminfo.html">terminfo</a>: add words for querying specific output capabilities</li>
<li><a href="https://docs.factorcode.org/content/vocab-threads.html">threads</a>: define a generalized <code>linked-thread</code> which used to be for <code>concurrency.mailboxes</code> only</li>
<li><a href="https://docs.factorcode.org/content/vocab-toml.html">toml</a>: use <a href="https://docs.factorcode.org/content/article-linked-assocs.html">linked-assocs</a> to preserve order, adding <code>>toml</code> and <code>write-toml</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.annotations.html">tools.annotations</a>: adding <code><WATCH ... WATCH></code> syntax</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.deploy.html">tools.deploy</a>: adding a command-line interface for deploy options</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.deploy.backend.html">tools.deploy.backend</a>: fix boot image location in system-wide installations</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.deploy.unix.html">tools.deploy.unix</a>: change binary name to append <code>.out</code> to fix conflict with vocab resources</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.directory-to-file.html">tools.directory-to-file</a>: better test file metrics, print filename for editing</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.memory.html">tools.memory</a>: adding <code>heap-stats-of</code> arbitrary sequence of instances, and <code>total-size</code> size of everything pointed to by an object</li>
<li><a href="https://docs.factorcode.org/content/vocab-txon.html">txon</a>: use <a href="https://docs.factorcode.org/content/article-linked-assocs.html">linked-assocs</a> to preserve order</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.html">ui</a>: adding <code>adjust-font-size</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.gadgets.buttons.html">ui.gadgets.buttons</a>: stop using images and respect theme colors</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.gadgets.sliders.html">ui.gadgets.sliders</a>: stop using images and respect theme colors</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.theme.base16.html">ui.theme.base16</a>: adding a lot more (270!) <a href="https://re.factorcode.org/2024/10/base16-themes.html">Base16 Themes</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.html">ui.tools</a>: adding font-sizing keyboard shortcuts</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.browser.html">ui.tools.browser</a>: more responsive font sizing</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.listener.html">ui.tools.listener</a>: more responsive font sizing, adding some <a href="https://docs.factorcode.org/content/article-ui-listener-style.html">UI listener styling</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.listener.completion.html">ui.tools.listener.completion</a>: allow spaces in history search popup</li>
<li><a href="https://docs.factorcode.org/content/vocab-unicode.html">unicode</a>: update to <a href="https://www.unicode.org/versions/Unicode17.0.0/">Unicode 17.0.0</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-webapps.planet.html">webapps.planet</a>: improve CSS for <code>video</code> tags</li>
<li><a href="https://docs.factorcode.org/content/vocab-words.html">words</a>: adding <code>define-temp-syntax</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-zoneinfo.html">zoneinfo</a>: update to version 2025b</li>
</ul>
<h3 id="removed-libraries">Removed libraries</h3>
<ul>
<li><code>ui.theme.images</code></li>
</ul>
<h3 id="vm-improvements">VM Improvements:</h3>
<ul>
<li>More work on ARM64 backend (fix set-callstack, fix generic dispatch)</li>
</ul>
zxcvbn
https://re.factorcode.org/2025/12/zxcvbn.html
Fri, 05 Dec 2025 08:00:00 -0700https://re.factorcode.org/2025/12/zxcvbn.html<p>Years ago, <a href="https://dropbox.com">Dropbox</a> wrote about <a href="https://dropbox.tech/security/zxcvbn-realistic-password-strength-estimation">zxcvbn: realistic password strength
estimation</a>:</p>
<blockquote>
<p><code>zxcvbn</code> is a password strength estimator inspired by password crackers.
Through pattern matching and conservative estimation, it recognizes and
weighs 30k common passwords, common names and surnames according to US census
data, popular English words from Wikipedia and US television and movies, and
other common patterns like dates, repeats (<code>aaa</code>), sequences (<code>abcd</code>),
keyboard patterns (<code>qwertyuiop</code>), and l33t speak.</p>
</blockquote>
<p>And it appears to have been successful – the <a href="https://github.com/dropbox/zxcvbn">original
implementation</a> is in JavaScript, but there
have been clones of the algorithm generated in many different languages:</p>
<blockquote>
<p>At Dropbox we use zxcvbn (<a href="https://github.com/dropbox/zxcvbn/releases">Release notes</a>) on our web, desktop, iOS and Android clients. If JavaScript doesn’t work for you, others have graciously ported the library to these languages:</p>
<ul>
<li><a href="https://github.com/dwolfhub/zxcvbn-python"><code>zxcvbn-python</code></a> (Python)</li>
<li><a href="https://github.com/rianhunter/zxcvbn-cpp"><code>zxcvbn-cpp</code></a> (C/C++/Python/JS)</li>
<li><a href="https://github.com/tsyrogit/zxcvbn-c"><code>zxcvbn-c</code></a> (C/C++)</li>
<li><a href="https://github.com/shssoichiro/zxcvbn-rs"><code>zxcvbn-rs</code></a> (Rust)</li>
<li><a href="https://github.com/nbutton23/zxcvbn-go"><code>zxcvbn-go</code></a> (Go)</li>
<li><a href="https://github.com/nulab/zxcvbn4j"><code>zxcvbn4j</code></a> (Java)</li>
<li><a href="https://github.com/GoSimpleLLC/nbvcxz"><code>nbvcxz</code></a> (Java)</li>
<li><a href="https://github.com/envato/zxcvbn-ruby"><code>zxcvbn-ruby</code></a> (Ruby)</li>
<li><a href="https://github.com/bitzesty/zxcvbn-js"><code>zxcvbn-js</code></a> (Ruby [via ExecJS])</li>
<li><a href="https://github.com/dropbox/zxcvbn-ios"><code>zxcvbn-ios</code></a> (Objective-C)</li>
<li><a href="https://github.com/mickford/zxcvbn-cs"><code>zxcvbn-cs</code></a> (C#/.NET)</li>
<li><a href="https://github.com/tekul/szxcvbn"><code>szxcvbn</code></a> (Scala)</li>
<li><a href="https://github.com/bjeavons/zxcvbn-php"><code>zxcvbn-php</code></a> (PHP)</li>
<li><a href="https://github.com/wcjr/zxcvbn-api"><code>zxcvbn-api</code></a> (REST)</li>
<li><a href="https://github.com/cryptosense/ocaml-zxcvbn"><code>ocaml-zxcvbn</code></a> (OCaml bindings for <code>zxcvbn-c</code>)</li>
</ul>
</blockquote>
<p>In today’s era of <a href="https://www.wired.com/story/best-password-managers/">password
managers</a>,
<a href="https://en.wikipedia.org/wiki/WebAuthn">WebAuthn</a> also known as <em>passkeys</em>,
and <a href="https://haveibeenpwned.com/PwnedWebsites">many pwned accounts</a>,
passwords may seem like a funny sort of outdated concept. They have
definitely provided good entertainment over the years from <a href="https://xkcd.com/936/">XKCD: Password
Strength</a> comics to the 20-year old <a href="https://knowyourmeme.com/memes/hunter2">hunter2
meme</a>:</p>
<p>
<img src="https://re.factorcode.org/images/2025-12-05-hunter2.jpg" alt="" width="736" height="261" />
</p>
<p>I have wanted a <a href="https://factorcode.org">Factor</a> implementation of this for
a long time – and finally <a href="https://github.com/mrjbq7/re-factor/commit/fa677e6663413c57dc4203b6ac2d961efa9c8f78">built zxcvbn in
Factor</a>!</p>
<p>We can use it to check out some potential passwords:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">zxcvbn</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"F@ct0r!"</span> zxcvbn.
</span></span><span class="line"><span class="cl">Score:
</span></span><span class="line"><span class="cl"> 1/4 (very guessable)
</span></span><span class="line"><span class="cl">Crack times:
</span></span><span class="line"><span class="cl"> Online (throttled): <span class="m">4 </span>months
</span></span><span class="line"><span class="cl"> Online (unthrottled): <span class="m">8 </span>hours
</span></span><span class="line"><span class="cl"> Offline (slow hash): <span class="m">30 </span>seconds
</span></span><span class="line"><span class="cl"> Offline (fast hash): less than a <span class="nb">second
</span></span></span><span class="line"><span class="cl">Suggestions:
</span></span><span class="line"><span class="cl"> Add another word <span class="nb">or </span>two. Uncommon words are better.
</span></span><span class="line"><span class="cl"> Capitalization doesn't help very much.
</span></span><span class="line"><span class="cl"> Predictable substitutions <span class="nb">like </span>'@' instead <span class="nb">of </span>'a' don't help very much.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"john2025"</span> zxcvbn.
</span></span><span class="line"><span class="cl">Score:
</span></span><span class="line"><span class="cl"> 1/4 (very guessable)
</span></span><span class="line"><span class="cl">Crack times:
</span></span><span class="line"><span class="cl"> Online (throttled): <span class="m">3 </span>months
</span></span><span class="line"><span class="cl"> Online (unthrottled): <span class="m">6 </span>hours
</span></span><span class="line"><span class="cl"> Offline (slow hash): <span class="m">23 </span>seconds
</span></span><span class="line"><span class="cl"> Offline (fast hash): less than a <span class="nb">second
</span></span></span><span class="line"><span class="cl">Warning:
</span></span><span class="line"><span class="cl"> Common names <span class="nb">and </span>surnames are easy to guess.
</span></span><span class="line"><span class="cl">Suggestions:
</span></span><span class="line"><span class="cl"> Add another word <span class="nb">or </span>two. Uncommon words are better.
</span></span></code></pre></div><p>That’s not so good, maybe we should use the <a href="https://docs.factorcode.org/content/article-random.passwords.html">random.passwords
vocabulary</a>
instead!</p>
<p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/zxcvbn/zxcvbn.factor">GitHub</a>.</p>
AsyncIO Performance
https://re.factorcode.org/2025/12/asyncio-performance.html
Mon, 01 Dec 2025 08:00:00 -0700https://re.factorcode.org/2025/12/asyncio-performance.html<p><a href="https://factorcode.org">Factor</a> has <a href="https://en.wikipedia.org/wiki/Green_thread">green
threads</a> and a long-standing
feature request to be able to utilize native threads more efficiently for
concurrent tasks. In the meantime, the cooperative threading model allows for
asynchronous tasks which is particularly useful when waiting for I/O such as
used by <a href="https://en.wikipedia.org/wiki/Network_socket">sockets over a computer
network</a>.</p>
<p>And while it might be true that <a href="https://kristoff.it/blog/asynchrony-is-not-concurrency/">asynchrony is not
concurrency</a>, there
are a lot of other things one could say about concurrency and multi-threaded or
multi-process performance. Today I want to discuss an article that <a href="https://github.com/willmcgugan">Will
McGugan</a> wrote about the <a href="https://textual.textualize.io/blog/2023/03/08/overhead-of-python-asyncio-tasks/">overhead of Python
asyncio
tasks</a>
and the good discussion that followed on <a href="https://news.ycombinator.com/item?id=35073136">Hacker
News</a>.</p>
<p>Let’s go over the benchmark in a few programming languages – including
<a href="https://factorcode.org">Factor</a>!</p>
<h2 id="python">Python</h2>
<p>The article presents this benchmark in <a href="https://python.org">Python</a> that does
no work but measures the relative overhead of the
<a href="https://docs.python.org/3/library/asyncio.html">asyncio</a> task infrastructure
when creating a large number of asynchronous tasks:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">asyncio</span> <span class="kn">import</span> <span class="n">create_task</span><span class="p">,</span> <span class="n">wait</span><span class="p">,</span> <span class="n">run</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">time</span> <span class="kn">import</span> <span class="n">process_time</span> <span class="k">as</span> <span class="n">time</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">time_tasks</span><span class="p">(</span><span class="n">count</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span> <span class="o">-></span> <span class="nb">float</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"""Time creating and destroying tasks."""</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">async</span> <span class="k">def</span> <span class="nf">nop_task</span><span class="p">()</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"""Do nothing task."""</span>
</span></span><span class="line"><span class="cl"> <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="n">tasks</span> <span class="o">=</span> <span class="p">[</span><span class="n">create_task</span><span class="p">(</span><span class="n">nop_task</span><span class="p">())</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">count</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="k">await</span> <span class="n">wait</span><span class="p">(</span><span class="n">tasks</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">elapsed</span> <span class="o">=</span> <span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">elapsed</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">count</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">100_000</span><span class="p">,</span> <span class="mi">1_000_000</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">100_000</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">create_time</span> <span class="o">=</span> <span class="n">run</span><span class="p">(</span><span class="n">time_tasks</span><span class="p">(</span><span class="n">count</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="n">create_per_second</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">/</span> <span class="p">(</span><span class="n">create_time</span> <span class="o">/</span> <span class="n">count</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">count</span><span class="si">:</span><span class="s2">9,</span><span class="si">}</span><span class="s2"> tasks </span><span class="se">\t</span><span class="s2"> </span><span class="si">{</span><span class="n">create_per_second</span><span class="si">:</span><span class="s2">0,.0f</span><span class="si">}</span><span class="s2"> tasks per/s"</span><span class="p">)</span>
</span></span></code></pre></div><p>Using the latest <a href="https://www.python.org/downloads/release/python-3140/">Python
3.14</a>, this is
reasonably fast on my laptop taking about 13 seconds:</p>
<pre tabindex="0"><code>$ time python3.14 foo.py
100,000 tasks 577,247 tasks per/s
200,000 tasks 533,911 tasks per/s
300,000 tasks 546,127 tasks per/s
400,000 tasks 488,219 tasks per/s
500,000 tasks 466,636 tasks per/s
600,000 tasks 469,972 tasks per/s
700,000 tasks 434,126 tasks per/s
800,000 tasks 428,456 tasks per/s
900,000 tasks 404,905 tasks per/s
1,000,000 tasks 376,167 tasks per/s
python3.14 foo.py 12.69s user 0.27s system 99% cpu 12.971 total
</code></pre><h2 id="factor">Factor</h2>
<p>We could translate this directly to <a href="https://factorcode.org">Factor</a> using the
<a href="https://docs.factorcode.org/content/article-concurrency.combinators.html">concurrency.combinators
vocabulary</a>.</p>
<p>In particular, the
<a href="https://docs.factorcode.org/content/word-parallel-map,concurrency.combinators.html">parallel-map</a>
word starts a new thread applying a
<a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a> to
each element in the
<a href="https://docs.factorcode.org/content/article-sequences.html">sequence</a> and then
waits for all the
<a href="https://docs.factorcode.org/content/article-threads.html">threads</a> to finish:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">concurrency.combinators</span> <span class="nn">formatting</span> <span class="nn">io</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">ranges</span> <span class="nn">sequences</span>
</span></span><span class="line"><span class="cl"><span class="nn">tools.time</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">time-tasks</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <iota> [ ] parallel-map <span class="nb">drop </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">run-tasks</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="m">100,000 1,000,000 100,000 </span><range> [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ time-tasks ] benchmark <span class="m">1e9 </span><span class="nb">/ dupd /
</span></span></span><span class="line"><span class="cl"> <span class="s">"%7d tasks \t %7d tasks per/s\n"</span> printf <span class="nb">flush
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>After making <a href="https://github.com/factor/factor/commit/07d901db390655f8dcb56ee12b103b3572660031">an improvement to our parallel-map
implementation</a>
that uses a <a href="https://docs.factorcode.org/content/article-concurrency.count-downs.html">count-down
latch</a>
for more efficient waiting on a group of tasks, this runs <strong>2.5x</strong> as fast as
Python:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ run-tasks ] time
</span></span><span class="line"><span class="cl"> <span class="m">100000 </span>tasks <span class="m">1246872 </span>tasks per/s
</span></span><span class="line"><span class="cl"> <span class="m">200000 </span>tasks <span class="m">1209500 </span>tasks per/s
</span></span><span class="line"><span class="cl"> <span class="m">300000 </span>tasks <span class="m">1141121 </span>tasks per/s
</span></span><span class="line"><span class="cl"> <span class="m">400000 </span>tasks <span class="m">1121304 </span>tasks per/s
</span></span><span class="line"><span class="cl"> <span class="m">500000 </span>tasks <span class="m">1119707 </span>tasks per/s
</span></span><span class="line"><span class="cl"> <span class="m">600000 </span>tasks <span class="m">1135459 </span>tasks per/s
</span></span><span class="line"><span class="cl"> <span class="m">700000 </span>tasks <span class="m">956541 </span>tasks per/s
</span></span><span class="line"><span class="cl"> <span class="m">800000 </span>tasks <span class="m">1091807 </span>tasks per/s
</span></span><span class="line"><span class="cl"> <span class="m">900000 </span>tasks <span class="m">944753 </span>tasks per/s
</span></span><span class="line"><span class="cl"><span class="m">1000000 </span>tasks <span class="m">1137681 </span>tasks per/s
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Running time: <span class="m">5.142044833 </span>seconds
</span></span></code></pre></div><p>That’s pretty good for a comparable dynamic language, and especially since we
are still running in <a href="https://support.apple.com/en-us/102527">Rosetta 2</a> on
<a href="https://www.apple.com/os/macos/">Apple macOS</a> translating Intel x86-64 to
Apple Silicon aarch64 on the fly!</p>
<p>It also turns out that 75% of the benchmark time is spent in the garbage
collector, so probably there are some big wins we can get if we look more
closely into that.</p>
<h2 id="go">Go</h2>
<p>We could translate that benchmark into <a href="https://go.dev/doc/go1.25">Go 1.25</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="s">"fmt"</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="s">"sync"</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="s">"time"</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">timeTasks</span><span class="p">(</span><span class="nx">count</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Duration</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">nopTask</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">done</span><span class="w"> </span><span class="kd">func</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">done</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">start</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">wg</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&</span><span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">wg</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="nx">count</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="p"><</span><span class="w"> </span><span class="nx">count</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">go</span><span class="w"> </span><span class="nf">nopTask</span><span class="p">(</span><span class="nx">wg</span><span class="p">.</span><span class="nx">Done</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">wg</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">Sub</span><span class="p">(</span><span class="nx">start</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">100_000</span><span class="p">;</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="o"><=</span><span class="w"> </span><span class="mi">1_000_000</span><span class="p">;</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="mi">100_000</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">createTime</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nf">timeTasks</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">createPerSecond</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="p">(</span><span class="mf">1.0</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="nb">float64</span><span class="p">(</span><span class="nx">createTime</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="nb">float64</span><span class="p">(</span><span class="nx">n</span><span class="p">)))</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nb">float64</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"%7d tasks \t %7d tasks per/s\n"</span><span class="p">,</span><span class="w"> </span><span class="nx">n</span><span class="p">,</span><span class="w"> </span><span class="nx">createPerSecond</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>And show that it is about <strong>11x</strong> times faster than Python using multiple CPUs.</p>
<pre tabindex="0"><code>$ time go run foo.go
100000 tasks 3889083 tasks per/s
200000 tasks 5748283 tasks per/s
300000 tasks 6324955 tasks per/s
400000 tasks 6265341 tasks per/s
500000 tasks 6301852 tasks per/s
600000 tasks 5572898 tasks per/s
700000 tasks 6239860 tasks per/s
800000 tasks 6276241 tasks per/s
900000 tasks 6226128 tasks per/s
1000000 tasks 6243859 tasks per/s
go run foo.go 2.44s user 0.71s system 270% cpu 1.165 total
</code></pre><p>If we limit <code>GOMAXPROCS</code> to one CPU, it runs only <strong>7.5x</strong> times faster than Python:</p>
<pre tabindex="0"><code>$ time GOMAXPROCS=1 go run foo.go
100000 tasks 2240106 tasks per/s
200000 tasks 2869379 tasks per/s
300000 tasks 2745897 tasks per/s
400000 tasks 3759142 tasks per/s
500000 tasks 3090267 tasks per/s
600000 tasks 3489138 tasks per/s
700000 tasks 3608874 tasks per/s
800000 tasks 3200636 tasks per/s
900000 tasks 3682102 tasks per/s
1000000 tasks 3259778 tasks per/s
GOMAXPROCS=1 go run foo.go 1.65s user 0.08s system 99% cpu 1.735 total
</code></pre><h2 id="javascript">JavaScript</h2>
<p>We could build the same benchmark in <a href="https://en.wikipedia.org/wiki/JavaScript">JavaScript</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">time_tasks</span><span class="p">(</span><span class="nx">count</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">async</span> <span class="kd">function</span> <span class="nx">nop_task</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">performance</span><span class="p">.</span><span class="nx">now</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">start</span> <span class="o">=</span> <span class="nx">performance</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">tasks</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">(</span><span class="nx">count</span><span class="p">).</span><span class="nx">map</span><span class="p">(</span><span class="nx">nop_task</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="kr">await</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">(</span><span class="nx">tasks</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">elapsed</span> <span class="o">=</span> <span class="nx">performance</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span> <span class="o">-</span> <span class="nx">start</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">elapsed</span> <span class="o">/</span> <span class="mf">1e3</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">run_tasks</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">count</span> <span class="o">=</span> <span class="mi">100000</span><span class="p">;</span> <span class="nx">count</span> <span class="o"><</span> <span class="mi">1000000</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">count</span> <span class="o">+=</span> <span class="mi">100000</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">ct</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">time_tasks</span><span class="p">(</span><span class="nx">count</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">count</span><span class="si">}</span><span class="sb">: </span><span class="si">${</span><span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="p">(</span><span class="nx">ct</span> <span class="o">/</span> <span class="nx">count</span><span class="p">))</span><span class="si">}</span><span class="sb"> tasks/sec`</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">run_tasks</span><span class="p">()</span>
</span></span></code></pre></div><p>And it runs pretty fast on <a href="https://nodejs.org/en/blog/release/v25.2.1">Node
25.2.1</a> – about <strong>26x</strong> times faster
than Python!</p>
<pre tabindex="0"><code>$ time node foo.js
100000: 9448038 tasks/sec
200000: 11555322 tasks/sec
300000: 18286318 tasks/sec
400000: 10017217 tasks/sec
500000: 12587060 tasks/sec
600000: 14198956 tasks/sec
700000: 13294620 tasks/sec
800000: 12045403 tasks/sec
900000: 11135513 tasks/sec
1000000: 13577663 tasks/sec
node foo.js 0.82s user 0.10s system 185% cpu 0.496 total
</code></pre><p>But it runs even faster on <a href="https://bun.com/blog/bun-v1.3.3">Bun 1.3.3</a> –
about <strong>36x</strong> times faster than Python!</p>
<pre tabindex="0"><code>$ time bun foo.js
100000: 9771222 tasks/sec
200000: 13388075 tasks/sec
300000: 13242548 tasks/sec
400000: 13130144 tasks/sec
500000: 16530496 tasks/sec
600000: 16979009 tasks/sec
700000: 16781272 tasks/sec
800000: 17098919 tasks/sec
900000: 17111784 tasks/sec
1000000: 18288515 tasks/sec
bun foo.js 0.37s user 0.02s system 111% cpu 0.353 total
</code></pre><p>I’m sure other languages perform both better and worse, but this gives us some
nice ideas of where we stand relative to some useful production programming
languages. There is clearly room to grow, some potential low-hanging fruit, and
known features such as supporting native threads that could be a big
improvement to the status quo!</p>
<p>PRs welcome!</p>
Cosine FizzBuzz
https://re.factorcode.org/2025/11/cosine-fizzbuzz.html
Sat, 22 Nov 2025 08:00:00 -0700https://re.factorcode.org/2025/11/cosine-fizzbuzz.html<p>After revisiting <a href="https://re.factorcode.org/2011/08/fizzbuzz.html">FizzBuzz</a> yesterday to discuss a
<a href="https://re.factorcode.org/2025/11/lazy-fizzbuzz.html">Lazy FizzBuzz</a> using infinite lazy lists, I
thought I would not return to the subject for awhile. Apparently, I was wrong!</p>
<p><a href="https://susam.net/about.html">Susam Pal</a> just wrote a really fun article about
<a href="https://susam.net/fizz-buzz-with-cosines.html">Solving Fizz Buzz with
Cosines</a>:</p>
<blockquote>
<p>We define a set of four functions { <code>s0</code>, <code>s1</code>, <code>s2</code>, <code>s3</code> } for
integers <code>n</code> by:</p>
<p><code>s0(n) = n</code></p>
<p><code>s1(n) = Fizz</code></p>
<p><code>s2(n) = Buzz</code></p>
<p><code>s3(n) = FizzBuzz</code></p>
</blockquote>
<p>And from that, they derive a formula which is essentially a finite <a href="https://en.wikipedia.org/wiki/Fourier_series">Fourier
series</a> for computing the <em>nth</em>
value in the <em>FizzBuzz</em> sequence, showing a nice fixed periodic cycling across
<code>n mod 15</code>, resolving at each value of <code>n</code> to either the integers <code>0, 1, 2, 3</code>:</p>
<p>
<img src="https://re.factorcode.org/images/2025-11-22-cosine-fizzbuzz.png" alt="" width="848" height="644" />
</p>
<p>I recommend reading the whole article, but I will jump to an implementation of
the formula in <a href="https://factorcode.org">Factor</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">fizzbuzz</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">val</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> 11/15
</span></span><span class="line"><span class="cl"> 2/3 n <span class="nb">* </span>pi <span class="nb">* </span>cos 2/3 <span class="nb">* +
</span></span></span><span class="line"><span class="cl"> 2/5 n <span class="nb">* </span>pi <span class="nb">* </span>cos 4/5 <span class="nb">* +
</span></span></span><span class="line"><span class="cl"> 4/5 n <span class="nb">* </span>pi <span class="nb">* </span>cos <span class="nb">+ </span>round <span class="nb">>integer
</span></span></span><span class="line"><span class="cl"> { n <span class="s">"Fizz"</span> <span class="s">"Buzz"</span> <span class="s">"FizzBuzz"</span> } <span class="nb">nth </span><span class="k">;
</span></span></span></code></pre></div><p>And we can use that to compute the first few values in the sequence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1 </span>..= <span class="m">100 </span>[ fizzbuzz <span class="m">. </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"><span class="m">1
</span></span></span><span class="line"><span class="cl"><span class="m">2
</span></span></span><span class="line"><span class="cl"><span class="s">"Fizz"</span>
</span></span><span class="line"><span class="cl"><span class="m">4
</span></span></span><span class="line"><span class="cl"><span class="s">"Buzz"</span>
</span></span><span class="line"><span class="cl"><span class="s">"Fizz"</span>
</span></span><span class="line"><span class="cl"><span class="m">7
</span></span></span><span class="line"><span class="cl"><span class="m">8
</span></span></span><span class="line"><span class="cl"><span class="s">"Fizz"</span>
</span></span><span class="line"><span class="cl"><span class="s">"Buzz"</span>
</span></span><span class="line"><span class="cl"><span class="m">11
</span></span></span><span class="line"><span class="cl"><span class="s">"Fizz"</span>
</span></span><span class="line"><span class="cl"><span class="m">13
</span></span></span><span class="line"><span class="cl"><span class="m">14
</span></span></span><span class="line"><span class="cl"><span class="s">"FizzBuzz"</span>
</span></span><span class="line"><span class="cl"><span class="m">16
</span></span></span><span class="line"><span class="cl"><span class="m">17
</span></span></span><span class="line"><span class="cl"><span class="s">"Fizz"</span>
</span></span><span class="line"><span class="cl"><span class="m">19
</span></span></span><span class="line"><span class="cl"><span class="s">"Buzz"</span>
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>Or, even some arbitrary values in the sequence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">67 </span>fizzbuzz <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">67
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">9,999,999 </span>fizzbuzz <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"Fizz"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10,000,000 </span>fizzbuzz <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"Buzz"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1,234,567,890 </span>fizzbuzz <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"FizzBuzz"</span>
</span></span></code></pre></div><p>Thats even more fun than using <a href="https://docs.factorcode.org/content/article-lists.lazy.html">lazy
lists</a>!</p>
Lazy FizzBuzz
https://re.factorcode.org/2025/11/lazy-fizzbuzz.html
Fri, 21 Nov 2025 08:00:00 -0700https://re.factorcode.org/2025/11/lazy-fizzbuzz.html<p>I wrote about <a href="https://re.factorcode.org/2011/08/fizzbuzz.html">FizzBuzz</a> many years ago. It’s a
silly programming task often cited and even
<a href="https://rosettacode.org/wiki/FizzBuzz">included</a> on
<a href="https://rosettacode.org">RosettaCode</a>. The task is described as:</p>
<blockquote>
<p>Write a program that prints the integers from <code>1</code> to <code>100</code> (inclusive).</p>
<p>But:</p>
<ul>
<li>for multiples of three, print <code>"Fizz"</code> instead of the number;</li>
<li>for multiples of five, print <code>"Buzz"</code> instead of the number;</li>
<li>for multiples of both three and five, print <code>"FizzBuzz"</code> instead of the number.</li>
</ul>
</blockquote>
<p>This has been solved <a href="https://en.wikipedia.org/wiki/Ad_nauseam">ad nauseum</a>,
but a few days ago <a href="https://evanhahn.com/">Evan Hawn</a> wrote about solving <a href="https://evanhahn.com/fizz-buzz-without-conditionals-or-booleans/">Fizz
Buzz without conditionals or
booleans</a>
using <a href="https://python.org">Python</a> and the
<a href="https://docs.python.org/3/library/itertools.html#itertools.cycle">itertools.cycle</a>
function to create an infinitely iterable solution.</p>
<p>Let’s build this in <a href="https://factorcode.org">Factor</a>!</p>
<p>There are several ways to implement this, including
<a href="https://docs.factorcode.org/content/article-generators.html">generators</a>,
but we will be using the <a href="https://docs.factorcode.org/content/article-lists.lazy.html">lists.lazy
vocabulary</a> to
provide a <em>lazy</em> and <em>infinite</em> stream of values. In particular, by
combining a stream of integers with a cycle of <code>"Fizz"</code> and a cycle of
<code>"Buzz"</code>.</p>
<p>The <a href="https://docs.factorcode.org/content/article-lists.circular.html">lists.circular
vocabulary</a>
extends <a href="https://docs.factorcode.org/content/article-circular.html">circular
sequences</a> to
support the <a href="https://docs.factorcode.org/content/article-lists.html">lists
protocol</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">lists.circular</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1 2 3 </span>} <circular> <span class="m">10 </span>ltake list>array <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">1 2 3 1 2 3 1 2 3 1 </span>}
</span></span></code></pre></div><p>Using that, we can create an <em>infinite</em> FizzBuzz list:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">lfizzbuzz</span> <span class="nf">( -- </span><span class="nv">list</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span>lfrom
</span></span><span class="line"><span class="cl"> { <span class="s">""</span> <span class="s">""</span> <span class="s">"Fizz"</span> } <circular>
</span></span><span class="line"><span class="cl"> { <span class="s">""</span> <span class="s">""</span> <span class="s">""</span> <span class="s">""</span> <span class="s">"Buzz"</span> } <circular>
</span></span><span class="line"><span class="cl"> lzip [ <span class="nb">concat </span>] lmap-lazy lzip <span class="k">;
</span></span></span></code></pre></div><p>We can print out the first few values quite simply:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> lfizzbuzz <span class="m">20 </span>ltake [ <span class="nb">first2 </span><span class="s">"%2d: %s\n"</span> printf ] leach
</span></span><span class="line"><span class="cl"> 1:
</span></span><span class="line"><span class="cl"> 2:
</span></span><span class="line"><span class="cl"> 3: Fizz
</span></span><span class="line"><span class="cl"> 4:
</span></span><span class="line"><span class="cl"> 5: Buzz
</span></span><span class="line"><span class="cl"> 6: Fizz
</span></span><span class="line"><span class="cl"> 7:
</span></span><span class="line"><span class="cl"> 8:
</span></span><span class="line"><span class="cl"> 9: Fizz
</span></span><span class="line"><span class="cl">10: Buzz
</span></span><span class="line"><span class="cl">11:
</span></span><span class="line"><span class="cl">12: Fizz
</span></span><span class="line"><span class="cl">13:
</span></span><span class="line"><span class="cl">14:
</span></span><span class="line"><span class="cl">15: FizzBuzz
</span></span><span class="line"><span class="cl">16:
</span></span><span class="line"><span class="cl">17:
</span></span><span class="line"><span class="cl">18: Fizz
</span></span><span class="line"><span class="cl">19:
</span></span><span class="line"><span class="cl">20: Buzz
</span></span></code></pre></div><p>And if we wanted a more traditional stream alternating between numbers and
labels:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> lfizzbuzz <span class="m">100 </span>ltake [ <span class="nb">first2 </span>[ <span class="nb">nip </span>] <span class="nb">unless-empty </span><span class="m">. </span>] leach
</span></span><span class="line"><span class="cl"><span class="m">1
</span></span></span><span class="line"><span class="cl"><span class="m">2
</span></span></span><span class="line"><span class="cl"><span class="s">"Fizz"</span>
</span></span><span class="line"><span class="cl"><span class="m">4
</span></span></span><span class="line"><span class="cl"><span class="s">"Buzz"</span>
</span></span><span class="line"><span class="cl"><span class="s">"Fizz"</span>
</span></span><span class="line"><span class="cl"><span class="m">7
</span></span></span><span class="line"><span class="cl"><span class="m">8
</span></span></span><span class="line"><span class="cl"><span class="s">"Fizz"</span>
</span></span><span class="line"><span class="cl"><span class="s">"Buzz"</span>
</span></span><span class="line"><span class="cl"><span class="m">11
</span></span></span><span class="line"><span class="cl"><span class="s">"Fizz"</span>
</span></span><span class="line"><span class="cl"><span class="m">13
</span></span></span><span class="line"><span class="cl"><span class="m">14
</span></span></span><span class="line"><span class="cl"><span class="s">"FizzBuzz"</span>
</span></span><span class="line"><span class="cl"><span class="m">16
</span></span></span><span class="line"><span class="cl"><span class="m">17
</span></span></span><span class="line"><span class="cl"><span class="s">"Fizz"</span>
</span></span><span class="line"><span class="cl"><span class="m">19
</span></span></span><span class="line"><span class="cl"><span class="s">"Buzz"</span>
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>While not the ultimate <a href="https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition">FizzBuzz Enterprise
Edition</a>,
this seems like a fun way to improve upon the simple <em>meant for whiteboards</em>
implementation that is most often shared.</p>
Lorem Ipsum
https://re.factorcode.org/2025/11/lorem-ipsum.html
Thu, 20 Nov 2025 08:00:00 -0700https://re.factorcode.org/2025/11/lorem-ipsum.html<p><a href="https://en.wikipedia.org/wiki/Lorem_ipsum">Lorem ipsum</a> is a type of
placeholder text that can be used in graphic design or web development. The
most common form of it will often begin like this paragraph:</p>
<blockquote>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</blockquote>
<p>I wanted to make a program to generate believable <em>lorem ipsum</em> text using
<a href="https://factorcode.org">Factor</a>.</p>
<p>We start by defining a bunch of possible words:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">words</span> qw{
</span></span><span class="line"><span class="cl"> a ab accusamus accusantium ad adipisci alias aliquam aliquid
</span></span><span class="line"><span class="cl"> amet animi aperiam architecto asperiores aspernatur
</span></span><span class="line"><span class="cl"> assumenda <span class="nb">at </span>atque aut autem beatae blanditiis commodi
</span></span><span class="line"><span class="cl"> consectetur consequatur consequuntur corporis corrupti culpa
</span></span><span class="line"><span class="cl"> cum cumque cupiditate debitis delectus deleniti deserunt
</span></span><span class="line"><span class="cl"> dicta dignissimos distinctio dolor dolore dolorem doloremque
</span></span><span class="line"><span class="cl"> dolores doloribus dolorum ducimus ea eaque earum eius
</span></span><span class="line"><span class="cl"> eligendi enim eos <span class="nb">error </span>esse est et eum eveniet ex excepturi
</span></span><span class="line"><span class="cl"> exercitationem expedita explicabo facere facilis fuga fugiat
</span></span><span class="line"><span class="cl"> fugit harum hic id illo illum impedit in incidunt inventore
</span></span><span class="line"><span class="cl"> ipsa ipsam ipsum iste itaque iure iusto labore laboriosam
</span></span><span class="line"><span class="cl"> laborum laudantium libero magnam magni maiores maxime minima
</span></span><span class="line"><span class="cl"> minus modi molestiae molestias mollitia nam natus
</span></span><span class="line"><span class="cl"> necessitatibus nemo neque nesciunt nihil nisi nobis non
</span></span><span class="line"><span class="cl"> nostrum nulla numquam obcaecati odio odit officia officiis
</span></span><span class="line"><span class="cl"> omnis optio pariatur perferendis perspiciatis placeat porro
</span></span><span class="line"><span class="cl"> possimus praesentium provident quae quaerat quam quas quasi
</span></span><span class="line"><span class="cl"> qui quia quibusdam quidem quis quisquam quo quod quos
</span></span><span class="line"><span class="cl"> ratione recusandae reiciendis <span class="nb">rem </span>repellat repellendus
</span></span><span class="line"><span class="cl"> reprehenderit repudiandae rerum saepe sapiente sed sequi
</span></span><span class="line"><span class="cl"> similique sint sit soluta sunt suscipit tempora tempore
</span></span><span class="line"><span class="cl"> temporibus tenetur totam ullam unde ut vel velit veniam
</span></span><span class="line"><span class="cl"> veritatis vero vitae voluptas voluptate voluptatem
</span></span><span class="line"><span class="cl"> voluptates voluptatibus voluptatum
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>We then use these to build a random <em>ipsum</em> sentence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-sentence</span> <span class="nf">( -- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">2 </span>..= <span class="m">5 </span>random [
</span></span><span class="line"><span class="cl"> words <span class="m">3 </span>..= <span class="m">12 </span>random sample <span class="s">" "</span> <span class="nb">join
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">replicate </span><span class="s">", "</span> <span class="nb">join
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span><span class="nb">over </span>[ ch>upper ] <span class="nb">change-nth </span><span class="s">"?."</span> random <span class="nb">suffix </span><span class="k">;
</span></span></span></code></pre></div><p>Then build a random <em>ipsum</em> paragraph from sentences:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-paragraph</span> <span class="nf">( -- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">2 </span>..= <span class="m">4 </span>random [ random-sentence ] <span class="nb">replicate </span><span class="s">" "</span> <span class="nb">join </span><span class="k">;
</span></span></span></code></pre></div><p>We can define the <em>initial paragraph</em> above:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">initial-paragraph</span> <span class="s">"\
</span></span></span><span class="line"><span class="cl"><span class="s">Lorem ipsum dolor sit amet, consectetur adipisicing \
</span></span></span><span class="line"><span class="cl"><span class="s">elit, sed do eiusmod tempor incididunt ut labore et \
</span></span></span><span class="line"><span class="cl"><span class="s">dolore magna aliqua. Ut enim ad minim veniam, quis \
</span></span></span><span class="line"><span class="cl"><span class="s">nostrud exercitation ullamco laboris nisi ut aliquip ex \
</span></span></span><span class="line"><span class="cl"><span class="s">ea commodo consequat. Duis aute irure dolor in \
</span></span></span><span class="line"><span class="cl"><span class="s">reprehenderit in voluptate velit esse cillum dolore eu \
</span></span></span><span class="line"><span class="cl"><span class="s">fugiat nulla pariatur. Excepteur sint occaecat cupidatat \
</span></span></span><span class="line"><span class="cl"><span class="s">non proident, sunt in culpa qui officia deserunt mollit \
</span></span></span><span class="line"><span class="cl"><span class="s">anim id est laborum."</span>
</span></span></code></pre></div><p>And use it to make random <em>ipsum</em> paragraphs, starting with the <em>initial</em> one:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-paragraphs</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <iota> [
</span></span><span class="line"><span class="cl"> <span class="nb">zero? </span>[ initial-paragraph ] [ random-paragraph ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map </span><span class="s">"\n"</span> <span class="nb">join </span><span class="k">;
</span></span></span></code></pre></div><p>Or even generate a list of random <em>ipsum</em> words, understanding that
<a href="https://docs.factorcode.org/content/word-sample,random.html">sample</a> can’t
generate more samples than the length of the sequence being sampled from:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">random-words</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> words <span class="nb">length </span>:> w
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> n [ words <span class="nb">over </span>w min sample % w [-] ] until-zero
</span></span><span class="line"><span class="cl"> ] { } make <span class="k">;
</span></span></span></code></pre></div><p>We can make a command-line interface using the <a href="https://re.factorcode.org/2024/05/argument-parser.html">argument
parser</a> to return words, sentence, or paragraph:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">OPTIONS</span> {
</span></span><span class="line"><span class="cl"> T{ option
</span></span><span class="line"><span class="cl"> { name <span class="s">"--w"</span> }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Generate some lorem ipsum words"</span> }
</span></span><span class="line"><span class="cl"> { #args <span class="m">1 </span>}
</span></span><span class="line"><span class="cl"> { type <span class="nb">integer </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ option
</span></span><span class="line"><span class="cl"> { name <span class="s">"--s"</span> }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Generate a lorem ipsum sentence"</span> }
</span></span><span class="line"><span class="cl"> { const <span class="no">t </span>}
</span></span><span class="line"><span class="cl"> { default <span class="no">f </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ option
</span></span><span class="line"><span class="cl"> { name <span class="s">"--p"</span> }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Generate a lorem ipsum paragraph"</span> }
</span></span><span class="line"><span class="cl"> { const <span class="no">t </span>}
</span></span><span class="line"><span class="cl"> { default <span class="no">f </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">[</span>
</span></span><span class="line"><span class="cl"> OPTIONS [
</span></span><span class="line"><span class="cl"> <span class="s">"w"</span> <span class="nb">get </span>[ random-words <span class="nb">print </span>] <span class="nb">when*
</span></span></span><span class="line"><span class="cl"> <span class="s">"s"</span> <span class="nb">get </span>[ random-sentence <span class="nb">print </span>] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> <span class="s">"p"</span> <span class="nb">get </span>[ random-paragraph <span class="nb">print </span>] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> ] with-options
</span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><p>And that gives you automatic help text showing the available options:</p>
<pre tabindex="0"><code>$ ./factor -run=lorem-ipsum --help
Usage:
factor -run=lorem-ipsum [--help] [--w W] [--s] [--p]
Options:
--help show this help and exit
--w W Generate some lorem ipsum words
--s Generate a lorem ipsum sentence
--p Generate a lorem ipsum paragraph
</code></pre><p>We can test it by generating some words, a sentence, and a paragraph:</p>
<pre tabindex="0"><code>$ ./factor -run=lorem-ipsum --w 10
vero eos quos optio magni soluta nulla delectus voluptas neque
$ ./factor -run=lorem-ipsum --s
Totam dicta laborum perferendis unde voluptas, culpa dignissimos odio
distinctio rem eius, tempora harum corporis accusamus.
$ ./factor -run=lorem-ipsum --p
Quaerat maiores veniam minus reprehenderit architecto numquam mollitia earum,
natus assumenda eius cumque minima sint magni accusantium facere, eius aperiam
explicabo molestias voluptatibus aspernatur maiores assumenda, nulla illo
doloremque voluptatum excepturi accusamus porro officiis tempore molestiae
saepe, iusto quibusdam explicabo obcaecati saepe quasi voluptate? Velit libero
tempore in nobis ratione nisi laborum rerum natus ipsam, aperiam placeat
laborum delectus dolor ab dolores itaque. Fuga maxime culpa quae adipisci, modi
quod distinctio ipsam, et vero natus consequuntur neque placeat saepe quam
perferendis, voluptate nemo ducimus ullam recusandae iusto laboriosam iure
temporibus sed saepe, optio dignissimos dolor modi accusamus quod culpa ab? Ad
dolore dignissimos, perferendis accusamus ducimus fuga eveniet a ut.
</code></pre><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/lorem-ipsum/lorem-ipsum.factor">GitHub</a>.</p>
Cardinal Direction
https://re.factorcode.org/2025/11/cardinal-direction.html
Wed, 19 Nov 2025 08:00:00 -0700https://re.factorcode.org/2025/11/cardinal-direction.html<p><a href="https://en.wikipedia.org/wiki/Cardinal_direction">Cardinal direction</a>
describes points on a compass — North (<code>N</code>), East (<code>E</code>), South (<code>S</code>),
and West (<code>W</code>).</p>
<p>In addition to those, there are also 4 <em>intercardinal directions</em> (Northwest
or <code>NW</code>, <code>NE</code>, <code>SW</code>, <code>SE</code>), 8 <em>secondary intercardinal directions</em>
(North-Northwest or <code>NNW</code>, <code>WNW</code>, <code>NNE</code>, <code>ENE</code>, <code>WSW</code>, <code>SSW</code>,
<code>ESE</code>, <code>SSE</code>), as well as 16 <em>tertiary intercardinal directions</em>
(Northwest-by-North or <code>NWbN</code>, etc.).</p>
<p>In fact, you can see all the <a href="https://en.wikipedia.org/wiki/Points_of_the_compass">points of the
compass</a> divided into 32
textual directions:</p>
<p>
<img src="https://re.factorcode.org/images/2025-11-19-cardinal-direction.png" alt="" width="640" height="640" />
</p>
<p>I like puzzles and sometimes those come from <a href="https://en.wikipedia.org/wiki/Code_golf">code
golfing</a>. I stumbled across two
symmetric challenges on the <a href="https://codegolf.stackexchange.com">Code Golf and Coding Challenges Stack
Exchange</a>:</p>
<ul>
<li><a href="https://codegolf.stackexchange.com/questions/54755/convert-a-point-of-the-compass-to-degrees">Convert a Point of the Compass to Degrees</a></li>
<li><a href="https://codegolf.stackexchange.com/questions/21927/convert-degrees-to-one-of-the-32-points-of-the-compass">Convert degrees to one of the 32 points of the compass</a></li>
</ul>
<p>I thought it would be a good task to code in <a href="https://factorcode.org">Factor</a>.</p>
<p>We start by enumerating the descriptive names for all 32 points of the compass.
For convenience we use the <a href="https://docs.factorcode.org/content/article-qw.html">quoted
words</a> vocabulary.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">directions</span> qw{
</span></span><span class="line"><span class="cl"> N NbE NNE NEbN NE NEbE ENE EbN
</span></span><span class="line"><span class="cl"> E EbS ESE SEbE SE SEbS SSE SbE
</span></span><span class="line"><span class="cl"> S SbW SSW SWbS SW SWbW WSW WbS
</span></span><span class="line"><span class="cl"> W WbN WNW NWbW NW NWbN NNW NbW
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Then, parsing a <em>compass</em> degrees to a textual name involves rounding to the
nearest 32-point:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">compass>string</span> <span class="nf">( </span><span class="nv">compass</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> 360/32 <span class="nb">/ </span>round <span class="nb">>integer </span><span class="m">32 </span><span class="nb">mod </span>directions <span class="nb">nth </span><span class="k">;
</span></span></span></code></pre></div><p>We can use some test cases from the challenges to check that it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="s">"N"</span> } [ <span class="m">0 </span> compass>string ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"NNE"</span> } [ <span class="m">23.97 </span>compass>string ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"NEbN"</span> } [ <span class="m">33.7 </span> compass>string ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"ENE"</span> } [ <span class="m">73.12 </span>compass>string ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"EbN"</span> } [ <span class="m">73.13 </span>compass>string ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"SWbS"</span> } [ <span class="m">219 </span> compass>string ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"W"</span> } [ <span class="m">275 </span> compass>string ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"WbN"</span> } [ <span class="m">276 </span> compass>string ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"WNW"</span> } [ <span class="m">287 </span> compass>string ] unit-test
</span></span></code></pre></div><p>And the reverse converts the name back to <em>compass</em> degrees by grabbing the
index and multiplying:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">string>compass</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">compass</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> directions <span class="nb">index </span>360/32 <span class="nb">* </span><span class="k">;
</span></span></span></code></pre></div><p>It might not be the shortest solution, but it works and it was fun to build!</p>
Flood Fill
https://re.factorcode.org/2025/11/flood-fill.html
Tue, 18 Nov 2025 08:00:00 -0700https://re.factorcode.org/2025/11/flood-fill.html<p>Yesterday, <a href="https://mathspp.com/about">Rodrigo Girão Serrão</a> wrote an article
about implementing the <a href="https://mathspp.com/blog/floodfill-algorithm-in-python">Floodfill algorithm in
Python</a>. He included a
Javascript demonstration you can click on, as well as a step-by-step example at
the end of his blog post to go over how <a href="https://en.wikipedia.org/wiki/Flood_fill">flood
fill</a> works.</p>
<p>We are going to implement this in <a href="https://factorcode.org">Factor</a> and then
extend the <a href="https://docs.factorcode.org/content/article-images.viewer.html">images.viewer
vocabulary</a>:</p>
<p>
<img src="https://re.factorcode.org/images/2025-11-18-flood-fill.png" alt="" width="624" height="652" />
</p>
<p>When working with <em>bitmap pixel data</em>, we typically store colors using
integers in the range <code>[0..255]</code>. We can generate a random color with 4
bytes by simply using
<a href="https://docs.factorcode.org/content/word-replicate,sequences.html">replicate</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">random-color</span> <span class="nf">( -- </span><span class="nv">color</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">4 </span>[ <span class="m">255 </span>random ] B{ } <span class="nb">replicate-as </span><span class="k">;
</span></span></span></code></pre></div><p>This allows us to implement the <em>flood fill four-way</em> algorithm:</p>
<ol>
<li>Start from a specified pixel in an image.</li>
<li>Choose a random but different color to assign.</li>
<li>If the pixel is not the initial color, you’re done.</li>
<li>If the pixel is, then change it’s color.</li>
<li>For each surrounding pixel, continue from step 3.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">neighbors</span> { { <span class="m">1 0 </span>} { <span class="m">0 1 </span>} { <span class="m">-1 0 </span>} { <span class="m">0 -1 </span>} }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">floodfill</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nv">image</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> image dim>> <span class="nb">first2 </span>:> <span class="nf">( </span><span class="nv">w</span> <span class="nv">h</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ x <span class="m">0 </span><span class="nb">>= </span>] [ x w <span class="nb">< </span>]
</span></span><span class="line"><span class="cl"> [ y <span class="m">0 </span><span class="nb">>= </span>] [ y h <span class="nb">< </span>]
</span></span><span class="line"><span class="cl"> } 0&& [
</span></span><span class="line"><span class="cl"> x y image pixel-at :> initial
</span></span><span class="line"><span class="cl"> <span class="no">f </span>[ <span class="nb">drop </span>random-color <span class="nb">dup </span>initial <span class="nb">= </span>] <span class="nb">loop </span>:> color
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> color x y image set-pixel-at
</span></span><span class="line"><span class="cl"> V{ { x y } } :> queue
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ queue <span class="nb">empty? </span>] [
</span></span><span class="line"><span class="cl"> queue <span class="nb">pop first2 </span>:> <span class="nf">( </span><span class="nv">tx</span> <span class="nv">ty</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> neighbors [
</span></span><span class="line"><span class="cl"> <span class="nb">first2 </span>:> <span class="nf">( </span><span class="nv">dx</span> <span class="nv">dy</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> tx dx <span class="nb">+ </span>:> nx
</span></span><span class="line"><span class="cl"> ty dy <span class="nb">+ </span>:> ny
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ nx <span class="m">0 </span><span class="nb">>= </span>] [ nx w <span class="nb">< </span>]
</span></span><span class="line"><span class="cl"> [ ny <span class="m">0 </span><span class="nb">>= </span>] [ ny h <span class="nb">< </span>]
</span></span><span class="line"><span class="cl"> [ nx ny image pixel-at initial <span class="nb">= </span>]
</span></span><span class="line"><span class="cl"> } 0&& [
</span></span><span class="line"><span class="cl"> color nx ny image set-pixel-at
</span></span><span class="line"><span class="cl"> { nx ny } queue <span class="nb">push
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">until </span><span class="no">t
</span></span></span><span class="line"><span class="cl"> ] [ <span class="no">f </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p><em>Note: as implemented, we change every pixel that matches the first click to a
different color. It might be more aesthetic to allow for anti-aliasing to
adjust colors that are fairly close to the original color.</em></p>
<p>Now, we’ll extend the
<a href="https://docs.factorcode.org/content/word-image-gadget,images.viewer.html">image-gadget</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">floodfill-gadget</span> < <span class="nc">image-gadget</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><floodfill-gadget></span> <span class="nf">( </span><span class="nv">image</span> <span class="nf">-- </span><span class="nv">gadget</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> floodfill-gadget new-image-gadget* <span class="k">;
</span></span></span></code></pre></div><p>We implement a <em>click handler</em> that performs a <em>flood fill</em> and if it changed
the image, cleans up the <a href="https://open.gl/textures">texture object</a> and
re-renders the gadget.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">on-click</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> gadget hand-rel <span class="nb">first2 </span>[ gl-scale <span class="nb">>integer </span>] <span class="nb">bi@ </span>:> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> x y gadget image>> floodfill [
</span></span><span class="line"><span class="cl"> gadget delete-current-texture
</span></span><span class="line"><span class="cl"> gadget relayout-1
</span></span><span class="line"><span class="cl"> ] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><p>That word is assigned as a gesture on <code>button-up</code> mouse clicks:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">floodfill-gadget <span class="s">"gestures"</span> <span class="no">f </span>{
</span></span><span class="line"><span class="cl"> { T{ button-up { # <span class="m">1 </span>} } on-click }
</span></span><span class="line"><span class="cl">} define-command-map
</span></span></code></pre></div><p>And, for convenience, make a <em>main window</em> word to launch the user interface
with an example image:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">MAIN-WINDOW: floodfill-window { { title <span class="s">"Floodfill"</span> } }
</span></span><span class="line"><span class="cl"> <span class="s">"vocab:floodfill/logo.png"</span> <floodfill-gadget> >>gadgets <span class="k">;
</span></span></span></code></pre></div><p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/floodfill/floodfill.factor">GitHub</a>.</p>
Parsing Chemistry
https://re.factorcode.org/2025/10/parsing-chemistry.html
Fri, 24 Oct 2025 08:00:00 -0700https://re.factorcode.org/2025/10/parsing-chemistry.html<p>In <a href="https://python.org">Python</a>, the
<a href="https://pypi.org/project/chemparse/">chemparse</a> project is available as a
“<em>lightweight package for parsing chemical formula strings into python
dictionaries</em>” mapping chemical elements to numeric counts.</p>
<p>It supports parsing several variants of formula such as:</p>
<ul>
<li>simple formulas like <code>"H2O"</code></li>
<li>fractional stoichiometry like <code>"C1.5O3"</code></li>
<li>groups such as <code>"(CH3)2"</code></li>
<li>nested groups such as <code>"((CH3)2)3"</code></li>
<li>square brackets such as <code>"K4[Fe(SCN)6]"</code></li>
</ul>
<p>I thought it would fun to build a similar functionality using
<a href="https://factorcode.org">Factor</a>.</p>
<p>We are going to be using the
<a href="https://docs.factorcode.org/content/article-peg.ebnf.html">EBNF</a> syntax
support to more simply write a <em>parsing expression grammar</em>. As is often the
most useful way to implement things, we break it down into steps. We can parse
a symbol as one or two letters, a number as an integer or float, and then a
pair which is a symbol with an optional number prefix and postfix.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">EBNF: split-formula [=[
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">symbol <span class="nb">= </span>[A-Z] [a-z]? => [[ <span class="nb">sift >string </span>]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">number = </span>[0-9]+ { <span class="s">"."</span> [0-9]+ }? { { <span class="s">"e"</span> | <span class="s">"E"</span> } { <span class="s">"+"</span> | <span class="s">"-"</span> }? [0-9]+ }?
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> => [[ <span class="nb">first3 </span>[ <span class="nb">concat </span>] <span class="nb">bi@ </span><span class="s">""</span> <span class="nb">3append-as </span>string>number ]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">pair </span> <span class="nb">= number? </span>{ symbol | <span class="s">"("</span>~ pair+ <span class="s">")"</span>~ | <span class="s">"["</span>~ pair+ <span class="s">"]"</span>~ } <span class="nb">number?
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> => [[ <span class="nb">first3 swapd </span>[ <span class="m">1 </span><span class="nb">or </span>] <span class="nb">bi@ * 2array </span>]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">pairs <span class="nb">= </span>pair+
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">]=]
</span></span></code></pre></div><p>We can test that this works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"H2O"</span> split-formula <span class="m">.
</span></span></span><span class="line"><span class="cl">V{ { <span class="s">"H"</span> <span class="m">2 </span>} { <span class="s">"O"</span> <span class="m">1 </span>} }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"(CH3)2"</span> split-formula <span class="m">.
</span></span></span><span class="line"><span class="cl">V{ { V{ { <span class="s">"C"</span> <span class="m">1 </span>} { <span class="s">"H"</span> <span class="m">3 </span>} } <span class="m">2 </span>} }
</span></span></code></pre></div><p>But we need to recursively flatten these into an
<a href="https://docs.factorcode.org/content/article-assocs.html">assoc</a>, mapping
element to count.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">flatten-formula</span> <span class="nf">( </span><span class="nv">elt</span> <span class="nv">n</span> <span class="nv">assoc</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ [ <span class="nb">first2 </span>] [ <span class="nb">* </span>] <span class="nb">bi* </span>] <span class="nb">dip pick string?
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">swapd at+ </span>] [ '[ _ _ flatten-formula ] <span class="nb">each </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>And combine those two steps to parse a formula:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-formula</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> split-formula H{ } <span class="nb">clone </span>[
</span></span><span class="line"><span class="cl"> '[ <span class="m">1 </span>_ flatten-formula ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div><p>We can now test that this works with a few <a href="https://docs.factorcode.org/content/article-tools.test.html">unit
tests</a> that show
each of the features we hoped to support:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ H{ { <span class="s">"H"</span> <span class="m">2 </span>} { <span class="s">"O"</span> <span class="m">1 </span>} } } [ <span class="s">"H2O"</span> parse-formula ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ H{ { <span class="s">"C"</span> <span class="m">1.5 </span>} { <span class="s">"O"</span> <span class="m">3 </span>} } } [ <span class="s">"C1.5O3"</span> parse-formula ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ H{ { <span class="s">"C"</span> <span class="m">2 </span>} { <span class="s">"H"</span> <span class="m">6 </span>} } } [ <span class="s">"(CH3)2"</span> parse-formula ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ H{ { <span class="s">"C"</span> <span class="m">6 </span>} { <span class="s">"H"</span> <span class="m">18 </span>} } } [ <span class="s">"((CH3)2)3"</span> parse-formula ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ H{ { <span class="s">"K"</span> <span class="m">4 </span>} { <span class="s">"Fe"</span> <span class="m">1 </span>} { <span class="s">"S"</span> <span class="m">6 </span>} { <span class="s">"C"</span> <span class="m">6 </span>} { <span class="s">"N"</span> <span class="m">6 </span>} } }
</span></span><span class="line"><span class="cl">[ <span class="s">"K4[Fe(SCN)6]"</span> parse-formula ] unit-test
</span></span></code></pre></div><p>This is available in my
<a href="https://github.com/mrjbq7/re-factor/blob/master/chemistry/chemistry.factor">GitHub</a>.</p>
Split Lines
https://re.factorcode.org/2025/10/split-lines.html
Sun, 12 Oct 2025 08:00:00 -0700https://re.factorcode.org/2025/10/split-lines.html<p><a href="https://yossarian.net/">William Woodruff</a> recently noticed that <a href="https://yossarian.net/til/post/python-s-splitlines-does-a-lot-more-than-just-newlines/">Python’s
splitlines does a lot more than just
newlines</a>:</p>
<blockquote>
<p>I always assumed that Python’s
<a href="https://docs.python.org/3/library/stdtypes.html#str.splitlines">str.splitlines()</a>
split strings by “<a href="https://docs.python.org/3/glossary.html#term-universal-newlines">universal newlines</a>”,
i.e., <code>\n</code>, <code>\r</code>, and <code>\r\n</code>.</p>
<p>But it turns out it does a lot more than that.</p>
</blockquote>
<p>The recent <a href="https://re.factorcode.org/2024/09/factor-0-100-now-available.html">Factor 0.100 release</a>
included a change to make the
<a href="https://docs.factorcode.org/content/word-split-lines,splitting.html">split-lines</a>
word split on <a href="https://github.com/factor/factor/commit/a525b01f25e2583cf818772095824d8ba3ff3cc7">unicode
linebreaks</a>
which matches the Python behavior.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"line1\nline2\rline3\r\nline4\vline5\x1dhello"</span>
</span></span><span class="line"><span class="cl"> split-lines <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"line1"</span> <span class="s">"line2"</span> <span class="s">"line3"</span> <span class="s">"line4"</span> <span class="s">"line5"</span> <span class="s">"hello"</span> }
</span></span></code></pre></div><p>These are considered line breaks:</p>
<table>
<thead>
<tr>
<th>Character</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>\n</code></td>
<td>Line Feed</td>
</tr>
<tr>
<td><code>\r</code></td>
<td>Carriage Return</td>
</tr>
<tr>
<td><code>\r\n</code></td>
<td>Carriage Return + Line Feed</td>
</tr>
<tr>
<td><code>\v</code></td>
<td>Line Tabulation</td>
</tr>
<tr>
<td><code>\f</code></td>
<td>Form Feed</td>
</tr>
<tr>
<td><code>\x1c</code></td>
<td>File Separator</td>
</tr>
<tr>
<td><code>\x1d</code></td>
<td>Group Separator</td>
</tr>
<tr>
<td><code>\x1e</code></td>
<td>Record Separator</td>
</tr>
<tr>
<td><code>\x85</code></td>
<td>Next Line (C1 Control Code)</td>
</tr>
<tr>
<td><code>\u002028</code></td>
<td>Line Separator</td>
</tr>
<tr>
<td><code>\u002029</code></td>
<td>Paragraph Separator</td>
</tr>
</tbody>
</table>
<p>This might be surprising – or just what you needed!</p>
Extensible Data Notation
https://re.factorcode.org/2025/10/extensible-data-notation.html
Sun, 05 Oct 2025 08:00:00 -0700https://re.factorcode.org/2025/10/extensible-data-notation.html<p>I wrote about the <a href="https://re.factorcode.org/2025/07/data-formats.html">Data Formats</a> support that comes
included in <a href="https://factorcode.org">Factor</a>. As I mentioned in that post,
there are many more that we could implement. One of those is <a href="https://github.com/edn-format/edn">Extensible Data
Notation</a> – also known as EDN – and comes
from the <a href="https://clojure.org">Clojure</a> community.</p>
<p>We can see a nice example of the EDN format in <a href="https://learnxinyminutes.com/edn/">Learn EDN in Y
minutes</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="c1">; Comments start with a semicolon.</span>
</span></span><span class="line"><span class="cl"><span class="c1">; Anything after the semicolon is ignored.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;;;;;;;;;;;;;;;;;;;</span>
</span></span><span class="line"><span class="cl"><span class="c1">;;; Basic Types ;;;</span>
</span></span><span class="line"><span class="cl"><span class="c1">;;;;;;;;;;;;;;;;;;;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">nil</span> <span class="c1">; also known in other languages as null</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">; Booleans</span>
</span></span><span class="line"><span class="cl"><span class="nv">true</span>
</span></span><span class="line"><span class="cl"><span class="nv">false</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">; Strings are enclosed in double quotes</span>
</span></span><span class="line"><span class="cl"><span class="s">"hungarian breakfast"</span>
</span></span><span class="line"><span class="cl"><span class="s">"farmer's cheesy omelette"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">; Characters are preceded by backslashes</span>
</span></span><span class="line"><span class="cl"><span class="sc">\g</span> <span class="sc">\r</span> <span class="sc">\a</span> <span class="sc">\c</span> <span class="sc">\e</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">; Keywords start with a colon. They behave like enums. Kind of</span>
</span></span><span class="line"><span class="cl"><span class="c1">; like symbols in Ruby.</span>
</span></span><span class="line"><span class="cl"><span class="ss">:eggs</span>
</span></span><span class="line"><span class="cl"><span class="ss">:cheese</span>
</span></span><span class="line"><span class="cl"><span class="ss">:olives</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">; Symbols are used to represent identifiers. </span>
</span></span><span class="line"><span class="cl"><span class="c1">; You can namespace symbols by using /. Whatever precedes / is</span>
</span></span><span class="line"><span class="cl"><span class="c1">; the namespace of the symbol.</span>
</span></span><span class="line"><span class="cl"><span class="nv">spoon</span>
</span></span><span class="line"><span class="cl"><span class="nv">kitchen/spoon</span> <span class="c1">; not the same as spoon</span>
</span></span><span class="line"><span class="cl"><span class="nv">kitchen/fork</span>
</span></span><span class="line"><span class="cl"><span class="nv">github/fork</span> <span class="c1">; you can't eat with this</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">; Integers and floats</span>
</span></span><span class="line"><span class="cl"><span class="mi">42</span>
</span></span><span class="line"><span class="cl"><span class="mf">3.14159</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">; Lists are sequences of values</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="ss">:bun</span> <span class="ss">:beef-patty</span> <span class="mi">9</span> <span class="s">"yum!"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">; Vectors allow random access</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="ss">:gelato</span> <span class="mi">1</span> <span class="mi">2</span> <span class="mi">-2</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">; Maps are associative data structures that associate the key with its value</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:eggs</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:lemon-juice</span> <span class="mf">3.5</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:butter</span> <span class="mi">1</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">; You're not restricted to using keywords as keys</span>
</span></span><span class="line"><span class="cl"><span class="p">{[</span><span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span><span class="p">]</span> <span class="s">"tell the people what she wore"</span>,
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="mi">5</span> <span class="mi">6</span> <span class="mi">7</span> <span class="mi">8</span><span class="p">]</span> <span class="s">"the more you see the more you hate"</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">; You may use commas for readability. They are treated as whitespace.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">; Sets are collections that contain unique elements.</span>
</span></span><span class="line"><span class="cl"><span class="o">#</span><span class="p">{</span><span class="ss">:a</span> <span class="ss">:b</span> <span class="mi">88</span> <span class="s">"huat"</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;;;;;;;;;;;;;;;;;;;;;;;</span>
</span></span><span class="line"><span class="cl"><span class="c1">;;; Tagged Elements ;;;</span>
</span></span><span class="line"><span class="cl"><span class="c1">;;;;;;;;;;;;;;;;;;;;;;;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">; EDN can be extended by tagging elements with # symbols.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">#</span><span class="nv">MyYelpClone/MenuItem</span> <span class="p">{</span><span class="ss">:name</span> <span class="s">"eggs-benedict"</span> <span class="ss">:rating</span> <span class="mi">10</span><span class="p">}</span>
</span></span></code></pre></div><p>Recently, I <a href="https://github.com/factor/factor/commit/b1f135b37eac6a0dfdad83f5c5d790c0118de6dd">implemented support for
EDN</a>,
originally using <a href="https://docs.factorcode.org/content/vocab-peg.html">Parsing Expression
Grammar</a> to do the parsing,
and then adding support for encoding Factor objects into EDN, and then
switching to a faster stream-based parsing approach.</p>
<p>This now allows us to parse that example above into:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> <span class="nb">null
</span></span></span><span class="line"><span class="cl"> <span class="no">t
</span></span></span><span class="line"><span class="cl"> <span class="no">f
</span></span></span><span class="line"><span class="cl"> <span class="s">"hungarian breakfast"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"farmer's cheesy omelette"</span>
</span></span><span class="line"><span class="cl"> <span class="m">103
</span></span></span><span class="line"><span class="cl"> <span class="m">114
</span></span></span><span class="line"><span class="cl"> <span class="m">97
</span></span></span><span class="line"><span class="cl"> <span class="m">99
</span></span></span><span class="line"><span class="cl"> <span class="m">101
</span></span></span><span class="line"><span class="cl"> T{ keyword { name <span class="s">"eggs"</span> } }
</span></span><span class="line"><span class="cl"> T{ keyword { name <span class="s">"cheese"</span> } }
</span></span><span class="line"><span class="cl"> T{ keyword { name <span class="s">"olives"</span> } }
</span></span><span class="line"><span class="cl"> T{ symbol { name <span class="s">"spoon"</span> } }
</span></span><span class="line"><span class="cl"> T{ symbol { name <span class="s">"kitchen/spoon"</span> } }
</span></span><span class="line"><span class="cl"> T{ symbol { name <span class="s">"kitchen/fork"</span> } }
</span></span><span class="line"><span class="cl"> T{ symbol { name <span class="s">"github/fork"</span> } }
</span></span><span class="line"><span class="cl"> <span class="m">42
</span></span></span><span class="line"><span class="cl"> <span class="m">3.14159
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> T{ keyword { name <span class="s">"bun"</span> } }
</span></span><span class="line"><span class="cl"> T{ keyword { name <span class="s">"beef-patty"</span> } }
</span></span><span class="line"><span class="cl"> <span class="m">9
</span></span></span><span class="line"><span class="cl"> <span class="s">"yum!"</span>
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> V{
</span></span><span class="line"><span class="cl"> T{ keyword { name <span class="s">"gelato"</span> } }
</span></span><span class="line"><span class="cl"> <span class="m">1
</span></span></span><span class="line"><span class="cl"> <span class="m">2
</span></span></span><span class="line"><span class="cl"> <span class="m">-2
</span></span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> LH{
</span></span><span class="line"><span class="cl"> { T{ keyword { name <span class="s">"eggs"</span> } } <span class="m">2 </span>}
</span></span><span class="line"><span class="cl"> { T{ keyword { name <span class="s">"lemon-juice"</span> } } <span class="m">3.5 </span>}
</span></span><span class="line"><span class="cl"> { T{ keyword { name <span class="s">"butter"</span> } } <span class="m">1 </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> LH{
</span></span><span class="line"><span class="cl"> { V{ <span class="m">1 2 3 4 </span>} <span class="s">"tell the people what she wore"</span> }
</span></span><span class="line"><span class="cl"> { V{ <span class="m">5 6 7 8 </span>} <span class="s">"the more you see the more you hate"</span> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> HS{
</span></span><span class="line"><span class="cl"> <span class="m">88
</span></span></span><span class="line"><span class="cl"> T{ keyword { name <span class="s">"a"</span> } }
</span></span><span class="line"><span class="cl"> T{ keyword { name <span class="s">"b"</span> } }
</span></span><span class="line"><span class="cl"> <span class="s">"huat"</span>
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ tagged
</span></span><span class="line"><span class="cl"> { name <span class="s">"MyYelpClone/MenuItem"</span> }
</span></span><span class="line"><span class="cl"> { value
</span></span><span class="line"><span class="cl"> LH{
</span></span><span class="line"><span class="cl"> { T{ keyword { name <span class="s">"name"</span> } } <span class="s">"eggs-benedict"</span> }
</span></span><span class="line"><span class="cl"> { T{ keyword { name <span class="s">"rating"</span> } } <span class="m">10 </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>The <a href="https://github.com/factor/factor/blob/master/extra/edn/edn.factor">edn
vocabulary</a>
is now included in the <a href="https://docs.factorcode.org/content/article-vocab-index.html">Factor standard
library</a>.</p>
<p>You can see some information about the various words currently available:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"edn"</span> help
</span></span><span class="line"><span class="cl">Extensible Data Notation (EDN)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">The edn vocabulary supports reading <span class="nb">and </span>writing from the Extensible Data
</span></span><span class="line"><span class="cl">Notation (EDN) format.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Reading from EDN:
</span></span><span class="line"><span class="cl"> read-edns <span class="nf">( -- </span><span class="nv">objects</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> read-edn <span class="nf">( -- </span><span class="nv">object</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> edn> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">objects</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Writing into EDN:
</span></span><span class="line"><span class="cl"> write-edns <span class="nf">( </span><span class="nv">objects</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> write-edn <span class="nf">( </span><span class="nv">object</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> >edn <span class="nf">( </span><span class="nv">object</span> <span class="nf">-- </span><span class="nv">string</span> <span class="nf">)
</span></span></span></code></pre></div><p>Basic support is included for encoding Factor objects:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="k">TUPLE:</span> <span class="nc">foo</span> <span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1 2 3 </span>foo <span class="nb">boa </span>write-edn
</span></span><span class="line"><span class="cl">#scratchpad/foo {:a 1, :b 2, :c 3}
</span></span></code></pre></div><p>But we don’t automatically parse these tagged objects back into a Factor object at the
moment.</p>
<p>Check it out!</p>
Pseudo Encrypt
https://re.factorcode.org/2025/10/pseudo-encrypt.html
Sat, 04 Oct 2025 12:00:00 -0700https://re.factorcode.org/2025/10/pseudo-encrypt.html<p><a href="https://wiki.postgresql.org/wiki/Pseudo_encrypt">Pseudo Encrypt</a> is a function
drawn from the <a href="https://www.postgresql.org">PostgreSQL</a> project.</p>
<blockquote>
<p><strong>pseudo_encrypt(int)</strong> can be used as a <strong>pseudo-random generator of unique
values</strong>. It produces an integer output that is uniquely associated to its
integer input (by a mathematical permutation), but looks random at the same
time, with <strong>zero collision</strong>. This is useful to communicate numbers
generated sequentially without revealing their ordinal position in the
sequence (for ticket numbers, URLs shorteners, promo codes…)</p>
</blockquote>
<p>It’s implementation is defined as:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">OR</span><span class="w"> </span><span class="k">REPLACE</span><span class="w"> </span><span class="k">FUNCTION</span><span class="w"> </span><span class="n">pseudo_encrypt</span><span class="p">(</span><span class="n">value</span><span class="w"> </span><span class="nb">int</span><span class="p">)</span><span class="w"> </span><span class="k">returns</span><span class="w"> </span><span class="nb">int</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="err">$$</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">DECLARE</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">l1</span><span class="w"> </span><span class="nb">int</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">l2</span><span class="w"> </span><span class="nb">int</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">r1</span><span class="w"> </span><span class="nb">int</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">r2</span><span class="w"> </span><span class="nb">int</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">i</span><span class="w"> </span><span class="nb">int</span><span class="p">:</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">BEGIN</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">l1</span><span class="p">:</span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">value</span><span class="w"> </span><span class="o">>></span><span class="w"> </span><span class="mi">16</span><span class="p">)</span><span class="w"> </span><span class="o">&</span><span class="w"> </span><span class="mi">65535</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">r1</span><span class="p">:</span><span class="o">=</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="o">&</span><span class="w"> </span><span class="mi">65535</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">WHILE</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="n">LOOP</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">l2</span><span class="w"> </span><span class="p">:</span><span class="o">=</span><span class="w"> </span><span class="n">r1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">r2</span><span class="w"> </span><span class="p">:</span><span class="o">=</span><span class="w"> </span><span class="n">l1</span><span class="w"> </span><span class="o">#</span><span class="w"> </span><span class="p">((((</span><span class="mi">1366</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">r1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">150889</span><span class="p">)</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">714025</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">714025</span><span class="p">.</span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mi">32767</span><span class="p">)::</span><span class="nb">int</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">l1</span><span class="w"> </span><span class="p">:</span><span class="o">=</span><span class="w"> </span><span class="n">l2</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">r1</span><span class="w"> </span><span class="p">:</span><span class="o">=</span><span class="w"> </span><span class="n">r2</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="p">:</span><span class="o">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">END</span><span class="w"> </span><span class="n">LOOP</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="p">((</span><span class="n">r1</span><span class="w"> </span><span class="o"><<</span><span class="w"> </span><span class="mi">16</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">l1</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">END</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="err">$$</span><span class="w"> </span><span class="k">LANGUAGE</span><span class="w"> </span><span class="n">plpgsql</span><span class="w"> </span><span class="k">strict</span><span class="w"> </span><span class="k">immutable</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>Let’s implement this in <a href="https://factorcode.org">Factor</a> using some of the
words from the <a href="https://docs.factorcode.org/content/article-math.bitwise.html">math.bitwise
vocabulary</a>,
working with the intermediate results as <a href="https://docs.factorcode.org/content/word-__gt__signed,math.bitwise.html">32-bit signed
integers</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pseudo-encrypt</span> <span class="nf">( </span><span class="nv">x</span> <span class="nf">-- </span><span class="nv">y</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">-16 </span><span class="nb">shift </span>] <span class="nb">keep </span>[ <span class="m">16 </span>bits ] <span class="nb">bi@ </span><span class="m">3 </span>[
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="m">1366 </span><span class="nb">* </span><span class="m">150889 </span><span class="nb">+ </span><span class="m">714025 </span><span class="nb">rem </span><span class="m">714025.0 </span><span class="nb">/ </span><span class="m">32767 </span><span class="nb">*
</span></span></span><span class="line"><span class="cl"> round <span class="nb">>integer bitxor </span><span class="m">32 </span>>signed
</span></span><span class="line"><span class="cl"> ] <span class="nb">keep swap
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">times </span><span class="m">16 </span><span class="nb">shift + </span><span class="m">32 </span>>signed <span class="k">;
</span></span></span></code></pre></div><p>We can compare our results for <code>[-10..10]</code> which are helpfully provided on the
original linked page:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">-10 </span>..= <span class="m">10 </span>[ <span class="nb">dup </span>pseudo-encrypt <span class="s">"%3d %12d\n"</span> printf ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"><span class="m">-10 </span> <span class="m">-1270576520
</span></span></span><span class="line"><span class="cl"> <span class="m">-9 </span> <span class="m">-236348969
</span></span></span><span class="line"><span class="cl"> <span class="m">-8 </span> <span class="m">-1184061109
</span></span></span><span class="line"><span class="cl"> <span class="m">-7 </span> <span class="m">-25446276
</span></span></span><span class="line"><span class="cl"> <span class="m">-6 </span> <span class="m">-1507538963
</span></span></span><span class="line"><span class="cl"> <span class="m">-5 </span> <span class="m">-518858927
</span></span></span><span class="line"><span class="cl"> <span class="m">-4 </span> <span class="m">-1458116927
</span></span></span><span class="line"><span class="cl"> <span class="m">-3 </span> <span class="m">-532482573
</span></span></span><span class="line"><span class="cl"> <span class="m">-2 </span> <span class="m">-157973154
</span></span></span><span class="line"><span class="cl"> <span class="m">-1 </span> <span class="m">-1105881908
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span> <span class="m">1777613459
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span> <span class="m">561465857
</span></span></span><span class="line"><span class="cl"> <span class="m">2 </span> <span class="m">436885871
</span></span></span><span class="line"><span class="cl"> <span class="m">3 </span> <span class="m">576481439
</span></span></span><span class="line"><span class="cl"> <span class="m">4 </span> <span class="m">483424269
</span></span></span><span class="line"><span class="cl"> <span class="m">5 </span> <span class="m">1905133426
</span></span></span><span class="line"><span class="cl"> <span class="m">6 </span> <span class="m">971249312
</span></span></span><span class="line"><span class="cl"> <span class="m">7 </span> <span class="m">1926833684
</span></span></span><span class="line"><span class="cl"> <span class="m">8 </span> <span class="m">735327624
</span></span></span><span class="line"><span class="cl"> <span class="m">9 </span> <span class="m">1731020007
</span></span></span><span class="line"><span class="cl"> <span class="m">10 </span> <span class="m">792482838
</span></span></span></code></pre></div><p>Great – it matches!</p>
std::flip
https://re.factorcode.org/2025/09/std-flip.html
Mon, 29 Sep 2025 20:00:00 -0700https://re.factorcode.org/2025/09/std-flip.html<p><a href="https://morwenn.github.io/about/">Morwenn</a> posted a blog about implementing a
<a href="https://morwenn.github.io//c++/2025/09/25/TSB004-std-flip.html"><code>std::flip</code></a>
operation in C++:</p>
<blockquote>
<p>This is basically walking up the tree from the child node as if it were a
linked list. The reverse operation either implies walking through two
children nodes, or simply flipping the order of parameters, which is where
<code>std::flip</code> intervenes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">is_descendant_of</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">flip</span><span class="p">(</span><span class="n">is_ancestor_of</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// This property should always hold
</span></span></span><span class="line"><span class="cl"><span class="n">assert</span><span class="p">(</span><span class="n">is_descendant_of</span><span class="p">(</span><span class="n">node1</span><span class="p">,</span> <span class="n">node2</span><span class="p">)</span> <span class="o">==</span> <span class="n">is_ancestor_of</span><span class="p">(</span><span class="n">node2</span><span class="p">,</span> <span class="n">node1</span><span class="p">));</span>
</span></span></code></pre></div></blockquote>
<p>Spoiler: the <code>std::flip</code> operator is not part of the <a href="https://cplusplus.com/reference/">C++ standard
library</a>, although an implementation is
providing at the end of the blog post in around 90 lines of code.</p>
<p>Still, I thought it would be fun to implement in
<a href="https://factorcode.org">Factor</a>.</p>
<p>As it turns out, we already have a
<a href="https://docs.factorcode.org/content/word-flip,sequences.html">flip</a> word that
modifies a sequence, essentially by returning the transpose of a matrix. One
could argue that
<a href="https://docs.factorcode.org/content/word-transpose,math.matrices.html">transpose</a>
might be a better name for that operation. In any event, let’s focus on
implementing the <code>std::flip</code> operation.</p>
<p>How would we reverse the arguments to a word?</p>
<ul>
<li><code>a b</code> can become <code>b a</code> by calling <code>swap</code>.</li>
<li><code>a b c</code> can become <code>c b a</code> by calling <code>swap rot</code>.</li>
<li><code>a b c d</code> can become <code>d c b a</code> by calling <code>swap rot roll</code>.</li>
</ul>
<p>We can generalize this into a
<a href="https://docs.factorcode.org/content/article-macros.html">macro</a> by repeatedly
calling
<a href="https://docs.factorcode.org/content/word--nrot,generalizations.html">-nrot</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MACRO:</span> <span class="nf">nreverse</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">quot</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>[a..b) [ '[ _ -nrot ] ] <span class="nb">map </span>[ ] <span class="nb">concat-as </span><span class="k">;
</span></span></span></code></pre></div><p>And then show that it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { } [ <span class="m">0 </span>nreverse ] <span class="nb">with-datastack </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="s">"a"</span> } [ <span class="m">1 </span>nreverse ] <span class="nb">with-datastack </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"a"</span> }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="s">"a"</span> <span class="s">"b"</span> } [ <span class="m">2 </span>nreverse ] <span class="nb">with-datastack </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"b"</span> <span class="s">"a"</span> }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="s">"a"</span> <span class="s">"b"</span> <span class="s">"c"</span> } [ <span class="m">3 </span>nreverse ] <span class="nb">with-datastack </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"c"</span> <span class="s">"b"</span> <span class="s">"a"</span> }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="s">"a"</span> <span class="s">"b"</span> <span class="s">"c"</span> <span class="s">"d"</span> } [ <span class="m">4 </span>nreverse ] <span class="nb">with-datastack </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"d"</span> <span class="s">"c"</span> <span class="s">"b"</span> <span class="s">"a"</span> }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="s">"a"</span> <span class="s">"b"</span> <span class="s">"c"</span> <span class="s">"d"</span> <span class="s">"e"</span> } [ <span class="m">5 </span>nreverse ] <span class="nb">with-datastack </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"e"</span> <span class="s">"d"</span> <span class="s">"c"</span> <span class="s">"b"</span> <span class="s">"a"</span> }
</span></span></code></pre></div><p><em>Note: this has been <a href="https://github.com/factor/factor/commit/67b320bce780db5843ed4bd4564548ddc70906e4">added to the shuffle
vocabulary</a>.</em></p>
<p>Using this, we can build some
<a href="https://docs.factorcode.org/content/article-syntax.html">syntax</a> that takes
the next token and searches for a matching word with that name, and then calls
it after reversing the inputs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYNTAX: </span>flip:
</span></span><span class="line"><span class="cl"> scan-word [ stack-effect in>> <span class="nb">length </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> '[ _ nreverse _ <span class="nb">execute </span>] <span class="nb">append! </span><span class="k">;
</span></span></span></code></pre></div><p>As an example, we will use the
<a href="https://docs.factorcode.org/content/word-4array,arrays.html">4array</a> word that
returns an array consisting of four arguments from the stack.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 20 30 40 </span><span class="nb">4array </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">10 20 30 40 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 20 30 40 </span>flip: <span class="nb">4array </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">40 30 20 10 </span>}
</span></span></code></pre></div><p>We could have different syntax for flipping arbitrary code – first parsing a
<a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a> and
then
<a href="https://docs.factorcode.org/content/word-infer,stack-checker.html">infer</a>
the <a href="https://docs.factorcode.org/content/article-effects.html">stack-effect</a>
and then inline a reversed argument version.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYNTAX: </span>flip[
</span></span><span class="line"><span class="cl"> parse-quotation [ infer in>> <span class="nb">length </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> '[ _ nreverse @ ] <span class="nb">suffix! </span><span class="k">;
</span></span></span></code></pre></div><p>We can try that out with a simple block of code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1 2 3 </span>flip[ [ <span class="m">10 </span><span class="nb">* </span>] <span class="nb">tri@ </span>] <span class="nb">call 3array </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">30 20 10 </span>}
</span></span></code></pre></div><p>And only a few lines of code in total.</p>
<p>Pretty cool!</p>
Scream Cipher
https://re.factorcode.org/2025/09/scream-cipher.html
Sun, 21 Sep 2025 08:00:00 -0700https://re.factorcode.org/2025/09/scream-cipher.html<p><a href="https://sethmlarson.dev">Seth Larson</a> wrote about a <a href="https://sethmlarson.dev/scream-cipher">Scream
Cipher</a>:</p>
<blockquote>
<p>You’ve probably heard of <a href="https://en.wikipedia.org/wiki/Stream_cipher">stream
ciphers</a>, but what about a
scream cipher 😱? Today I learned there are more “<a href="https://utf8.xyz/latin-capital-letter-a-">Latin capital letter
A</a>” Unicode characters than there
are letters in the English alphabet. You know what that means, it’s time to
scream:</p>
</blockquote>
<p>We can use <a href="https://docs.factorcode.org/content/article-biassocs.html">bidirectional
assocs</a> to keep a
single cipher data structure that efficiently maps into and out of the cipher:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">cipher</span> $[
</span></span><span class="line"><span class="cl"> <span class="s">"ABCDEFGHIJKLMNOPQRSTUVWXYZ"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"AÁĂẮẶẰẲẴǍÂẤẬẦẨẪÄǞȦǠẠȀÀẢȂĀĄ"</span>
</span></span><span class="line"><span class="cl"> <span class="nb">zip </span>>biassoc
</span></span><span class="line"><span class="cl">]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>scream</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">SCREAM</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ ch>upper cipher <span class="nb">?at drop </span>] <span class="nb">map </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">scream></span> <span class="nf">( </span><span class="nv">SCREAM</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ cipher ?value-at <span class="nb">drop </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>And then give it a try!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"FACTOR!"</span> >scream <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"ẰAĂẠẪȦ!"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"ẰAĂẠẪȦ!"</span> scream> <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"FACTOR!"</span>
</span></span></code></pre></div><p>Fun!</p>
Environment Variables
https://re.factorcode.org/2025/09/environment-variables.html
Fri, 19 Sep 2025 08:00:00 -0700https://re.factorcode.org/2025/09/environment-variables.html<p><a href="https://factorcode.org">Factor</a> has an <a href="https://docs.factorcode.org/content/article-environment.html">environment
vocabulary</a>
for working with process <a href="https://en.wikipedia.org/wiki/Environment_variable">environment
variables</a> on all the
platforms we currently support: macOS, Windows, and Linux.</p>
<p>Recently, I noticed that <a href="https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-9/overview">.NET
9</a>
added <a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/9.0/empty-env-variable">support for empty environment
variables</a>.
This was particulary relevant due to a test failure of the new
<a href="https://re.factorcode.org/2025/06/dotenv.html">Dotenv</a> implementation on Windows. It turns out that
we inherited the same issue that earlier .NET versions had, which is an
inability to disambiguate an <em>unset</em> environment variable from one that was
set to the <em>empty string</em>. This issue has <a href="https://github.com/factor/factor/commit/cfa54086d6fec839693c07dede4dc894c4ea0c4e">now been
fixed</a>
in the latest <a href="https://github.com/factor/factor">development version</a>.</p>
<p>Before:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"FACTOR"</span> os-env <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">f
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">""</span> <span class="s">"FACTOR"</span> set-os-env
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"FACTOR"</span> os-env <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">f
</span></span></span></code></pre></div><p>After:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"FACTOR"</span> os-env <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">f
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">""</span> <span class="s">"FACTOR"</span> set-os-env
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"FACTOR"</span> os-env <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">""</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"FACTOR"</span> unset-os-env
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"FACTOR"</span> os-env <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">f
</span></span></span></code></pre></div><p>There might be other cross-platform environment-related topics to
investigate, such as an open issue to look into <a href="https://github.com/factor/factor/issues/2168">case-preserving but
case-insensitive environment variables on
Windows</a>.</p>
<p>PRs welcome!</p>
HiDPI
https://re.factorcode.org/2025/09/hidpi.html
Tue, 02 Sep 2025 08:00:00 -0700https://re.factorcode.org/2025/09/hidpi.html<p><a href="https://wiki.archlinux.org/title/HiDPI">HiDPI</a> is a name for <em>high resolution</em>
displays, sometimes called <em>retina</em> displays. A <a href="https://www.youtube.com/watch?v=PRpiBpDy7MQ">long long time
ago</a>, I added support for <a href="2012/11/retina-displays.html">Retina
Displays</a> on <a href="https://www.apple.com/macos">macOS</a>
using <a href="https://factorcode.org">Factor</a>. But, they have not been well supported
on either Linux or Windows platforms.</p>
<p>That ends today!</p>
<h3 id="linux">Linux</h3>
<p>Some users have seen the “small window” problem on Linux, where on high
resolution displays the Factor <a href="https://docs.factorcode.org/content/article-ui-listener.html">UI
listener</a> was
rendered super tiny:</p>
<p>
<img src="https://re.factorcode.org/images/2025-09-02-old-linux.png" alt="" width="422" height="519" />
</p>
<p>This is now fixed, it renders at the appropriate resolution detecting the
screen it is launched on, or using the <code>GDK_SCALE</code> environment variable:</p>
<p>
<img src="https://re.factorcode.org/images/2025-09-02-new-linux.png" alt="" width="722" height="878" />
</p>
<p>There has been one report that this works in the <a href="https://www.gnome.org">Gnome</a>
environments but not on <a href="https://kde.org">KDE</a>, so we might still have a few
code changes necessary to make this more universal. And we also still need to
switch from using our older GTK integration to the newer one with clean support
for <a href="https://wayland.freedesktop.org">Wayland</a>.</p>
<h3 id="windows">Windows</h3>
<p>Other users have noticed the <em>blurry</em> text on Windows, due to using a legacy
compatibility mode:</p>
<p>
<img src="https://re.factorcode.org/images/2025-09-02-old-windows.png" alt="" width="721" height="865" />
</p>
<p>This is now fixed, rendering with the correct scaling factor:</p>
<p>
<img src="https://re.factorcode.org/images/2025-09-02-new-windows.png" alt="" width="725" height="874" />
</p>
<p>It has been tested with 200% and 300% scaling factors. It is possible that
intermediate scaling factors like 150% are not well supported and additional
tweaks might be necessary to make this more universal.</p>
<p>Currently, on all three supported platforms, we use a global scaling factor
which does not allow for moving Factor windows cleanly between screens with
different scaling factors, for example when using HDMI on presentations, etc.</p>
<p>PRs welcome!</p>
Neovim
https://re.factorcode.org/2025/08/neovim.html
Wed, 27 Aug 2025 08:00:00 -0700https://re.factorcode.org/2025/08/neovim.html<p><a href="https://neovim.io/">Neovim</a> is a modern implementation of a <em>vim-like</em> editor. It started as a refactor, but “<em>not a rewrite but a continuation and extension of Vim</em>”. It does have some ability to load plugins built in <a href="https://learnxinyminutes.com/vimscript/">Vimscript</a>, but most new plugins seem to be written using the <a href="https://www.lua.org/">Lua programming language</a>.</p>
<p><a href="https://factorcode.org">Factor</a> has many different <a href="https://docs.factorcode.org/content/article-editor.html">editor integrations</a> supporting various text editors as well as plugins for some that provide additional features. One of these is the <a href="https://github.com/factor/factor.vim">factor.vim</a> plugin for Vim, which I happen to use frequently.</p>
<p>In the <a href="https://x.com/dhh/status/1960408077674668083">Big Omarchy 2.0 Tour</a>, <a href="https://dhh.dk/">DHH</a> presents about the <a href="https://omarchy.org/">Omarchy</a> customization of Linux. I noticed that they have a pretty nice Neovim integration, particularly with the system themes. It turns out to be based somewhat on the <a href="https://www.lazyvim.org/">Lazyvim</a> system.</p>
<p>In any event, I wondered about how easy it would be to make a Neovim plugin for Factor. It isn’t fully necessary as there is support for Vimscript plugins and the Factor one works pretty well out of the box. However, I thought I’d ask <a href="https://www.anthropic.com/claude-code">Claude Code</a> to go off and <a href="https://en.wikipedia.org/wiki/YOLO_(aphorism)">YOLO</a> an implementation based on the existing one. Thankfully this was not a <a href="https://www.merriam-webster.com/slang/fafo">FAFO</a> moment, and after a few cycles it came back with something that mostly works!</p>
<p>
<img src="https://re.factorcode.org/images/2025-08-27-neovim.png" alt="" width="912" height="740" />
</p>
<p>This is available in the <a href="https://github.com/factor/factor.nvim">factor.nvim</a> repository and should be pretty easy to integrate into your Neovim setup. Perhaps give it a try and see what you think? I’ve been using it and it seems to work pretty well.</p>
New Icon
https://re.factorcode.org/2025/08/new-icon.html
Tue, 26 Aug 2025 18:00:00 -0700https://re.factorcode.org/2025/08/new-icon.html<p>Encouraged by a fun rant about <a href="https://daringfireball.net/2025/08/macos_26_tahoes_dead_canary_utility_app_icons">MacOS Tahoe’s Dead-Canary Utility App
Icons</a>,
the reality that <a href="https://apple.com">Apple</a> is moving into the wonderful
<a href="https://en.wikipedia.org/wiki/Squircle">squircle-filled</a> future, and the
particularly annoying fact that <em>legacy</em> icons look terrible on <a href="https://www.apple.com/os/macos/">macOS
Tahoe</a> – we have a new icon for
<a href="https://factorcode.org">Factor</a>!</p>
<p>
<img src="https://re.factorcode.org/images/2025-08-26-new-icon.png" alt="" width="512" height="512" />
</p>
<p>The latest development version <a href="https://github.com/factor/factor/tree/master/misc/icons">includes new icon
files</a> in both PNG
and SVG formats. And these are being used across macOS, Windows, and Linux
builds. And, it might be <a href="https://www.dictionary.com/e/slang/bury-the-lede/">burying the
lede</a>, but this is a
particularly good time to do this as we finally have <em>high-resolution</em>
“HiDPI” support working on Windows and Linux.</p>
<p>The next release is likely to be a good one!</p>
TA-Lib
https://re.factorcode.org/2025/08/ta-lib.html
Sun, 24 Aug 2025 08:00:00 -0700https://re.factorcode.org/2025/08/ta-lib.html<p>The <a href="https://ta-lib.org">TA-Lib</a> is a C project that supports adding
“<em>technical analysis to your own financial market trading applications</em>”. It
was originally created in 2001 and is well-tested, recently released, and
popular:</p>
<blockquote>
<p>200 indicators such as ADX, MACD, RSI, Stochastic, Bollinger Bands etc… <a href="https://ta-lib.org/functions/">See complete list…</a></p>
<p>Candlestick patterns recognition</p>
<p>Core written in <a href="https://ta-lib.org/api/">C/C++</a> with API also available for <a href="https://ta-lib.org/wrappers/">Python</a>.</p>
<p>Open-Source (BSD License). Can be freely integrated in your own open-source or commercial applications.</p>
</blockquote>
<p>Of course, I wanted to be able to call the library using
<a href="https://factorcode.org">Factor</a>. We have a <a href="https://docs.factorcode.org/content/article-alien.html">C library
interface</a> that makes
it pretty easy to interface with C libraries.</p>
<p>First, we add the library we expect to load:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><< <span class="s">"ta-lib"</span> {
</span></span><span class="line"><span class="cl"> { [ os windows? ] [ <span class="s">"libta-lib.dll"</span> ] }
</span></span><span class="line"><span class="cl"> { [ os macos? ] [ <span class="s">"libta-lib.dylib"</span> ] }
</span></span><span class="line"><span class="cl"> { [ os unix? ] [ <span class="s">"libta-lib.so"</span> ] }
</span></span><span class="line"><span class="cl">} <span class="nb">cond </span>cdecl add-library >>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">LIBRARY: ta-lib
</span></span></code></pre></div><p>Then, we can define some types and some library functions to calculate the
<a href="https://en.wikipedia.org/wiki/Relative_strength_index">relative strength
index</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">TYPEDEF:</span> <span class="nf">int</span> <span class="nf">TA_RetCode</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> TA_RetCode <span class="nf">TA_RSI</span> ( int startIdx, int endIdx, double* inReal, int optInTimePeriod, int* outBegIdx, int* outNBElement, double* outReal )
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> int <span class="nf">TA_RSI_Lookback</span> ( int optInTimePeriod )
</span></span></code></pre></div><p>We use a simple <em>code generator</em> to define all the functions, as well as
wrappers that can be used to call it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">RSI</span> <span class="nf">( </span><span class="nv">real</span> <span class="nv">timeperiod</span> <span class="nf">-- </span><span class="nv">real</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>int <ref> :> outbegidx
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>int <ref> :> outnbelement
</span></span><span class="line"><span class="cl"> <span class="nb">real </span>check-array :> inreal
</span></span><span class="line"><span class="cl"> inreal <span class="nb">length </span>:> len
</span></span><span class="line"><span class="cl"> inreal check-begidx1 :> begidx
</span></span><span class="line"><span class="cl"> len <span class="m">1 </span><span class="nb">- </span>begidx <span class="nb">- </span>:> endidx
</span></span><span class="line"><span class="cl"> timeperiod TA_RSI_Lookback begidx <span class="nb">+ </span>:> lookback
</span></span><span class="line"><span class="cl"> len lookback make-double-array :> outreal
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>endidx inreal begidx <span class="nb">tail-slice </span>timeperiod outbegidx outnbelement outreal lookback <span class="nb">tail-slice </span>TA_RSI ta-check-success
</span></span><span class="line"><span class="cl"> outreal <span class="k">;
</span></span></span></code></pre></div><p>And, now we can use it!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 10 </span>randoms <span class="m">3 </span>RSI <span class="m">.
</span></span></span><span class="line"><span class="cl">double-array{
</span></span><span class="line"><span class="cl"> 0/0.
</span></span><span class="line"><span class="cl"> 0/0.
</span></span><span class="line"><span class="cl"> 0/0.
</span></span><span class="line"><span class="cl"> <span class="m">50.0
</span></span></span><span class="line"><span class="cl"> <span class="m">62.16216216216216
</span></span></span><span class="line"><span class="cl"> <span class="m">31.506849315068497
</span></span></span><span class="line"><span class="cl"> <span class="m">46.38069705093834
</span></span></span><span class="line"><span class="cl"> <span class="m">32.33644859813084
</span></span></span><span class="line"><span class="cl"> <span class="m">59.75541967759867
</span></span></span><span class="line"><span class="cl"> <span class="m">66.53570603189276
</span></span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>You’ll note that the first few values are <code>0/0.</code> which represents a
<a href="https://en.wikipedia.org/wiki/NaN">NaN</a> when we don’t have enough data to
compute an answer – either because we are in the lookback phase or because the
inputs have NaNs.</p>
<p>For convenience, we convert the inputs to <code>double-array</code> to perform the
calculation, but if the input is already a <code>double-array</code> then there is not
any data conversion cost.</p>
<p>There are some advanced techniques including use of the <em>Abstract API</em> for
meta-programming, default values for parameters, candlestick settings,
streaming indicator support, and documentation that we probably should think
about adding as well.</p>
<p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/ta-lib/ta-lib.factor">GitHub</a>.</p>
String Length
https://re.factorcode.org/2025/08/string-length.html
Sat, 23 Aug 2025 08:00:00 -0700https://re.factorcode.org/2025/08/string-length.html<p>I was reminded recently about a great article about <a href="https://hsivonen.fi/string-length/">unicode string
lengths</a>:</p>
<blockquote>
<p>It’s Not Wrong that <code>"🤦🏼♂️".length == 7</code></p>
<p>But It’s Better that <code>"🤦🏼♂️".len() == 17</code> and Rather Useless that <code>len("🤦🏼♂️") == 5</code></p>
</blockquote>
<p>This comes at a time of <a href="https://community.openai.com/t/excessive-emoji-tsunami-in-chatgpt-conversations/1112668">excessive emoji
tsunami</a>
thanks to the proliferation of <a href="https://en.wikipedia.org/wiki/Large_language_model">large language
models</a> and probably lots
of <a href="https://en.wikipedia.org/wiki/Generation_Z">Gen Z</a> in the training data
sets. Sometimes emojis are fun and useful like in
<a href="https://re.factorcode.org/2025/03/base256emoji.html">Base256Emoji</a> and sometimes it can get carried
away like in the <a href="https://emojikitchen.dev">Emoji Kitchen</a>.</p>
<p>I have written about <a href="https://re.factorcode.org/2023/05/unicode.html">Factor’s unicode support</a> before
and wanted to use this example to show a bit more about how
<a href="https://factorcode.org">Factor</a> represents text using the <a href="https://www.unicode.org/standard/standard.html">Unicode
standard</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"🤦"</span> <span class="nb">length </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">1
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"🤦🏼♂️"</span> <span class="nb">length </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">5
</span></span></span></code></pre></div><p><a href="https://www.destroyallsoftware.com/talks/wat">Wat</a>.</p>
<p>Well, what is happening is that the current <a href="https://docs.factorcode.org/content/article-strings.html">strings
vocabulary</a> stores
Unicode <a href="https://en.wikipedia.org/wiki/Code_point">code points</a>. This can be
both <em>useful</em> and <em>useless</em> depending on the task at hand. We can print out
which ones are used in this example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"🤦🏼♂️"</span> [ char>name <span class="m">. </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"><span class="s">"face-palm"</span>
</span></span><span class="line"><span class="cl"><span class="s">"emoji-modifier-fitzpatrick-type-3"</span>
</span></span><span class="line"><span class="cl"><span class="s">"zero-width-joiner"</span>
</span></span><span class="line"><span class="cl"><span class="s">"male-sign"</span>
</span></span><span class="line"><span class="cl"><span class="s">"variation-selector-16"</span>
</span></span></code></pre></div><p>When a developer expresses a need to store or retrieve textual data, they
likely need to know about <a href="https://en.wikipedia.org/wiki/Character_encoding">character
encodings</a>. In this case, we
can see the number of bytes required to store this string in different
<a href="https://docs.factorcode.org/content/article-io.encodings.html">encodings</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"🤦🏼♂️"</span> utf8 encode <span class="nb">length </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">17
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"🤦🏼♂️"</span> utf16 encode <span class="nb">length </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">16
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"🤦🏼♂️"</span> utf32 encode <span class="nb">length </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">24
</span></span></span></code></pre></div><p>But, what if we just want to know how many <em>visual</em> characters are in the
string?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"🤦🏼♂️"</span> >graphemes <span class="nb">length </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">1
</span></span></span></code></pre></div><p>This is covered in <a href="https://tonsky.me/blog/unicode/">The Absolute Minimum Every Software Developer Must Know
About Unicode in 2023</a>, which is also a great
article and covers this as well as a number of other aspects of the Unicode
standard.</p>
Anubis
https://re.factorcode.org/2025/08/anubis.html
Thu, 21 Aug 2025 08:00:00 -0700https://re.factorcode.org/2025/08/anubis.html<p><a href="https://lock.cmpxchg8b.com">Tavis Ormany</a> wrote a <a href="https://lock.cmpxchg8b.com/anubis.html">great blog
post</a> about the <a href="https://github.com/TecharoHQ/anubis">Anubis
project</a> asking a very valid sounding
question:</p>
<blockquote>
<p>Hey… quick question, why are anime catgirls blocking my access to the Linux
kernel?</p>
</blockquote>
<p>
<img src="https://re.factorcode.org/images/2025-08-21-anubis.png" alt="" width="467" height="382" />
</p>
<p>The answer seems to be that it “<em><a href="https://en.wikipedia.org/wiki/Weighing_of_souls">weighs the soul of your
connection</a> using one or more
challenges in order to protect upstream resources from scraper bots</em>”. In
particular, this project is an attempt to <a href="https://lwn.net/Articles/1008897/">fight the AI scraperbot
scourge</a> which is making many popular
websites annoying to use these days and spawning a kind of <em>arms race</em> amongst
website owners, content delivery networks, and well-funded and
morally-ambiguous AI firms.</p>
<p>Tavis goes into great detail about the estimated costs and inconvenience of
this approach — and why it might likely inconvenience scraper bots which use
many different <a href="https://en.wikipedia.org/wiki/IP_address">IP addresses</a> more
than normal traffic which typically does not — as well as how the
<a href="https://en.wikipedia.org/wiki/Proof_of_work">proof-of-work</a> methodology is
implemented using a solution written in the <a href="https://en.wikipedia.org/wiki/C_(programming_language)">C programming
language</a>.</p>
<p>Without going into the <em>safety versus security</em> debate (a focus of the
<a href="https://lobste.rs/s/qw5iyf/why_are_anime_catgirls_blocking_my_access">discussion on
Lobsters</a>),
I thought I would show how to implement this using
<a href="https://factorcode.org">Factor</a>.</p>
<h3 id="how-does-anubis-work">How does Anubis work?</h3>
<p>The <em>Anubis challenge</em> is a message, for example <code>5d737f0600ff2dd</code>, which we
use as a prefix while trying up to <code>262144</code> different <em>nonce</em> suffixes to
find a SHA-256 hash that starts with 16 bits of zero.</p>
<p>The <em>Anubis response</em> in this case is <code>47224</code>, which has a SHA-256 hash
starting with <code>0000</code>:</p>
<pre tabindex="0"><code>$ printf "5d737f0600ff2dd%d" 47224 | sha256sum
000043f7c4392a781a04419a7cb503089ebcf3164e2b1d4258b3e6c15b8b07f1 -
</code></pre><h3 id="solving-in-factor">Solving in Factor</h3>
<p><a href="https://factorcode.org">Factor</a> includes support for <a href="https://docs.factorcode.org/content/article-checksums.sha.html">SHA-2
checksums</a> that
we can use to solve the example puzzle:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">checksums</span> <span class="nn">checksums.sha</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">math.parser</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-anubis</span> <span class="nf">( </span><span class="nv">message</span> <span class="nf">-- </span><span class="nv">nonce</span> <span class="nv">anubis</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">18 </span><span class="nb">2^ </span><iota> [
</span></span><span class="line"><span class="cl"> >dec <span class="nb">append </span>sha-256 checksum-bytes
</span></span><span class="line"><span class="cl"> [ B{ <span class="m">0 0 </span>} <span class="nb">head? </span>] <span class="nb">keep </span><span class="no">f </span><span class="nb">?
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">with map-find swap </span><span class="k">;
</span></span></span></code></pre></div><p>And a test showing that it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> <span class="m">47224
</span></span></span><span class="line"><span class="cl"> <span class="s">"000043f7c4392a781a04419a7cb503089ebcf3164e2b1d4258b3e6c15b8b07f1"</span>
</span></span><span class="line"><span class="cl">} [ <span class="s">"5d737f0600ff2dd"</span> find-anubis bytes>hex-string ] unit-test
</span></span></code></pre></div><p>But, I noticed that it’s not <em>that</em> fast:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"5d737f0600ff2dd"</span> find-anubis ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.216027939 </span>seconds
</span></span></code></pre></div><h3 id="solving-in-factor-using-c">Solving in Factor using C</h3>
<p>Tanis used some <a href="https://docs.openssl.org/3.0/man3/SHA256_Init/">functions from the OpenSSL
library</a> to compute the
checksum.</p>
<p>We can take the same approach using the <a href="https://docs.factorcode.org/content/article-alien.html">C library
interface</a>. It would be
great to be able to parse header files and make this a little simpler, but for
now we can define these C functions that we would like to call:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">LIBRARY: libcrypto
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">STRUCT:</span> <span class="nc">SHA256_CTX</span>
</span></span><span class="line"><span class="cl"> { h uint[8] }
</span></span><span class="line"><span class="cl"> { Nl uint }
</span></span><span class="line"><span class="cl"> { Nh uint }
</span></span><span class="line"><span class="cl"> { data uint[16] }
</span></span><span class="line"><span class="cl"> { num uint }
</span></span><span class="line"><span class="cl"> { md_len uint } <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> int <span class="nf">SHA256_Init</span> ( SHA256_CTX* c )
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> int <span class="nf">SHA256_Update</span> ( SHA256_CTX* c, void* data, size_t len )
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> int <span class="nf">SHA256_Final</span> ( uchar* md, SHA256_CTX* c )
</span></span></code></pre></div><p>And then build the same C program in Factor:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">find-anubis</span> <span class="nf">( </span><span class="nv">message</span> <span class="nf">-- </span><span class="nv">nonce</span> <span class="nv">anubis</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> SHA256_CTX <span class="nb">new </span>:> base
</span></span><span class="line"><span class="cl"> base SHA256_Init <span class="m">1 </span><span class="nb">assert=
</span></span></span><span class="line"><span class="cl"> base message binary encode <span class="nb">dup length </span>SHA256_Update <span class="m">1 </span><span class="nb">assert=
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="m">32 </span><byte-array> :> hash
</span></span><span class="line"><span class="cl"> <span class="m">18 </span><span class="nb">2^ </span><iota> [
</span></span><span class="line"><span class="cl"> base <span class="nb">clone </span>:> ctx
</span></span><span class="line"><span class="cl"> ctx <span class="nb">swap </span>>dec binary encode <span class="nb">dup length </span>SHA256_Update <span class="m">1 </span><span class="nb">assert=
</span></span></span><span class="line"><span class="cl"> hash ctx SHA256_Final <span class="m">1 </span><span class="nb">assert=
</span></span></span><span class="line"><span class="cl"> hash B{ <span class="m">0 0 </span>} <span class="nb">head?
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">find nip </span>hash <span class="k">;
</span></span></span></code></pre></div><p>Is it fast?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"5d737f0600ff2dd"</span> find-anubis ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.009508132 </span>seconds
</span></span></code></pre></div><p>Sure is!</p>
<p>How does that compare to the original C program?</p>
<pre tabindex="0"><code>$ gcc -Ofast -march=native anubis-miner.c -lcrypto -o anubis-miner
$ time ./anubis-miner 5d737f0600ff2dd
47224
real 0m0.005s
user 0m0.003s
sys 0m0.002s
</code></pre><p>Pretty favorably!</p>
<h3 id="solving-in-factor-using-c-approach">Solving in Factor using C approach</h3>
<p>Part of the reason the C approach is fast, is that it hashes the message and
then only has to hash the additional bytes of the <em>nonce</em> and check the result
passes. We can try this, by cloning our <code>sha-256-state</code> and checking on each
iteration whether it passes the test:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">checksums</span> <span class="nn">checksums.sha</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">math.parser</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-anubis</span> <span class="nf">( </span><span class="nv">message</span> <span class="nf">-- </span><span class="nv">nonce</span> <span class="nv">anubis</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> sha-256 initialize-checksum-state <span class="nb">swap </span>add-checksum-bytes
</span></span><span class="line"><span class="cl"> <span class="m">18 </span><span class="nb">2^ </span><iota> [
</span></span><span class="line"><span class="cl"> [ <span class="nb">clone </span>] <span class="nb">dip </span>>dec add-checksum-bytes
</span></span><span class="line"><span class="cl"> get-checksum [ B{ <span class="m">0 0 </span>} <span class="nb">head? </span>] <span class="nb">keep </span><span class="no">f </span><span class="nb">?
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">with map-find swap </span><span class="k">;
</span></span></span></code></pre></div><p>Is that faster?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"5d737f0600ff2dd"</span> find-anubis ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.183254613 </span>seconds
</span></span></code></pre></div><p>A little bit. But what’s the problem?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"5d737f0600ff2dd"</span> find-anubis ] profile
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> top-down profile.
</span></span><span class="line"><span class="cl">depth time ms GC % JIT % FFI % FT %
</span></span><span class="line"><span class="cl"> <span class="m">13 </span> <span class="m">183.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">13.11 </span> <span class="m">0.00 </span>T{ thread <span class="no">f </span><span class="s">"Initial"</span> ~quotation~ ~quotation~ <span class="m">19 </span>~box~ <span class="no">f t f </span>H{...
</span></span><span class="line"><span class="cl"> <span class="m">14 </span> <span class="m">149.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">8.72 </span> <span class="m">0.00 </span> <span class="no">M\ sha-256-state get-checksum</span>
</span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">85.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">2.35 </span> <span class="m">0.00 </span> <span class="no">M\ sha2-short checksum-block</span>
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">27.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">7.41 </span> <span class="m">0.00 </span> 4be>
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">10.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ virtual-sequence nth-unsafe</span>
</span></span><span class="line"><span class="cl"> <span class="m">18 </span> <span class="m">8.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ slice virtual@</span>
</span></span><span class="line"><span class="cl"> <span class="m">19 </span> <span class="m">6.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="nb">+
</span></span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">6.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ fixnum shift</span>
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">4.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">50.00 </span> <span class="m">0.00 </span> fixnum-shift
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">4.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ byte-array nth-unsafe</span>
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="nb">bitor
</span></span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="nb">>
</span></span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ slice length</span>
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">7.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ fixnum integer>fixnum</span>
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">5.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ fixnum >fixnum</span>
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">4.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> be>
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">2.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ slice length</span>
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">2.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> (byte-array)
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">2.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ slice length</span>
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> c-ptr?
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ fixnum integer>fixnum-strict</span>
</span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">35.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">25.71 </span> <span class="m">0.00 </span> pad-last-block
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">26.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">26.92 </span> <span class="m">0.00 </span> %
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">7.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">85.71 </span> <span class="m">0.00 </span> set-alien-unsigned-1
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">7.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ growable nth-unsafe</span>
</span></span><span class="line"><span class="cl"> <span class="m">18 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ byte-vector underlying>></span>
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">4.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ growable set-nth-unsafe</span>
</span></span><span class="line"><span class="cl"> <span class="m">18 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ byte-vector underlying>></span>
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">2.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">50.00 </span> <span class="m">0.00 </span> resize-byte-array
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ growable lengthen</span>
</span></span><span class="line"><span class="cl"> <span class="m">18 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ byte-vector length>></span>
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ growable length</span>
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ byte-array set-nth-unsafe</span>
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="nb">integer?
</span></span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">5.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> >slow-be
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">2.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ fixnum shift</span>
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> fixnum-shift
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ fixnum integer>fixnum</span>
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 100.00 </span> <span class="m">0.00 </span> <byte-array>
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 100.00 </span> <span class="m">0.00 </span> set-alien-unsigned-1
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ growable set-nth</span>
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ byte-vector underlying>></span>
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> ,
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="nb">assoc-stack
</span></span></span><span class="line"><span class="cl"> <span class="m">18 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ hashtable at*</span>
</span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">16.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">6.25 </span> <span class="m">0.00 </span> >slow-be
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">8.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ fixnum shift</span>
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">2.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">50.00 </span> <span class="m">0.00 </span> (byte-array)
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> fixnum-shift
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="nb"><
</span></span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ fixnum integer>fixnum</span>
</span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">3.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ chunking nth-unsafe</span>
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">2.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ groups group@</span>
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ fixnum min</span>
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="nb">*
</span></span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">2.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> >be
</span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ sha2-state H>></span>
</span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 100.00 </span> <span class="m">0.00 </span> fixnum/i
</span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ sha2-state clone</span>
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ sha2-state H<<</span>
</span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ uint-array nth-unsafe</span>
</span></span><span class="line"><span class="cl"> <span class="m">14 </span> <span class="m">23.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">30.43 </span> <span class="m">0.00 </span> <span class="no">M\ block-checksum-state add-checksum-bytes</span>
</span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">18.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">27.78 </span> <span class="m">0.00 </span> >byte-vector
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">7.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ virtual-sequence nth-unsafe</span>
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ slice virtual@</span>
</span></span><span class="line"><span class="cl"> <span class="m">18 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="nb">+
</span></span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">5.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">80.00 </span> <span class="m">0.00 </span> set-alien-unsigned-1
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">2.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ byte-array nth-unsafe</span>
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">2.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ growable nth-unsafe</span>
</span></span><span class="line"><span class="cl"> <span class="m">17 </span> <span class="m">2.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ byte-vector underlying>></span>
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 100.00 </span> <span class="m">0.00 </span> (byte-array)
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ slice length</span>
</span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">2.0 </span> <span class="m">0.00 </span> <span class="m">0.00 100.00 </span> <span class="m">0.00 </span> fixnum/i
</span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ string nth-unsafe</span>
</span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="nb">>
</span></span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="nb">number=
</span></span></span><span class="line"><span class="cl"> <span class="m">14 </span> <span class="m">4.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="nb">head?
</span></span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ byte-array length</span>
</span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="nb">>
</span></span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ byte-array nth-unsafe</span>
</span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="nb">integer?
</span></span></span><span class="line"><span class="cl"> <span class="m">14 </span> <span class="m">3.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">66.67 </span> <span class="m">0.00 </span> <span class="no">M\ fixnum positive>dec</span>
</span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">2.0 </span> <span class="m">0.00 </span> <span class="m">0.00 100.00 </span> <span class="m">0.00 </span> <span class="nb"><string>
</span></span></span><span class="line"><span class="cl"> <span class="m">14 </span> <span class="m">2.0 </span> <span class="m">0.00 </span> <span class="m">0.00 100.00 </span> <span class="m">0.00 </span> <span class="no">M\ sha2-state clone</span>
</span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 100.00 </span> <span class="m">0.00 </span> <span class="no">M\ checksum-state clone</span>
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 100.00 </span> <span class="m">0.00 </span> <span class="nb">(clone)
</span></span></span><span class="line"><span class="cl"> <span class="m">15 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 100.00 </span> <span class="m">0.00 </span> <span class="no">M\ uint-array clone</span>
</span></span><span class="line"><span class="cl"> <span class="m">16 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 100.00 </span> <span class="m">0.00 </span> <span class="nb">(clone)
</span></span></span><span class="line"><span class="cl"> <span class="m">14 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="no">M\ integer >base</span>
</span></span><span class="line"><span class="cl"> <span class="m">14 </span> <span class="m">1.0 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="m">0.00 </span> <span class="nb">reverse!
</span></span></span></code></pre></div><p>Visualizing the profile using the <a href="https://docs.factorcode.org/content/vocab-flamegraph.html">flamegraph
vocabulary</a> allows
us to dig a little bit further:</p>
<p>
<img src="https://re.factorcode.org/images/2025-08-21-flamegraph.png" alt="" width="1196" height="176" />
</p>
<p>Looks like a lot of generic dispatch, inefficient byte swapping, memory
allocations, and type conversions. Probably this could be made much faster by
looking into how we handle block checksums.</p>
<p>PRs welcome!</p>
Left to Right
https://re.factorcode.org/2025/08/left-to-right.html
Tue, 19 Aug 2025 08:00:00 -0700https://re.factorcode.org/2025/08/left-to-right.html<p>An article about <a href="https://graic.net/p/left-to-right-programming">Left to Right
Programming</a> was posted a few
days ago with a good discussions <a href="https://news.ycombinator.com/item?id=44942936">on Hacker
News</a> and <a href="https://lobste.rs/s/ik0pjv/left_right_programming">on
Lobsters</a>. It’s a nice read
with some syntax examples in different languages looking at some code blocks
that are structured left-to-right or right-to-left.</p>
<p>We can look at a few of the shared examples and think about how they might look
like naturally in <a href="https://factorcode.org">Factor</a>, which inherits a natural
<em>data flow</em> style due to the nature of being a <a href="https://concatenative.org/wiki/view/Concatenative%20language">concatenative
language</a>.</p>
<h3 id="the-challenge">The Challenge</h3>
<p>The blog post discusses <a href="https://github.com/Graicc/advent-of-code-2024/blob/0d7bf0f4f05489f0b5a09255fde47370084066e3/day_2/aoc2.py#L9">Graic’s 2024 Advent of Code
solution</a>,
written in Python:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nb">len</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">filter</span><span class="p">(</span><span class="k">lambda</span> <span class="n">line</span><span class="p">:</span> <span class="nb">all</span><span class="p">([</span><span class="nb">abs</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">>=</span> <span class="mi">1</span> <span class="ow">and</span> <span class="nb">abs</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o"><=</span> <span class="mi">3</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">line</span><span class="p">])</span> <span class="ow">and</span> <span class="p">(</span><span class="nb">all</span><span class="p">([</span><span class="n">x</span> <span class="o">></span> <span class="mi">0</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">line</span><span class="p">])</span> <span class="ow">or</span> <span class="nb">all</span><span class="p">([</span><span class="n">x</span> <span class="o"><</span> <span class="mi">0</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">line</span><span class="p">])),</span> <span class="n">diffs</span><span class="p">)))</span>
</span></span></code></pre></div><p>And compares it to an equivalent <em>improved</em> form in JavaScript:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">diffs</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">line</span> <span class="p">=></span>
</span></span><span class="line"><span class="cl"> <span class="nx">line</span><span class="p">.</span><span class="nx">every</span><span class="p">(</span><span class="nx">x</span> <span class="p">=></span> <span class="nb">Math</span><span class="p">.</span><span class="nx">abs</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="o">>=</span> <span class="mi">1</span> <span class="o">&&</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">abs</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="o"><=</span> <span class="mi">3</span><span class="p">)</span> <span class="o">&&</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nx">line</span><span class="p">.</span><span class="nx">every</span><span class="p">(</span><span class="nx">x</span> <span class="p">=></span> <span class="nx">x</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span> <span class="o">||</span> <span class="nx">line</span><span class="p">.</span><span class="nx">every</span><span class="p">(</span><span class="nx">x</span> <span class="p">=></span> <span class="nx">x</span> <span class="o"><</span> <span class="mi">0</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">).</span><span class="nx">length</span><span class="p">;</span>
</span></span></code></pre></div><p>There’s nothing quite like syntax wars – the nerd version of the <a href="https://en.wikipedia.org/wiki/Linguistics_wars">linguistic
wars</a> – to get people
interested in a topic. It is only one dimension, but perhaps the most visible
one, to evaluate a <a href="https://en.wikipedia.org/wiki/Programming_language">programming
language</a> on.</p>
<p>I usually get excited for any code that solves a problem, and I give kudos to
<a href="https://graic.net">Graic</a> for their efforts solving the <a href="https://adventofcode.com">Advent of
Code</a>! It’s often only working code that we can make
iterative improvements upon, and that should be appreciated.</p>
<h3 id="the-response">The Response</h3>
<p>On Hacker News, someone <a href="https://news.ycombinator.com/item?id=44943331">shared</a>
a version using Python’s list comprehensions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nb">len</span><span class="p">([</span><span class="n">line</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">diffs</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">all</span><span class="p">(</span><span class="mi">1</span> <span class="o"><=</span> <span class="nb">abs</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o"><=</span> <span class="mi">3</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="ow">and</span> <span class="p">(</span><span class="nb">all</span><span class="p">(</span><span class="n">x</span> <span class="o">></span> <span class="mi">0</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">line</span><span class="p">)</span> <span class="ow">or</span> <span class="nb">all</span><span class="p">(</span><span class="n">x</span> <span class="o"><</span> <span class="mi">0</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">line</span><span class="p">))])</span>
</span></span></code></pre></div><p>As well as a direct translation in Rust:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="n">diffs</span><span class="p">.</span><span class="n">iter</span><span class="p">().</span><span class="n">filter</span><span class="p">(</span><span class="o">|</span><span class="n">line</span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">line</span><span class="p">.</span><span class="n">iter</span><span class="p">().</span><span class="n">all</span><span class="p">(</span><span class="o">|</span><span class="n">x</span><span class="o">|</span><span class="w"> </span><span class="n">x</span><span class="p">.</span><span class="n">abs</span><span class="p">()</span><span class="w"> </span><span class="o">>=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="n">x</span><span class="p">.</span><span class="n">abs</span><span class="p">()</span><span class="w"> </span><span class="o"><=</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&&</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">(</span><span class="n">line</span><span class="p">.</span><span class="n">iter</span><span class="p">().</span><span class="n">all</span><span class="p">(</span><span class="o">|</span><span class="n">x</span><span class="o">|</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">></span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">line</span><span class="p">.</span><span class="n">iter</span><span class="p">().</span><span class="n">all</span><span class="p">(</span><span class="o">|</span><span class="n">x</span><span class="o">|</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mi">0</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}).</span><span class="n">count</span><span class="p">()</span><span class="w">
</span></span></span></code></pre></div><p>And a <em>single pass</em> version in Rust with improved performance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="n">diffs</span><span class="p">.</span><span class="n">iter</span><span class="p">().</span><span class="n">filter</span><span class="p">(</span><span class="o">|</span><span class="n">line</span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">iter</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">line</span><span class="p">.</span><span class="n">iter</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">range</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">iter</span><span class="p">.</span><span class="n">next</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="o">-</span><span class="mi">3</span><span class="o">..=-</span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="o">-</span><span class="mi">3</span><span class="o">..=-</span><span class="mi">1</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="mi">1</span><span class="o">..=</span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="mi">1</span><span class="o">..=</span><span class="mi">3</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">None</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">iter</span><span class="p">.</span><span class="n">all</span><span class="p">(</span><span class="o">|</span><span class="n">x</span><span class="o">|</span><span class="w"> </span><span class="n">range</span><span class="p">.</span><span class="n">contains</span><span class="p">(</span><span class="n">x</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}).</span><span class="n">count</span><span class="p">()</span><span class="w">
</span></span></span></code></pre></div><p>Someone else <a href="https://news.ycombinator.com/item?id=44950079">shared</a> a version
using <a href="https://numpy.org">numpy</a> arrays:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nb">sum</span><span class="p">(</span><span class="mi">1</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">diffs</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">((</span><span class="n">np</span><span class="o">.</span><span class="n">abs</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="o">>=</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&</span> <span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">abs</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="o"><=</span> <span class="mi">3</span><span class="p">))</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="ow">and</span> <span class="p">((</span><span class="n">line</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span><span class="o">.</span><span class="n">all</span><span class="p">()</span> <span class="ow">or</span> <span class="p">(</span><span class="n">line</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span><span class="o">.</span><span class="n">all</span><span class="p">()))</span>
</span></span></code></pre></div><p>And another comment <a href="https://news.ycombinator.com/item?id=44951150">shared</a> a
version in <a href="https://kotlinlang.org">Kotlin</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="line"><span class="cl"><span class="n">diffs</span><span class="p">.</span><span class="n">countIf</span> <span class="p">{</span> <span class="n">line</span> <span class="o">-></span>
</span></span><span class="line"><span class="cl"> <span class="n">line</span><span class="p">.</span><span class="n">all</span> <span class="p">{</span> <span class="n">abs</span><span class="p">(</span><span class="k">it</span><span class="p">)</span> <span class="k">in</span> <span class="mi">1</span><span class="o">..</span><span class="mi">3</span> <span class="p">}</span> <span class="n">and</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">line</span><span class="p">.</span><span class="n">all</span> <span class="p">{</span> <span class="k">it</span> <span class="p">></span> <span class="mi">0</span><span class="p">}</span> <span class="n">or</span>
</span></span><span class="line"><span class="cl"> <span class="n">line</span><span class="p">.</span><span class="n">all</span> <span class="p">{</span> <span class="k">it</span> <span class="p"><</span> <span class="mi">0</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>There was also a <a href="https://news.ycombinator.com/item?id=44943375">shared</a>
version in Python perhaps a bit more <em>idiomatic</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nb">sum</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">line</span><span class="p">:</span> <span class="nb">all</span><span class="p">(</span><span class="mi">1</span> <span class="o"><=</span> <span class="nb">abs</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o"><=</span> <span class="mi">3</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="ow">and</span> <span class="p">(</span><span class="nb">all</span><span class="p">(</span><span class="n">x</span> <span class="o">></span> <span class="mi">0</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">line</span><span class="p">)</span> <span class="ow">or</span> <span class="nb">all</span><span class="p">(</span><span class="n">x</span> <span class="o"><</span> <span class="mi">0</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">line</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl"> <span class="n">diffs</span><span class="p">))</span>
</span></span></code></pre></div><h3 id="what-about-factor">What about Factor?</h3>
<p>As you might imagine, I was also curious about what this would look like in
<a href="https://factorcode.org">Factor</a>.</p>
<p>Directly translating using <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a> does up to
<em>three passes</em> through the <code>line</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">[| line |
</span></span><span class="line"><span class="cl"> line [ <span class="nb">abs </span><span class="m">1 3 </span>between? ] <span class="nb">all?
</span></span></span><span class="line"><span class="cl"> line [ <span class="m">0 </span><span class="nb">> </span>] <span class="nb">all?
</span></span></span><span class="line"><span class="cl"> line [ <span class="m">0 </span><span class="nb">< </span>] <span class="nb">all? or and
</span></span></span><span class="line"><span class="cl">] <span class="nb">count
</span></span></span></code></pre></div><p>However, it would be better if we only check against zero if the first check
passes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">[| line |
</span></span><span class="line"><span class="cl"> line [ <span class="nb">abs </span><span class="m">1 3 </span>between? ] <span class="nb">all? </span>[
</span></span><span class="line"><span class="cl"> line [ <span class="m">0 </span><span class="nb">> </span>] <span class="nb">all?
</span></span></span><span class="line"><span class="cl"> line [ <span class="m">0 </span><span class="nb">< </span>] <span class="nb">all? or
</span></span></span><span class="line"><span class="cl"> ] [ <span class="no">f </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl">] <span class="nb">count
</span></span></span></code></pre></div><p>And, despite still being <em>three passes</em>, it is better if we only check negative
if the positive check fails:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">[| line |
</span></span><span class="line"><span class="cl"> line [ <span class="nb">abs </span><span class="m">1 3 </span>between? ] <span class="nb">all? </span>[
</span></span><span class="line"><span class="cl"> line [ <span class="m">0 </span><span class="nb">> </span>] <span class="nb">all? </span>[ <span class="no">t </span>] [
</span></span><span class="line"><span class="cl"> line [ <span class="m">0 </span><span class="nb">< </span>] <span class="nb">all?
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] [ <span class="no">f </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl">] <span class="nb">count
</span></span></span></code></pre></div><p>We can do these <em>short-circuiting</em> checks using a <a href="https://docs.factorcode.org/content/article-combinators.short-circuit.html">short-circuit
combinator</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ [ <span class="nb">abs </span><span class="m">1 3 </span>between? ] <span class="nb">all? </span>]
</span></span><span class="line"><span class="cl"> [ { [ [ <span class="m">0 </span><span class="nb">> </span>] <span class="nb">all? </span>] [ [ <span class="m">0 </span><span class="nb">< </span>] <span class="nb">all? </span>] } 1|| ]
</span></span><span class="line"><span class="cl"> } 1&&
</span></span><span class="line"><span class="cl">] <span class="nb">count
</span></span></span></code></pre></div><p>Checking that the
<a href="https://docs.factorcode.org/content/word-sgn,math.html">sign</a> of all the
numbers are the same only does <em>two passes</em> through the <code>line</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">[| line
</span></span><span class="line"><span class="cl"> line <span class="nb">empty? </span>[ <span class="no">t </span>] [
</span></span><span class="line"><span class="cl"> line [ <span class="nb">abs </span><span class="m">1 3 </span>between? ] <span class="nb">all?
</span></span></span><span class="line"><span class="cl"> line <span class="nb">unclip sgn </span>'[ <span class="nb">sgn </span>_ <span class="nb">= </span>] <span class="nb">all? and
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl">] <span class="nb">count
</span></span></span></code></pre></div><p>Comparing the first value to subsequent values does a <em>single pass</em> through the
<code>line</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl"> [ <span class="no">t </span>] [
</span></span><span class="line"><span class="cl"> <span class="nb">unclip </span>{ [ <span class="nb">abs </span><span class="m">1 3 </span>between? ] [ <span class="nb">sgn </span>] } 1&& [
</span></span><span class="line"><span class="cl"> '[ { [ <span class="nb">abs </span><span class="m">1 3 </span>between? ] [ <span class="nb">sgn </span>] } 1&& _ <span class="nb">= </span>] <span class="nb">all?
</span></span></span><span class="line"><span class="cl"> ] [ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-empty
</span></span></span><span class="line"><span class="cl">] <span class="nb">count
</span></span></span></code></pre></div><p>We often encourage writing <em>combinators</em> to do algorithmic things:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">all-same?</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">elt</span> <span class="nf">-- </span><span class="nv">obj/f</span> <span class="nf">) -- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> seq [ <span class="no">t </span>] [
</span></span><span class="line"><span class="cl"> <span class="nb">unclip </span>quot <span class="nb">call </span>[ '[ quot <span class="nb">call </span>_ <span class="nb">= </span>] <span class="nb">all? </span>] [ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-empty </span><span class="k">; inline
</span></span></span></code></pre></div><p>Which makes for a satisfyingly simple version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">[ [ { [ <span class="nb">abs </span><span class="m">1 3 </span>between? ] [ <span class="nb">sgn </span>] } 1&& ] all-same? ] <span class="nb">count
</span></span></span></code></pre></div><p>We could even do something like the Rust version above, getting the endpoints
from the first value to check that the subsequent ones match:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl"> [ <span class="no">t </span>] [
</span></span><span class="line"><span class="cl"> <span class="nb">unclip </span>{
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">1 3 </span>between? ] [ <span class="nb">drop </span><span class="m">1 3 </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">-3 -1 </span>between? ] [ <span class="nb">drop </span><span class="m">-3 -1 </span>] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="no">f f </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span>[ '[ _ _ between? ] <span class="nb">all? </span>] [ <span class="nb">nip </span>] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-empty
</span></span></span><span class="line"><span class="cl">] <span class="nb">count
</span></span></span></code></pre></div><p>And that simplifies even more if we use <a href="https://docs.factorcode.org/content/word-..%3D%2Cranges.html">range
syntax</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl"> [ <span class="no">t </span>] [
</span></span><span class="line"><span class="cl"> <span class="nb">unclip </span>{ <span class="m">1 </span>..= <span class="m">3 -3 </span>..= <span class="m">-1 </span>} [ in? ] <span class="nb">with find nip
</span></span></span><span class="line"><span class="cl"> [ '[ _ in? ] <span class="nb">all? </span>] [ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-empty
</span></span></span><span class="line"><span class="cl">] <span class="nb">count
</span></span></span></code></pre></div><p>As usual, <a href="https://wiki.c2.com/?ThereIsMoreThanOneWayToDoIt">there is more than one way to do
it</a>, and that’s okay.</p>
<p>Are any of these <em>best</em>? How else might we write this <em>better</em>?</p>
Pickle
https://re.factorcode.org/2025/08/pickle.html
Mon, 18 Aug 2025 08:00:00 -0700https://re.factorcode.org/2025/08/pickle.html<p>Pretty much everything <a href="https://en.wikipedia.org/wiki/Pickle">pickle</a> is
great: sweet, dill, bread and butter, full sour, half sour, gherkins, achar,
even pickleball. In addition to being both yummy and fun and <a href="https://journal.burningman.org/2013/07/black-rock-city/tales-from-the-playa/ask-and-you-shall-have-a-pickle/">a great
Tuesday night on the
Playa</a>,
<code>pickle</code> is also the name for <a href="https://docs.python.org/3/library/pickle.html">Python object
serialization</a>.</p>
<blockquote>
<p>There are currently 6 different protocols which can be used for pickling.
The higher the protocol used, the more recent the version of Python needed
to read the pickle produced.</p>
<ul>
<li>Protocol version 0 is the original “human-readable” protocol and is
backwards compatible with earlier versions of Python.</li>
<li>Protocol version 1 is an old binary format which is also compatible with
earlier versions of Python.</li>
<li>Protocol version 2 was introduced in Python 2.3. It provides much more
efficient pickling of <a href="https://docs.python.org/3/glossary.html#term-new-style-class">new-style
classes</a>.
Refer to <a href="https://peps.python.org/pep-0307/">PEP 307</a> for information about
improvements brought by protocol 2.</li>
<li>Protocol version 3 was added in Python 3.0. It has explicit support for
<a href="https://docs.python.org/3/library/stdtypes.html#bytes">bytes</a> objects and
cannot be unpickled by Python 2.x. This was the default protocol in Python
3.0–3.7.</li>
<li>Protocol version 4 was added in Python 3.4. It adds support for very large
objects, pickling more kinds of objects, and some data format optimizations.
It is the default protocol starting with Python 3.8. Refer to <a href="https://peps.python.org/pep-3154/">PEP
3154</a> for information about improvements
brought by protocol 4.</li>
<li>Protocol version 5 was added in Python 3.8. It adds support for
out-of-band data and speedup for in-band data. Refer to <a href="https://peps.python.org/pep-0574/">PEP
574</a> for information about improvements
brought by protocol 5.</li>
</ul>
</blockquote>
<p>While recently learning about how the <em>pickle protocol</em> works, I was able to
build a basic <em>unpickler</em> in <a href="https://factorcode.org">Factor</a>. The
implementation is about 300 lines of code, and has some decent tests. There
are a few more features we should add for completeness, but it’s a good
start!</p>
<p>I thought I’d go over a few parts of the implementation here.</p>
<p>The <em>pickle protocol</em> is stack-based, which we represent by a growable
<a href="https://docs.factorcode.org/content/article-vectors.html">vector</a>, and uses a
memoization feature to refer to objects by integer keys when they repeat in the
data stream, which we store in a
<a href="https://docs.factorcode.org/content/article-hashtables.html">hashtable</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">stack</span> V{ }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">memo</span> H{ }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">ERROR:</span> <span class="nc">invalid-memo</span> <span class="nv">key</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">get-memo</span> <span class="nf">( </span><span class="nv">i</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> memo <span class="nb">?at </span>[ stack <span class="nb">push </span>] [ invalid-memo ] <span class="nb">if </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">put-memo</span> <span class="nf">( </span><span class="nv">i</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ stack <span class="nb">last </span>] <span class="nb">dip </span>memo <span class="nb">set-at </span><span class="k">;
</span></span></span></code></pre></div><p>It also has the concept of <em>markers</em> which can be placed using the
<code>+marker+</code> symbol and then used, for example, to pop all items on the
stack until the last <em>marker</em> was seen:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">+marker+</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pop-from-marker</span> <span class="nf">( -- </span><span class="nv">items</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> +marker+ stack <span class="nb">last-index
</span></span></span><span class="line"><span class="cl"> [ <span class="m">1 </span><span class="nb">+ </span>stack <span class="nb">swap tail </span>] [ stack <span class="nb">shorten </span>] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>Unpickling starts with a <em>dispatch</em> loop that acts on each supported opcode.
We can use a <code>+no-return+</code> symbol to indicate that we are not ready to return
an object until the <code>STOP</code> opcode is seen.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">ERROR:</span> <span class="nc">invalid-opcode</span> <span class="nv">opcode</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">+no-return+</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">unpickle-dispatch</span> <span class="nf">( </span><span class="nv">opcode</span> <span class="nf">-- </span><span class="nv">value</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> +no-return+ <span class="nb">swap </span>{
</span></span><span class="line"><span class="cl"> <span class="c">! Protocol 0 and 1</span>
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: ( </span>[ load-mark ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: . </span>[ <span class="nb">drop </span>stack <span class="nb">pop </span>] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: 0 </span>[ load-pop ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: 1 </span>[ load-pop-mark ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: 2 </span>[ load-dup ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: F </span>[ load-float ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: I </span>[ load-int ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: J </span>[ load-binint ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: K </span>[ load-binint1 ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: L </span>[ load-long ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: M </span>[ load-binint2 ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: N </span>[ load-none ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: P </span>[ load-persid ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: Q </span>[ load-binpersid ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: R </span>[ load-reduce ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: S </span>[ load-string ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: T </span>[ load-binstring ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: U </span>[ load-short-binstring ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: V </span>[ load-unicode ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: X </span>[ load-binunicode ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: a </span>[ load-append ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: b </span>[ load-build ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: c </span>[ load-global ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: d </span>[ load-dict ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: } </span>[ load-empty-dict ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: e </span>[ load-appends ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: g </span>[ load-get ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: h </span>[ load-binget ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: i </span>[ load-inst ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: j </span>[ load-long-binget ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: l </span>[ load-list ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: ] </span>[ load-empty-list ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: o </span>[ load-obj ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: p </span>[ load-put ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: q </span>[ load-binput ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: r </span>[ load-long-binput ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: s </span>[ load-setitem ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: t </span>[ load-tuple ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: ) </span>[ load-empty-tuple ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: u </span>[ load-setitems ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: G </span>[ load-binfloat ] }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! Protocol 2</span>
</span></span><span class="line"><span class="cl"> { <span class="m">0x80 </span>[ load-proto ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x81 </span>[ load-newobj ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x82 </span>[ load-ext1 ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x83 </span>[ load-ext2 ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x84 </span>[ load-ext4 ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x85 </span>[ load-tuple1 ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x86 </span>[ load-tuple2 ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x87 </span>[ load-tuple3 ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x88 </span>[ load-true ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x89 </span>[ load-false ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x8a </span>[ load-long1 ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x8b </span>[ load-long4 ] }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! Protocol 3 (Python 3.x)</span>
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: B </span>[ load-binbytes ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: C </span>[ load-short-binbytes ] }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! Protocol 4 (Python 3.4-3.7)</span>
</span></span><span class="line"><span class="cl"> { <span class="m">0x8c </span>[ load-short-binunicode ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x8d </span>[ load-binunicode8 ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x8e </span>[ load-binbytes8 ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x8f </span>[ load-empty-set ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x90 </span>[ load-additems ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x91 </span>[ load-frozenset ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x92 </span>[ load-newobj-ex ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x93 </span>[ load-stack-global ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x94 </span>[ load-memoize ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x95 </span>[ load-frame ] }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! Protocol 5 (Python 3.8+)</span>
</span></span><span class="line"><span class="cl"> { <span class="m">0x96 </span>[ load-bytearray8 ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x97 </span>[ load-readonly-buffer ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x98 </span>[ load-next-buffer ] }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ invalid-opcode ]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div><p>With that, we can build our <code>unpickle</code> word that acts on an
<a href="https://docs.factorcode.org/content/word-input-stream,io.html">input-stream</a>,
first clearing state and then looping until we see an object to return:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">unpickle</span> <span class="nf">( -- </span><span class="nv">obj</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> stack <span class="nb">delete-all </span>memo <span class="nb">clear-assoc
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span>[ <span class="nb">drop read1 </span>unpickle-dispatch <span class="nb">dup </span>+no-return+ <span class="nb">= </span>] <span class="nb">loop </span><span class="k">;
</span></span></span></code></pre></div><p>For convenience, a <code>pickle></code> word acts on concrete data:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">GENERIC:</span> <span class="nf">pickle></span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">obj</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">string</span> <span class="nf">pickle></span> [ unpickle ] with-string-reader <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">byte-array</span> <span class="nf">pickle></span> binary [ unpickle ] with-byte-reader <span class="k">;
</span></span></span></code></pre></div><p>In addition, we needed to support Python’s string escapes which are slightly
different than the ones that Factor defines – mainly <code>\u####</code> and
<code>\U########</code>, and then add support for some of the basic class types that
we might encounter such as byte-arrays, decimals, timestamps, etc.</p>
<p>We currently do not support: persistent id’s, readonly vs read/write
buffers, out-of-band buffers, the object <em>build</em> opcode, and the <a href="https://peps.python.org/pep-0307/">extension
registry</a>. And of course, this is just
<em>unpickling</em>, we do not yet support <em>pickling</em> of Factor objects, although
that shouldn’t be too hard to add.</p>
<p>But, despite that, it works pretty well!</p>
<p>Here’s an example where we store some mixed data in a <code>pickles</code> file with
Python:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">>>></span> <span class="n">data</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"abc"</span><span class="p">,</span> <span class="mi">123</span><span class="p">,</span> <span class="mf">4.56</span><span class="p">,</span> <span class="p">{</span><span class="s2">"a"</span><span class="p">:</span><span class="mi">1</span><span class="o">+</span><span class="mi">5</span><span class="n">j</span><span class="p">},</span> <span class="p">{</span><span class="mi">17</span><span class="p">,</span><span class="mi">37</span><span class="p">,</span><span class="mi">52</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">>>></span> <span class="kn">import</span> <span class="nn">pickle</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">>>></span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'pickles'</span><span class="p">,</span> <span class="s1">'wb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span> <span class="n">pickle</span><span class="o">.</span><span class="n">dump</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span></code></pre></div><p>And then look at and then load that <code>pickles</code> file with Factor!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">tools.hexdump</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"pickles"</span> hexdump-file
</span></span><span class="line"><span class="cl"><span class="m">00000000 </span> <span class="m">80 04 95 54 00 00 00 00 00 00 00 </span>5d <span class="m">94 28 </span>8c <span class="m">03 </span> ...T.......].(..
</span></span><span class="line"><span class="cl"><span class="m">00000010 </span> <span class="m">61 62 63 94 </span>4b 7b <span class="m">47 40 12 </span>3d <span class="m">70 </span>a3 d7 0a 3d 7d abc.K{G@.=p...=}
</span></span><span class="line"><span class="cl"><span class="m">00000020 </span> <span class="m">94 </span>8c <span class="m">01 61 94 </span>8c <span class="m">08 62 75 69 </span>6c <span class="m">74 69 </span>6e <span class="m">73 94 </span> ...a...builtins.
</span></span><span class="line"><span class="cl"><span class="m">00000030 </span> 8c <span class="m">07 63 </span>6f 6d <span class="m">70 </span>6c <span class="m">65 78 94 93 94 47 </span>3f f0 <span class="m">00 </span> ..complex...G?..
</span></span><span class="line"><span class="cl"><span class="m">00000040 </span> <span class="m">00 00 00 00 00 47 40 14 00 00 00 00 00 00 86 94 </span> .....G@.........
</span></span><span class="line"><span class="cl"><span class="m">00000050 </span> <span class="m">52 94 73 </span>8f <span class="m">94 28 </span>4b <span class="m">11 </span>4b <span class="m">34 </span>4b <span class="m">25 90 65 </span>2e R.s..(K.K4K%.e.
</span></span><span class="line"><span class="cl">0000005f
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">pickle</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"pickles"</span> binary file-contents pickle> <span class="m">.
</span></span></span><span class="line"><span class="cl">V{ <span class="s">"abc"</span> <span class="m">123 4.56 </span>H{ { <span class="s">"a"</span> C{ <span class="m">1.0 5.0 </span>} } } HS{ <span class="m">17 52 37 </span>} }
</span></span></code></pre></div><p>This is available in the latest <a href="https://github.com/factor/factor/blob/master/extra/pickle/pickle.factor">development
version</a>.</p>
Marp
https://re.factorcode.org/2025/08/marp.html
Fri, 15 Aug 2025 08:00:00 -0700https://re.factorcode.org/2025/08/marp.html<p><a href="https://marp.app">Marp</a>, also known as the <strong>Markdown Presentation
Ecosystem</strong>, is a way to “<em>create beautiful slide decks using an intuitive
Markdown experience</em>”.</p>
<p>If you’ve seen a <a href="https://www.youtube.com/watch?v=OLh61q4c4XE">presentation about
Factor</a> before, you might notice
that we have a <a href="https://docs.factorcode.org/content/vocab-slides.html">slides
vocabulary</a> that allows
us to build <a href="https://re.factorcode.org/2010/10/presentations-in-factor.html">presentations in Factor</a>
and then present it using the <a href="https://docs.factorcode.org/content/article-ui.html">Factor
UI</a>. Many of our previous
talks are shared in the <a href="https://github.com/factor/factor-talks">factor-talks</a>
repository, including the <a href="https://github.com/factor/factor-talks/blob/main/svfig-talk/svfig-talk.factor">slides for the SVFIG
talk</a>.</p>
<p>Today, I thought it would be fun to merge these two concepts together, and
allow us to build <em>slides</em> in <a href="https://factorcode.org">Factor</a> but do the
<em>presentation</em> using <a href="https://marp.app">Marp</a>.</p>
<h3 id="what-does-a-factor-slide-look-like">What does a Factor slide look like?</h3>
<p>Let’s start by examining a <code>$slide</code>, and see what it looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ $slide <span class="s">"Quotations"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Quotation: un-named blocks of code"</span>
</span></span><span class="line"><span class="cl"> { $code <span class="s">"[ \"Hello, World\" print ]"</span> }
</span></span><span class="line"><span class="cl"> <span class="s">"Combinators: words taking quotations"</span>
</span></span><span class="line"><span class="cl"> { $code <span class="s">"10 dup 0 < [ 1 - ] [ 1 + ] if ."</span> }
</span></span><span class="line"><span class="cl"> { $code <span class="s">"{ -1 1 -2 0 3 } [ 0 max ] map ."</span> }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>As with most <a href="https://www.dictionary.com/browse/businessy">businessy</a> slides,
it starts with a <em>title</em>, and then has a sequence of various blocks to render.</p>
<h3 id="what-would-a-marp-slide-look-like">What would a Marp slide look like?</h3>
<p>We can manually translate this to a similar-looking slide using
<a href="https://en.wikipedia.org/wiki/Markdown">Markdown</a>:</p>
<pre tabindex="0"><code>---
# Quotations
- Quotation: un-named blocks of code
```factor
[ "Hello, World" print ]
```
- Combinators: words taking quotations
```factor
10 dup 0 < [ 1 - ] [ 1 + ] if .
```
```factor
{ -1 1 -2 0 3 } [ 0 max ] map .
```
</code></pre><h3 id="can-we-automate-this">Can we automate this?</h3>
<p>Of course!</p>
<p>Our <code>slides</code> vocabulary uses <a href="https://docs.factorcode.org/content/article-element-types.html">elements from the help
system</a> to
provide markup (which is how we render it in the Factor user interface). These
elements are specified as a kind of <em>array</em>, with <em>typing</em> provided by their
first argument.</p>
<p>We can leverage this to manually support a few types:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">GENERIC:</span> <span class="nf">write-marp</span> <span class="nf">( </span><span class="nv">element</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">string</span> <span class="nf">write-marp</span> <span class="nb">write </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">array</span> <span class="nf">write-marp</span>
</span></span><span class="line"><span class="cl"> <span class="nb">unclip </span>{
</span></span><span class="line"><span class="cl"> { <span class="no">\ $slide</span> [ write-slide ] }
</span></span><span class="line"><span class="cl"> { <span class="no">\ $code</span> [ write-code ] }
</span></span><span class="line"><span class="cl"> { <span class="no">\ $link</span> [ write-link ] }
</span></span><span class="line"><span class="cl"> { <span class="no">\ $vocab-link</span> [ write-vocab-link ] }
</span></span><span class="line"><span class="cl"> { <span class="no">\ $url</span> [ write-url ] }
</span></span><span class="line"><span class="cl"> { <span class="no">\ $snippet</span> [ write-snippet ] }
</span></span><span class="line"><span class="cl"> [ write-marp [ write-marp ] <span class="nb">each </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div><p>Using that, we can create a Marp file, with some chosen style:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-marp-file</span> <span class="nf">( </span><span class="nv">slides</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"---
</span></span></span><span class="line"><span class="cl"><span class="s">marp: true
</span></span></span><span class="line"><span class="cl"><span class="s">theme: gaia
</span></span></span><span class="line"><span class="cl"><span class="s">paginate: true
</span></span></span><span class="line"><span class="cl"><span class="s">backgroundColor: #1e1e2e
</span></span></span><span class="line"><span class="cl"><span class="s">color: #cdd6f4
</span></span></span><span class="line"><span class="cl"><span class="s">style: |
</span></span></span><span class="line"><span class="cl"><span class="s"> section {
</span></span></span><span class="line"><span class="cl"><span class="s"> font-family: 'SF Pro Display', 'Segoe UI', sans-serif;
</span></span></span><span class="line"><span class="cl"><span class="s"> }
</span></span></span><span class="line"><span class="cl"><span class="s"> h1 {
</span></span></span><span class="line"><span class="cl"><span class="s"> color: #89b4fa;
</span></span></span><span class="line"><span class="cl"><span class="s"> }
</span></span></span><span class="line"><span class="cl"><span class="s"> h2 {
</span></span></span><span class="line"><span class="cl"><span class="s"> color: #94e2d5;
</span></span></span><span class="line"><span class="cl"><span class="s"> }
</span></span></span><span class="line"><span class="cl"><span class="s"> h3 {
</span></span></span><span class="line"><span class="cl"><span class="s"> color: #f5c2e7;
</span></span></span><span class="line"><span class="cl"><span class="s"> }
</span></span></span><span class="line"><span class="cl"><span class="s"> code {
</span></span></span><span class="line"><span class="cl"><span class="s"> background-color: #313244;
</span></span></span><span class="line"><span class="cl"><span class="s"> color: #cdd6f4;
</span></span></span><span class="line"><span class="cl"><span class="s"> border-radius: 0.25em;
</span></span></span><span class="line"><span class="cl"><span class="s"> }
</span></span></span><span class="line"><span class="cl"><span class="s"> pre {
</span></span></span><span class="line"><span class="cl"><span class="s"> background-color: #313244;
</span></span></span><span class="line"><span class="cl"><span class="s"> border-radius: 0.5em;
</span></span></span><span class="line"><span class="cl"><span class="s"> }
</span></span></span><span class="line"><span class="cl"><span class="s"> ul {
</span></span></span><span class="line"><span class="cl"><span class="s"> list-style: none;
</span></span></span><span class="line"><span class="cl"><span class="s"> padding-left: 0;
</span></span></span><span class="line"><span class="cl"><span class="s"> }
</span></span></span><span class="line"><span class="cl"><span class="s"> ul li::before {
</span></span></span><span class="line"><span class="cl"><span class="s"> content: \"▸ \";
</span></span></span><span class="line"><span class="cl"><span class="s"> color: #89b4fa;
</span></span></span><span class="line"><span class="cl"><span class="s"> font-weight: bold;
</span></span></span><span class="line"><span class="cl"><span class="s"> }"</span> <span class="nb">print </span>[ write-marp ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>And now we can use that to generate a Marp file of our talk!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"~/FACTOR.md"</span> utf8 [
</span></span><span class="line"><span class="cl"> svfig-slides write-marp-file
</span></span><span class="line"><span class="cl"> ] with-file-writer
</span></span></code></pre></div><p>And then use the <a href="https://github.com/marp-team/marp-cli/">Marp CLI</a> to convert
it to HTML and open in a browser!</p>
<pre tabindex="0"><code>$ marp FACTOR.md
[ INFO ] Converting 1 markdown...
[ INFO ] FACTOR.md => FACTOR.html
$ open -a Safari FACTOR.html
</code></pre><p>And then view the slides, embedded below for convenience:</p>
<iframe src="https://re.factorcode.org/html/FACTOR.html"></iframe>
<p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/marp/marp.factor">GitHub</a>.</p>
Data Formats
https://re.factorcode.org/2025/07/data-formats.html
Thu, 31 Jul 2025 08:00:00 -0700https://re.factorcode.org/2025/07/data-formats.html<p>A <em>data format</em> is a standardized way of encoding, storing, and representing
data, allowing different software applications to interpret and process it. I
was reminded of this recently when a link was shared to <a href="https://cborbook.com/introduction/from_xml_to_json_to_cbor.html">From XML to JSON to
CBOR</a> which
discusses three <em>pivotal data formats</em> and their evolution.</p>
<p>Some <em>data formats</em> that <a href="https://factorcode.org">Factor</a> supports in its
<a href="https://docs.factorcode.org/content/article-vocab-index.html">extensive standard
library</a>:</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Bencode">Bencode</a> or <em>BitTorrent encoding</em></li>
<li><a href="https://en.wikipedia.org/wiki/BSON">BSON</a> or <em>Binary JSON</em></li>
<li><a href="https://en.wikipedia.org/wiki/CBOR">CBOR</a> or <em>Concise Binary Object Representation</em></li>
<li><a href="https://en.wikipedia.org/wiki/Comma-separated_values">CSV</a> or <em>Comma-separated values</em></li>
<li><a href="https://en.wikipedia.org/wiki/JSON">JSON</a> or <em>JavaScript Object Notation</em></li>
<li><a href="https://en.wikipedia.org/wiki/MessagePack">MessagePack</a> – <em>It’s like JSON, but fast and small</em></li>
<li><a href="https://toml.io/en/">TOML</a> or <em>Tom’s Obvious Minimal Language</em></li>
<li><a href="https://www.hxa.name/txon/">TXON</a> or <em>Text Object Notation</em></li>
<li><a href="https://en.wikipedia.org/wiki/XML">XML</a> or <em>Extensible Markup Language</em></li>
<li><a href="https://en.wikipedia.org/wiki/YAML">YAML</a> or <em>Yet Another Markup Language</em></li>
</ul>
<p>Most of these are general purpose and can encode most basic object types,
including nested structures. With some exceptions – for example <code>csv</code>
doesn’t support nesting, <code>txon</code> supports only string keys and values, and
<code>xml</code> requires some manual <em>object-to-XML</em> conversion.</p>
<p>In any event, here is an example showing data that round-trips through seven
different <em>data formats</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> LH{
</span></span><span class="line"><span class="cl"> { <span class="s">"name"</span> <span class="s">"Factor"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"age"</span> <span class="m">22 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"list"</span> { <span class="m">4 8 15 16 23 42 </span>} }
</span></span><span class="line"><span class="cl"> { <span class="s">"map"</span> { LH{ { <span class="s">"one"</span> <span class="m">1 </span>} { <span class="s">"two"</span> <span class="m">2 </span>} } } }
</span></span><span class="line"><span class="cl"> } [
</span></span><span class="line"><span class="cl"> >json json>
</span></span><span class="line"><span class="cl"> >msgpack msgpack>
</span></span><span class="line"><span class="cl"> >toml toml>
</span></span><span class="line"><span class="cl"> >cbor cbor>
</span></span><span class="line"><span class="cl"> >bson bson>
</span></span><span class="line"><span class="cl"> >bencode bencode>
</span></span><span class="line"><span class="cl"> >yaml yaml>
</span></span><span class="line"><span class="cl"> ] <span class="nb">keep = </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">t
</span></span></span></code></pre></div><p>There are two more <em>data formats</em> that might not be obvious, but also
round-trip:</p>
<ul>
<li>The <a href="https://docs.factorcode.org/content/article-serialize.html">serialize
vocabulary</a>:
<code>object>bytes bytes>object</code></li>
<li>The <a href="https://docs.factorcode.org/content/article-prettyprint.html">prettyprint
vocabulary</a>:
<code>[ unparse ] without-limits eval( -- obj )</code></li>
</ul>
<p>And there are probably many more useful ones we could add to the standard
library. For example, <a href="https://ziglang.org">Zig</a> has a new data format I’d
love to support someday called
<a href="https://ziglang.org/documentation/master/std/#std.zon">Zon</a> or <em>Zig Object
Notation</em>.</p>
<p>PR’s welcome!</p>
Tribonacci Numbers
https://re.factorcode.org/2025/07/tribonacci-numbers.html
Thu, 24 Jul 2025 08:00:00 -0700https://re.factorcode.org/2025/07/tribonacci-numbers.html<p><a href="https://mathworld.wolfram.com/TribonacciNumber.html">Tribonacci numbers</a> are a
3 argument version of the <a href="https://en.wikipedia.org/wiki/Fibonacci_sequence">Fibonacci
sequence</a>. Specifically, they
are defined by the first 3 numbers in the sequence and then a recursive
formula:</p>
<ul>
<li>T(0) = 0</li>
<li>T(1) = 1</li>
<li>T(2) = 1</li>
<li>T(n) = T(n-1) + T(n-2) + T(n-3)</li>
</ul>
<p><em>Note: Some other tribonacci sequences have different starting values, and in
general are defined by their first 3 values: <code>a</code>, <code>b</code>, and <code>c</code> — in this
case: <code>a=0</code>, <code>b=1</code>, <code>c=1</code>.</em></p>
<p>Let’s go ahead and build this in <a href="https://factorcode.org">Factor</a>, making sure
to use <a href="https://docs.factorcode.org/content/article-memoize.html">memoization</a>
to improve performance!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MEMO:</span> <span class="nf">tribonacci</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">r</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { <span class="m">0 </span>[ <span class="m">0 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">1 </span>[ <span class="m">1 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">2 </span>[ <span class="m">1 </span>] }
</span></span><span class="line"><span class="cl"> [ <span class="m">3 2 1 </span>[ <span class="nb">- </span>tribonacci ] <span class="nb">tri-curry@ tri + + </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div><p>And write some tests to make sure it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="m">0 </span>} [ <span class="m">0 </span>tribonacci ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">1 </span>} [ <span class="m">1 </span>tribonacci ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">1 </span>} [ <span class="m">2 </span>tribonacci ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">2 </span>} [ <span class="m">3 </span>tribonacci ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="m">98079530178586034536500564 </span>} [ <span class="m">100 </span>tribonacci ] unit-test
</span></span></code></pre></div><p>And just for fun, we can easily print the 10,000th tribonacci number:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10,000 </span>tribonacci <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">104970887945512179720156402562881884282669908701507376396226849
</span></span></span><span class="line"><span class="cl"><span class="m">507715880964253502566653834053217081868282654421270875998847083
</span></span></span><span class="line"><span class="cl"><span class="m">354963158781389172213221498323513985337426272701217011859592971
</span></span></span><span class="line"><span class="cl"><span class="m">352141098891805206420900134582429646587046427436005495255798883
</span></span></span><span class="line"><span class="cl"><span class="m">246073618031475000139396853427893513436406142049154715999765086
</span></span></span><span class="line"><span class="cl"><span class="m">094048789520150380301629928195007762620367936007950031281567424
</span></span></span><span class="line"><span class="cl"><span class="m">852796946143324764098612546824050145564601448681311727789904337
</span></span></span><span class="line"><span class="cl"><span class="m">617216196640219842106590802021786745819241462665059249132141577
</span></span></span><span class="line"><span class="cl"><span class="m">894579630772922197404756269452287431128951784520600882264401580
</span></span></span><span class="line"><span class="cl"><span class="m">836789430310052922795100107599314150699335030135447534365101972
</span></span></span><span class="line"><span class="cl"><span class="m">212856424810302264081751728175450009246816642287808303756431642
</span></span></span><span class="line"><span class="cl"><span class="m">190229866618955021220164854376449799003570929253752356712382127
</span></span></span><span class="line"><span class="cl"><span class="m">291554067625436999262482815025295032640196317590693733687242877
</span></span></span><span class="line"><span class="cl"><span class="m">497883233594054067312780455244938306491277888412407808533948897
</span></span></span><span class="line"><span class="cl"><span class="m">005235795246966581196760639279751925372382092157588570477362227
</span></span></span><span class="line"><span class="cl"><span class="m">283625689293926489206546461529591280578644740130681436616270947
</span></span></span><span class="line"><span class="cl"><span class="m">399935554399753037011040986396134560464682737227548122835065232
</span></span></span><span class="line"><span class="cl"><span class="m">761643277488106437767837022869773684192764695319605886553868567
</span></span></span><span class="line"><span class="cl"><span class="m">702494744004565987231502563031894899087422143067624588247239246
</span></span></span><span class="line"><span class="cl"><span class="m">209236949388263940118095456771744163270015460183378322484380736
</span></span></span><span class="line"><span class="cl"><span class="m">904832346845106349873481841268013335459411318711113804921252701
</span></span></span><span class="line"><span class="cl"><span class="m">685818949515305915278859806703369590630798205066890114557299609
</span></span></span><span class="line"><span class="cl"><span class="m">200541159049091178364828975083353240663240973018961078785884623
</span></span></span><span class="line"><span class="cl"><span class="m">754180387714833134965646472067988582492257260500195932241449015
</span></span></span><span class="line"><span class="cl"><span class="m">697681978518891318647464646160278703772005509287994250063276822
</span></span></span><span class="line"><span class="cl"><span class="m">738895989306087163591588324360743585286169954643632915363025595
</span></span></span><span class="line"><span class="cl"><span class="m">643953167630122098912124456056268912098768342566371268760363924
</span></span></span><span class="line"><span class="cl"><span class="m">442889888379550780367698680933768551834171582391185682035582704
</span></span></span><span class="line"><span class="cl"><span class="m">931276111575092809048515510639048207408965825151544995661087361
</span></span></span><span class="line"><span class="cl"><span class="m">202117757159833101907264820660711114748638250941161679012607717
</span></span></span><span class="line"><span class="cl"><span class="m">553825173370181059141538350424453457744455331742600716423279982
</span></span></span><span class="line"><span class="cl"><span class="m">267989192083554417406081238679478603607583176975551563763168876
</span></span></span><span class="line"><span class="cl"><span class="m">520982495090528860247780663854311039370938787207531507059750255
</span></span></span><span class="line"><span class="cl"><span class="m">331836155899138120278616825251002413907470627146790383955760350
</span></span></span><span class="line"><span class="cl"><span class="m">784999437437547112858192681843997153570155437021076042591359601
</span></span></span><span class="line"><span class="cl"><span class="m">550872111063258125973939212682654169479750147837067955643100320
</span></span></span><span class="line"><span class="cl"><span class="m">187644616709502788318461621953487535087913200131635597400731137
</span></span></span><span class="line"><span class="cl"><span class="m">771945144547828956806094091586378911072748574105653223033633806
</span></span></span><span class="line"><span class="cl"><span class="m">849090649589109247111615640029564835225683410527907839461600211
</span></span></span><span class="line"><span class="cl"><span class="m">731583707928939187244387112961247901688359209735935698701057372
</span></span></span><span class="line"><span class="cl"><span class="m">453283021738155105525668197910700091097683706470201003266177476
</span></span></span><span class="line"><span class="cl"><span class="m">093728339123616912247097871463717510180588398935948725584082432
</span></span></span><span class="line"><span class="cl"><span class="m">8
</span></span></span></code></pre></div><p>And that, of course, shows off our <a href="https://docs.factorcode.org/content/word-bignum,math.html">bignum
integers</a> holding
almost 8,800 bits.</p>
<p>But, if we want to calculate the 100,000th tribonacci number, we get a <em>retain
stack overflow</em>. This error depends on how many recursions have to be performed
before a previously memoized answer is found:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">100,000 </span>tribonacci
</span></span><span class="line"><span class="cl">Retain stack overflow
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Type :help for debugging help.
</span></span></code></pre></div><p>Instead, we would need to approach it by calculating interim values:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 </span>[1..b] [
</span></span><span class="line"><span class="cl"> <span class="m">10,000 </span><span class="nb">* dup </span>tribonacci <span class="nb">log2
</span></span></span><span class="line"><span class="cl"> <span class="s">"%s = %s\n"</span> printf
</span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"><span class="m">10000 </span><span class="nb">= </span><span class="m">8789
</span></span></span><span class="line"><span class="cl"><span class="m">20000 </span><span class="nb">= </span><span class="m">17581
</span></span></span><span class="line"><span class="cl"><span class="m">30000 </span><span class="nb">= </span><span class="m">26372
</span></span></span><span class="line"><span class="cl"><span class="m">40000 </span><span class="nb">= </span><span class="m">35164
</span></span></span><span class="line"><span class="cl"><span class="m">50000 </span><span class="nb">= </span><span class="m">43955
</span></span></span><span class="line"><span class="cl"><span class="m">60000 </span><span class="nb">= </span><span class="m">52747
</span></span></span><span class="line"><span class="cl"><span class="m">70000 </span><span class="nb">= </span><span class="m">61538
</span></span></span><span class="line"><span class="cl"><span class="m">80000 </span><span class="nb">= </span><span class="m">70330
</span></span></span><span class="line"><span class="cl"><span class="m">90000 </span><span class="nb">= </span><span class="m">79121
</span></span></span><span class="line"><span class="cl"><span class="m">100000 </span><span class="nb">= </span><span class="m">87913
</span></span></span></code></pre></div><p>An iterative implementation might do more computation when called repeatedly,
due to lacking memoization, but gets us directly to the answer we want:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">tribonacci</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">r</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { <span class="m">0 </span>[ <span class="m">0 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">1 </span>[ <span class="m">1 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">2 </span>[ <span class="m">1 </span>] }
</span></span><span class="line"><span class="cl"> [ [ <span class="m">0 1 1 </span>] <span class="nb">dip </span><span class="m">2 </span><span class="nb">- </span>[ [ <span class="nb">+ + </span>] <span class="nb">2keep rot </span>] <span class="nb">times 2nip </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div><p>We can see that the millionth tribonacci number has almost 880,000 bits!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10,000 </span>tribonacci <span class="nb">log2 </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">8789
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">100,000 </span>tribonacci <span class="nb">log2 </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">87913
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1,000,000 </span>tribonacci <span class="nb">log2 </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">879144
</span></span></span></code></pre></div><p>We could combine <em>memoization</em> and <em>iterative computation</em> by implementing an
inline cache:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">tribonacci</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">r</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> V{ <span class="m">0 1 1 </span>} :> seq
</span></span><span class="line"><span class="cl"> n seq <span class="nb">?nth </span>[
</span></span><span class="line"><span class="cl"> n <span class="m">1 </span><span class="nb">+ next-power-of-2 dup
</span></span></span><span class="line"><span class="cl"> seq capacity <span class="nb">> </span>[ seq expand ] [ <span class="nb">drop </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> seq <span class="m">3 </span><span class="nb">tail-slice* first3 </span>n seq <span class="nb">length </span><span class="m">1 </span><span class="nb">- -
</span></span></span><span class="line"><span class="cl"> [ [ <span class="nb">+ + </span>] <span class="nb">2keep rot dup </span>seq <span class="nb">push </span>] <span class="nb">times 2nip
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">unless* </span><span class="k">;
</span></span></span></code></pre></div><p>Now subsequent calls are fast, at the expense of a possibly large cache!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="c">! First computation is slow</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">100,000 </span>tribonacci ] time <span class="m">.
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">0.211418458 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">! Second computation is fast (cached)</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">100,000 </span>tribonacci ] time <span class="m">.
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">0.000272875 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">! Other requests in range are fast (cached)</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">99,999 </span>tribonacci ] time <span class="m">.
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">0.000344792 </span>seconds
</span></span></code></pre></div><p>In this case, the cache now holds 100,000 integers in about 550 MB of memory.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">100,000 </span><iota> [ tribonacci size ] <span class="nb">map-sum </span>megs
</span></span><span class="line"><span class="cl"><span class="m">543.9405975341797 </span>MB
</span></span></code></pre></div><p>Nifty!</p>
Tak Function
https://re.factorcode.org/2025/07/tak-function.html
Wed, 23 Jul 2025 08:00:00 -0700https://re.factorcode.org/2025/07/tak-function.html<p>The <a href="https://en.wikipedia.org/wiki/Tak_(function)">Tak function</a> is a recursive
function, named after Ikuo Takeuchi, used sometimes as a programming language
benchmark. On the <a href="https://concatenative.org/wiki/">Concatenative Wiki</a>, I
noticed a page with <a href="https://concatenative.org/wiki/view/Tak%20function">concatenative implementations of the Tak
function</a>, and wanted to
explore some different ways to build it in <a href="https://factorcode.org">Factor</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">tak</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">z</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">y</span> <span class="o"><</span> <span class="n">x</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">tak</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">tak</span><span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="n">tak</span><span class="p">(</span><span class="n">y</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">z</span><span class="p">,</span> <span class="n">x</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="n">tak</span><span class="p">(</span><span class="n">z</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">y</span>
</span></span></code></pre></div><p><em>Note: there are two variants of this function defined, some return <code>z</code>
instead of <code>y</code>.</em></p>
<p>Some parameters cause an extremely large number of recursions. For example,
<code>tak(18, 12, 25)</code> makes <code>76,527,789</code> function calls! Measuring the
execution time demonstrates that:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">In</span> <span class="p">[</span><span class="mi">23</span><span class="p">]:</span> <span class="o">%</span><span class="n">time</span> <span class="n">tak</span><span class="p">(</span><span class="mi">18</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">25</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">CPU</span> <span class="n">times</span><span class="p">:</span> <span class="n">user</span> <span class="mf">1.79</span> <span class="n">s</span><span class="p">,</span> <span class="n">sys</span><span class="p">:</span> <span class="mf">4.89</span> <span class="n">ms</span><span class="p">,</span> <span class="n">total</span><span class="p">:</span> <span class="mf">1.79</span> <span class="n">s</span>
</span></span><span class="line"><span class="cl"><span class="n">Wall</span> <span class="n">time</span><span class="p">:</span> <span class="mf">1.79</span> <span class="n">s</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Out</span> <span class="p">[</span><span class="mi">23</span><span class="p">]:</span> <span class="mi">25</span>
</span></span></code></pre></div><p>A direct translation of the Python code might look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">tak</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nv">z</span> <span class="nf">-- </span><span class="nv">result</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> y x <span class="nb">< </span>[
</span></span><span class="line"><span class="cl"> x <span class="m">1 </span><span class="nb">- </span>y z tak
</span></span><span class="line"><span class="cl"> y <span class="m">1 </span><span class="nb">- </span>z x tak
</span></span><span class="line"><span class="cl"> z <span class="m">1 </span><span class="nb">- </span>x y tak
</span></span><span class="line"><span class="cl"> tak
</span></span><span class="line"><span class="cl"> ] [ y ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>And we can see that it works and is almost <strong>6x</strong> faster than Python.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">18 12 25 </span>tak ] time <span class="m">.
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">0.307333458 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="m">25
</span></span></span></code></pre></div><p>But, we could get all <em>stacky</em> and build it like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">tak</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nv">z</span> <span class="nf">-- </span><span class="nv">result</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">2over > </span>[
</span></span><span class="line"><span class="cl"> [ [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">2dip </span>tak ] <span class="nb">3keep
</span></span></span><span class="line"><span class="cl"> [ [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">dip rot </span>tak ] <span class="nb">3keep
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="nb">- -rot </span>tak tak
</span></span><span class="line"><span class="cl"> ] [ <span class="nb">drop nip </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Or, <em>cleavy</em> and build it like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">tak</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nv">z</span> <span class="nf">-- </span><span class="nv">result</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">2over > </span>[
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">2dip </span>tak ]
</span></span><span class="line"><span class="cl"> [ [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">dip rot </span>tak ]
</span></span><span class="line"><span class="cl"> [ <span class="m">1 </span><span class="nb">- -rot </span>tak ]
</span></span><span class="line"><span class="cl"> } <span class="nb">3cleave </span>tak
</span></span><span class="line"><span class="cl"> ] [ <span class="nb">drop nip </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Or, <em>composey</em> and build it like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">tak</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nv">z</span> <span class="nf">-- </span><span class="nv">result</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">2over > </span>[
</span></span><span class="line"><span class="cl"> [ ] [ <span class="nb">rot </span>] [ <span class="nb">-rot </span>] [ '[ @ [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">2dip </span>tak ] ] <span class="nb">tri@ 3tri </span>tak
</span></span><span class="line"><span class="cl"> ] [ <span class="nb">drop nip </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Or, reduce some unnecessary recursion with a more efficient version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">tak</span> <span class="nf">( </span><span class="nv">x!</span> <span class="nv">y!</span> <span class="nv">z!</span> <span class="nf">-- </span><span class="nv">result</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ x y <span class="nb">> </span>] [
</span></span><span class="line"><span class="cl"> x y :> <span class="nf">( </span><span class="nv">oldx</span> <span class="nv">oldy</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> x <span class="m">1 </span><span class="nb">- </span>y z tak x!
</span></span><span class="line"><span class="cl"> y <span class="m">1 </span><span class="nb">- </span>z oldx tak y!
</span></span><span class="line"><span class="cl"> x y <span class="nb"><= </span>[ z <span class="m">1 </span><span class="nb">- </span>oldx oldy tak z! ] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while </span>y <span class="k">;
</span></span></span></code></pre></div><p>But, it turns out there is a <a href="https://mathworld.wolfram.com/TAKFunction.html">simpler solution</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">tak</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nv">z</span> <span class="nf">-- </span><span class="nv">result</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ y x <span class="nb">>= </span>] [ y ] }
</span></span><span class="line"><span class="cl"> { [ y z <span class="nb"><= </span>] [ z ] }
</span></span><span class="line"><span class="cl"> [ x ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="k">;
</span></span></span></code></pre></div><p>Which could be written more concisely in one line:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">tak</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nv">z</span> <span class="nf">-- </span><span class="nv">result</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> y x <span class="nb">>= </span>[ y ] [ y z <span class="nb"><= </span>z x <span class="nb">? </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>And that is the fastest of all:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">18 12 25 </span>tak ] time <span class="m">.
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">0.000073916 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="m">25
</span></span></span></code></pre></div><p>Fun.</p>
Ask OK?
https://re.factorcode.org/2025/07/ask-ok.html
Tue, 22 Jul 2025 08:00:00 -0700https://re.factorcode.org/2025/07/ask-ok.html<p>To illustrate some varied aspects of programming with
<a href="https://factorcode.org">Factor</a>, I wanted to show how you might write an
example used in the <a href="https://docs.python.org/3/tutorial/controlflow.html#default-argument-values">Python
documentation</a>
to demonstrate control flow and default values:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">ask_ok</span><span class="p">(</span><span class="n">prompt</span><span class="p">,</span> <span class="n">retries</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">complaint</span><span class="o">=</span><span class="s1">'Yes or no, please!'</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">ok</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">ok</span> <span class="ow">in</span> <span class="p">(</span><span class="s1">'y'</span><span class="p">,</span> <span class="s1">'ye'</span><span class="p">,</span> <span class="s1">'yes'</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">ok</span> <span class="ow">in</span> <span class="p">(</span><span class="s1">'n'</span><span class="p">,</span> <span class="s1">'no'</span><span class="p">,</span> <span class="s1">'nop'</span><span class="p">,</span> <span class="s1">'nope'</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl"> <span class="n">retries</span> <span class="o">=</span> <span class="n">retries</span> <span class="o">-</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">retries</span> <span class="o"><</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="ne">IOError</span><span class="p">(</span><span class="s1">'invalid user response'</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="n">complaint</span><span class="p">)</span>
</span></span></code></pre></div><p>You can see how it works in Python:</p>
<pre tabindex="0"><code>>>> ask_ok("Continue? ")
Continue? y
True
>>> ask_ok("Continue? ")
Continue? no
False
>>> ask_ok("Continue? ")
Continue? r
Yes or no, please!
Continue? r
Yes or no, please!
Continue? r
Yes or no, please!
Continue? r
Yes or no, please!
Continue? r
Traceback (most recent call last):
File "<python-input-1>", line 1, in <module>
ask_ok("Continue? ")
~~~~~~^^^^^^^^^^^^^^
File "<python-input-0>", line 10, in ask_ok
raise IOError('invalid user response')
OSError: invalid user response
</code></pre><h3 id="direct-translation">Direct Translation</h3>
<p>Without focusing on the keyword arguments with default values for the moment,
we could directly translate this to a similar looping implementation with all
arguments provided on the stack:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">ask-ok</span> <span class="nf">( </span><span class="nv">prompt</span> <span class="nv">retries!</span> <span class="nv">complaint</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">drop </span>prompt <span class="nb">write bl flush readln </span>{
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>{ <span class="s">"y"</span> <span class="s">"ye"</span> <span class="s">"yes"</span> } <span class="nb">member? </span>] [ <span class="nb">drop </span><span class="no">t f </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>{ <span class="s">"n"</span> <span class="s">"no"</span> <span class="s">"nop"</span> <span class="s">"nope"</span> } <span class="nb">member? </span>] [ <span class="nb">drop </span><span class="no">f f </span>] }
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> retries <span class="m">1 </span><span class="nb">- </span>retries!
</span></span><span class="line"><span class="cl"> retries <span class="m">0 </span><span class="nb">< </span>[ <span class="s">"invalid user response"</span> <span class="nb">throw </span>] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> complaint <span class="nb">print </span><span class="no">t
</span></span></span><span class="line"><span class="cl"> ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop </span><span class="k">;
</span></span></span></code></pre></div><p>And then try it out for a pretty similar result:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Continue?"</span> <span class="m">4 </span><span class="s">"Yes or no, please!"</span> ask-ok
</span></span><span class="line"><span class="cl">Continue? y
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- Data stack:
</span></span><span class="line"><span class="cl"><span class="no">t
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Continue?"</span> <span class="m">4 </span><span class="s">"Yes or no, please!"</span> ask-ok
</span></span><span class="line"><span class="cl">Continue? no
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- Data stack:
</span></span><span class="line"><span class="cl"><span class="no">f
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Continue?"</span> <span class="m">4 </span><span class="s">"Yes or no, please!"</span> ask-ok
</span></span><span class="line"><span class="cl">Continue? r
</span></span><span class="line"><span class="cl">Yes <span class="nb">or </span>no, please!
</span></span><span class="line"><span class="cl">Continue? r
</span></span><span class="line"><span class="cl">Yes <span class="nb">or </span>no, please!
</span></span><span class="line"><span class="cl">Continue? r
</span></span><span class="line"><span class="cl">Yes <span class="nb">or </span>no, please!
</span></span><span class="line"><span class="cl">Continue? r
</span></span><span class="line"><span class="cl">Yes <span class="nb">or </span>no, please!
</span></span><span class="line"><span class="cl">Continue? r
</span></span><span class="line"><span class="cl">invalid user response
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Type :help for debugging help.
</span></span></code></pre></div><h3 id="using-namespaces">Using Namespaces</h3>
<p>One way to get default values would be to use <em>initialized</em> <a href="https://docs.factorcode.org/content/article-namespaces.html">dynamic
variables</a>, and an
inner word that implements the <em>retry</em> logic with <a href="https://docs.factorcode.org/content/article-tail-call-opt.html">tail
calls</a>, while
also simplifying our <a href="https://docs.factorcode.org/content/word-head__que__,sequences.html">prefix
check</a> for
each supported input variation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">INITIALIZED-SYMBOL: retries [ <span class="m">4 </span>]
</span></span><span class="line"><span class="cl">INITIALIZED-SYMBOL: complaint [ <span class="s">"Yes or no, please!"</span> ]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(ask-ok)</span> <span class="nf">( </span><span class="nv">prompt</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"invalid user response"</span> <span class="nb">throw </span>] [
</span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="nb">- over write bl flush readln </span>{
</span></span><span class="line"><span class="cl"> { [ <span class="s">"yes"</span> <span class="nb">over head? </span>] [ <span class="nb">3drop </span><span class="no">t </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="s">"nope"</span> <span class="nb">over head? </span>] [ <span class="nb">3drop </span><span class="no">f </span>] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>complaint <span class="nb">get print </span>(ask-ok) ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-zero </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">ask-ok</span> <span class="nf">( </span><span class="nv">prompt</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> retries <span class="nb">get </span>(ask-ok) <span class="k">;
</span></span></span></code></pre></div><p>This has the benefit that those arguments can be <a href="https://docs.factorcode.org/content/article-namespaces-change.html">changed
easily</a>
using the <a href="https://docs.factorcode.org/content/vocab-namespaces.html">namespaces
vocabulary</a>.</p>
<h3 id="option-arguments">Option Arguments</h3>
<p>Another way might be to provide an options
<a href="https://docs.factorcode.org/content/article-tuples.html">tuple</a>, with default
values:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">ask</span> <span class="nv">prompt</span> <span class="nv">retries</span> <span class="nv">complaint</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><ask></span> <span class="nf">( </span><span class="nv">prompt</span> <span class="nf">-- </span><span class="nv">ask</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="s">"Yes or no, please!"</span> ask <span class="nb">boa </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">ask-ok</span> <span class="nf">( </span><span class="nv">ask</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">drop </span>ask prompt>> <span class="nb">write bl flush readln </span>{
</span></span><span class="line"><span class="cl"> { [ <span class="s">"yes"</span> <span class="nb">over head? </span>] [ <span class="nb">drop </span><span class="no">t f </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="s">"nope"</span> <span class="nb">over head? </span>] [ <span class="nb">drop </span><span class="no">f f </span>] }
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> ask [ <span class="m">1 </span><span class="nb">- dup </span>] change-retries <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span><span class="nb">< </span>[ <span class="s">"invalid user response"</span> <span class="nb">throw </span>] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> ask complaint>> <span class="nb">print </span><span class="no">t
</span></span></span><span class="line"><span class="cl"> ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop </span><span class="k">;
</span></span></span></code></pre></div><p>And then use it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Continue?"</span> <ask> ask-ok
</span></span><span class="line"><span class="cl">Continue? yes
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- Data stack:
</span></span><span class="line"><span class="cl"><span class="no">t
</span></span></span></code></pre></div><h3 id="combinators">Combinators</h3>
<p>A different approach might be to use <a href="https://docs.factorcode.org/content/article-errors.html">exception
handling</a> instead and
separate out the logic of the <em>trying</em>
<a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a> from
the <em>erroring</em> one, and build a <em>retrying</em> combinator that loops and throws
after <code>n</code> attempts:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">with-retries</span> <span class="nf">( </span><span class="nv">try-quot</span> <span class="nv">error-quot</span> <span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">-rot </span>'[
</span></span><span class="line"><span class="cl"> [ _ <span class="nb">dip </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap </span><span class="m">1 </span><span class="nb">- </span>[ <span class="nb">rethrow </span>] [ <span class="nb">nip </span>@ ] <span class="nb">if-zero </span>] <span class="nb">recover </span><span class="no">t
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop drop </span><span class="k">; inline
</span></span></span></code></pre></div><p>With that we can then make our simple <code>ask-ok</code> word with an <a href="https://docs.factorcode.org/content/vocab-classes.error.html">error
class</a> to throw
the invalid input:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">ERROR:</span> <span class="nc">invalid-user-response</span> <span class="nv">input</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">ask-ok</span> <span class="nf">( </span><span class="nv">prompt</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> prompt <span class="nb">write bl flush readln </span>{
</span></span><span class="line"><span class="cl"> { [ <span class="s">"yes"</span> <span class="nb">over head? </span>] [ <span class="nb">drop </span><span class="no">t </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="s">"nope"</span> <span class="nb">over head? </span>] [ <span class="nb">drop </span><span class="no">f </span>] }
</span></span><span class="line"><span class="cl"> [ invalid-user-response ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="k">;
</span></span></span></code></pre></div><p>And then try it out with the specified <em>retry</em> logic:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"Continue?"</span> ask-ok ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"Yes or no, please?"</span> <span class="nb">print </span>] <span class="m">4 </span>with-retries
</span></span><span class="line"><span class="cl">Continue? r
</span></span><span class="line"><span class="cl">Yes <span class="nb">or </span>no, please?
</span></span><span class="line"><span class="cl">Continue? r
</span></span><span class="line"><span class="cl">Yes <span class="nb">or </span>no, please?
</span></span><span class="line"><span class="cl">Continue? r
</span></span><span class="line"><span class="cl">Yes <span class="nb">or </span>no, please?
</span></span><span class="line"><span class="cl">Continue? r
</span></span><span class="line"><span class="cl">invalid-user-response
</span></span><span class="line"><span class="cl">input <span class="s">"r"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Type :help for debugging help.
</span></span></code></pre></div><p>This is now generic and can be used in any other place that <em>retrying</em> is
required.</p>
<h3 id="restarts">Restarts</h3>
<p><a href="https://docs.factorcode.org/content/article-errors-restartable.html">Restartable
errors</a> is
a neat feature, and we can use this to <a href="https://docs.factorcode.org/content/word-throw-restarts%2Ccontinuations.html">throw a
restart</a>
when invalid input is provided:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">ask-ok</span> <span class="nf">( </span><span class="nv">prompt</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> prompt <span class="nb">write bl flush readln </span>{
</span></span><span class="line"><span class="cl"> { [ <span class="s">"yes"</span> <span class="nb">over head? </span>] [ <span class="nb">drop </span><span class="no">t </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="s">"nope"</span> <span class="nb">over head? </span>] [ <span class="nb">drop </span><span class="no">f </span>] }
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="nb">drop </span><span class="s">"invalid user response"</span>
</span></span><span class="line"><span class="cl"> { { <span class="s">"Yes"</span> <span class="no">t </span>} { <span class="s">"Nope"</span> <span class="no">f </span>} } <span class="nb">throw-restarts
</span></span></span><span class="line"><span class="cl"> ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="k">;
</span></span></span></code></pre></div><p>And see that we throw a restart, and then can select one of the options:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Continue?"</span> ask-ok
</span></span><span class="line"><span class="cl">Continue? r
</span></span><span class="line"><span class="cl">invalid user response
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">The following <span class="nb">restarts </span>are available:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">:1 Yes
</span></span><span class="line"><span class="cl">:2 Nope
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Type :help for debugging help.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> :1
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- Data stack:
</span></span><span class="line"><span class="cl"><span class="no">t
</span></span></span></code></pre></div><p>Other improvements could include splitting out the <code>yes</code> and <code>nope</code>
checks into their own words:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">yes?</span> <span class="nf">( </span><span class="nv">input</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">) </span><span class="s">"yes"</span> <span class="nb">swap head? </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">nope?</span> <span class="nf">( </span><span class="nv">input</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">) </span><span class="s">"nope"</span> <span class="nb">swap head? </span><span class="k">;
</span></span></span></code></pre></div><p>This would allow us to simplify the code – or make it more complex without
impacting the calling context – and also to write <a href="https://docs.factorcode.org/content/article-tools.test.html">unit
tests</a> for small
pieces of the overall system.</p>
<p>How else might you write this?</p>
Asserting Implications
https://re.factorcode.org/2025/07/asserting-implications.html
Mon, 21 Jul 2025 08:00:00 -0700https://re.factorcode.org/2025/07/asserting-implications.html<p><a href="https://tigerbeetle.com/blog/2025-05-26-asserting-implications/">Asserting
Implications</a>
is a short, but targeted, blog post by the
<a href="https://tigerbeetle.com">Tigerbeetle</a> team about writing some
<a href="https://en.wikipedia.org/wiki/Assertion_(software_development)">asserts</a>
differently – along with some interesting <a href="https://lobste.rs/s/xzieif/asserting_implications">lobste.rs
discussions</a>. Their main
conclusion was made using an example code change in
<a href="https://ziglang.org">Zig</a>:</p>
<blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="c1">// Before:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nf">assert</span><span class="p">(</span><span class="n">header_b</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">null</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="n">replica</span><span class="p">.</span><span class="n">commit_min</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">replica</span><span class="p">.</span><span class="n">op_checkpoint</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// After:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">header_b</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="nf">assert</span><span class="p">(</span><span class="n">replica</span><span class="p">.</span><span class="n">commit_min</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">replica</span><span class="p">.</span><span class="n">op_checkpoint</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div></blockquote>
<p>Directly translating into <a href="https://factorcode.org">Factor</a> using <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a> might look
like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="c">! Before</span>
</span></span><span class="line"><span class="cl">header_b <span class="nb">not </span>replica commit-min>> replica checkpoint>> <span class="nb">= or </span><span class="no">t </span><span class="nb">assert=
</span></span></span></code></pre></div><p>While using <a href="https://docs.factorcode.org/content/article-combinators.short-circuit.html">short-circuit
combinators</a>
looks both <em>less</em> and <em>more</em> readable:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> [ header_b <span class="nb">not </span>]
</span></span><span class="line"><span class="cl"> [ replica commit-min>> replica checkpoint>> <span class="nb">= </span>]
</span></span><span class="line"><span class="cl">} 0|| <span class="no">t </span><span class="nb">assert=
</span></span></span></code></pre></div><p>And writing it awkwardly with <a href="https://docs.factorcode.org/content/article-dataflow-combinators.html">dataflow
combinators</a>
looks inscrutable:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="nb">2dup </span>[ <span class="nb">not </span>] [ [ commit-min>> ] [ checkpoint>> ] <span class="nb">bi = </span>] <span class="nb">bi* or </span><span class="no">t </span><span class="nb">assert=
</span></span></span></code></pre></div><p>Nesting the assertion does indeed look much better:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="c">! After</span>
</span></span><span class="line"><span class="cl">header_b [ replica commit-min>> replica checkpoint>> <span class="nb">assert= </span>] <span class="nb">when
</span></span></span></code></pre></div><p>Alternatively, using implicit arguments on the stack that are preserved:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="nb">2dup </span>'[ _ [ commit-min>> ] [ checkpoint>> ] <span class="nb">bi assert= </span>] <span class="nb">when
</span></span></span></code></pre></div><p>Neat idea!</p>
World Emoji Day
https://re.factorcode.org/2025/07/world-emoji-day.html
Thu, 17 Jul 2025 08:00:00 -0700https://re.factorcode.org/2025/07/world-emoji-day.html<p>Today, and every July 17th, is <a href="https://en.wikipedia.org/wiki/World_Emoji_Day">World Emoji
Day</a>. Kind of amazing that there
have already been <em>twelve</em> annual global emoji celebrations! In any event, I
was reminded of that by this post:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Your fate is sealed in emoji! 🎲<br><br>Roll a d20 and face your destiny on World Emoji Day! <a href="https://t.co/aIyzDJB4Bw">pic.twitter.com/aIyzDJB4Bw</a></p>— Dungeons & Dragons (@Wizards_DnD) <a href="https://twitter.com/Wizards_DnD/status/1945979177527607513?ref_src=twsrc%5Etfw">July 17, 2025</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>We have had support in the <a href="https://docs.factorcode.org/content/vocab-calendar.holidays.html">calendar.holidays
vocabulary</a>
for defining and computing on holidays, or at least the ones that we have
defined already. Turns out, we were missing <em>World Emoji Day</em>!</p>
<p>Well, after adding it, we can now see:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="c">! The last world emoji day (today)</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> today [ world-emoji-day ] year<= <span class="m">.
</span></span></span><span class="line"><span class="cl">T{ timestamp
</span></span><span class="line"><span class="cl"> { year <span class="m">2025 </span>}
</span></span><span class="line"><span class="cl"> { month <span class="m">7 </span>}
</span></span><span class="line"><span class="cl"> { day <span class="m">17 </span>}
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">! The next world emoji day!</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> today [ world-emoji-day ] year> <span class="m">.
</span></span></span><span class="line"><span class="cl">T{ timestamp
</span></span><span class="line"><span class="cl"> { year <span class="m">2026 </span>}
</span></span><span class="line"><span class="cl"> { month <span class="m">7 </span>}
</span></span><span class="line"><span class="cl"> { day <span class="m">17 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>And we can implement the <em>fate checker</em> from the original post by defining our
emojis:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">emojis</span> $[
</span></span><span class="line"><span class="cl"> <span class="s">"💩🤬🤡😰🤮☠️😱😭🤢🙃🙈🙉🙊👍👀🙂😀🤗😍🐲"</span>
</span></span><span class="line"><span class="cl"> >graphemes [ <span class="nb">>string </span>] <span class="nb">map
</span></span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><p>We could choose one at <a href="https://docs.factorcode.org/content/word-random,random.html">random</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> emojis random <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"💩"</span>
</span></span></code></pre></div><p>Or by <a href="https://re.factorcode.org/2010/07/rolling-dice.html">rolling dice</a>, and adjusting to zero-based indices:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> ROLL: 1d20 <span class="m">1 </span><span class="nb">- </span>emojis <span class="nb">nth </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"💩"</span>
</span></span></code></pre></div><p>Crap.</p>
One Billion Loops
https://re.factorcode.org/2025/07/one-billion-loops.html
Wed, 16 Jul 2025 08:00:00 -0700https://re.factorcode.org/2025/07/one-billion-loops.html<p><a href="https://www.youtube.com/@ThePrimeTimeagen">The Primeagen</a> had a great video
about <a href="https://www.youtube.com/watch?v=RrHGX1wwSYM">Language Performance Comparisons Are
Junk</a> about six months ago talking
about the <a href="https://benjdd.com/languages2/">1 Billion nested loop iterations</a>
benchmark that <a href="https://benjdd.com">Benjamin Dicken</a> wrote. You can find a copy
of the <a href="https://github.com/bddicken/languages">benchmark code</a> on
<a href="https://github.com">GitHub</a>.</p>
<p>The <em>loops benchmark</em> can be summarized by a version in
<a href="https://python.org">Python</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">random</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">u</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="c1"># Get an input number from the command line</span>
</span></span><span class="line"><span class="cl"><span class="n">r</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">10000</span><span class="p">)</span> <span class="c1"># Get a random number 0 <= r < 10k</span>
</span></span><span class="line"><span class="cl"><span class="n">a</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="mi">10000</span> <span class="c1"># Array of 10k elements initialized to 0</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10000</span><span class="p">):</span> <span class="c1"># 10k outer loop iterations</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">100000</span><span class="p">):</span> <span class="c1"># 100k inner loop iterations, per outer loop iteration</span>
</span></span><span class="line"><span class="cl"> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+=</span> <span class="n">j</span> <span class="o">%</span> <span class="n">u</span> <span class="c1"># Simple sum</span>
</span></span><span class="line"><span class="cl"> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+=</span> <span class="n">r</span> <span class="c1"># Add a random value to each element in array</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">a</span><span class="p">[</span><span class="n">r</span><span class="p">])</span> <span class="c1"># Print out a single element from the array</span>
</span></span></code></pre></div><p>As a benchmark, it has some flaws, but it was fun to iterate on and I would
like to compare <a href="https://factorcode.org">Factor</a> as a percentage of
<a href="https://ziglang.org">Zig</a> – which was the <em>fastest solution</em> – and find out
if <a href="https://re.factorcode.org/2023/11/factor-is-faster-than-zig.html">Factor is faster than Zig!</a> again.</p>
<p>The <a href="https://github.com/bddicken/languages/blob/main/loops/zig">Zig version</a>
takes about <strong>1.3 seconds</strong> on the computer I used for benchmarking:</p>
<pre tabindex="0"><code>$ git clone https://github.com/bddicken/languages.git
$ cd languages/loops/zig/
$ zig version
0.14.1
$ zig build-exe -O ReleaseFast code.zig
$ time ./code 100
4958365
real 0m1.292s
user 0m1.288s
sys 0m0.002s
</code></pre><p>Benjamin has some thoughts about <a href="https://github.com/bddicken/languages?tab=readme-ov-file#the-runner">using test
runners</a>
to eliminate startup time and arrive at stable benchmark times. I’m going to
ignore this for the purpose of this blog post, but it might be worth revisiting
at some point to see what the steady state performance of
<a href="https://factorcode.org">Factor</a> is.</p>
<h3 id="version-1">Version 1</h3>
<p>Our first implementation will be the simplest direct copy of the <a href="https://github.com/bddicken/languages/blob/main/loops/py/code.py">Python
version</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">loops-benchmark</span> <span class="nf">( </span><span class="nv">u</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="m">10,000 </span>random :> r
</span></span><span class="line"><span class="cl"> <span class="m">10,000 0 </span><span class="nb"><array> </span>:> a
</span></span><span class="line"><span class="cl"> <span class="m">10,000 </span>[| i |
</span></span><span class="line"><span class="cl"> <span class="m">100,000 </span>[| j |
</span></span><span class="line"><span class="cl"> i a [ j u <span class="nb">mod + </span>] <span class="nb">change-nth
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-integer
</span></span></span><span class="line"><span class="cl"> i a [ r <span class="nb">+ </span>] <span class="nb">change-nth
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-integer </span>r a <span class="nb">nth </span><span class="m">. </span><span class="k">;
</span></span></span></code></pre></div><p>This takes <strong>4.7 seconds</strong> or about <strong>3.6x</strong> of Zig.</p>
<h3 id="version-2">Version 2</h3>
<p>By default
<a href="https://docs.factorcode.org/content/word-change-nth,sequences.html">change-nth</a>
performs <em>bounds-checking</em> and we can notice that our indices are always within
bounds, so we can use the <em>unsafe</em> version instead:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl">:: loops-benchmark ( u -- )
</span></span><span class="line"><span class="cl"> 10,000 random :> r
</span></span><span class="line"><span class="cl"> 10,000 0 <array> :> a
</span></span><span class="line"><span class="cl"> 10,000 [| i |
</span></span><span class="line"><span class="cl"> 100,000 [| j |
</span></span><span class="line"><span class="cl"><span class="gd">- i a [ j u mod + ] change-nth
</span></span></span><span class="line"><span class="cl"><span class="gi">+ i a [ j u mod + ] change-nth-unsafe
</span></span></span><span class="line"><span class="cl"> ] each-integer
</span></span><span class="line"><span class="cl"><span class="gd">- i a [ r + ] change-nth
</span></span></span><span class="line"><span class="cl"><span class="gi">+ i a [ r + ] change-nth-unsafe
</span></span></span><span class="line"><span class="cl"><span class="gd">- ] each-integer r a nth . ;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ ] each-integer r a nth-unsafe . ;
</span></span></span></code></pre></div><p>This does not really speed the benchmark up, taking <strong>4.7 seconds</strong>.</p>
<h3 id="version-3">Version 3</h3>
<p>We can improve our <a href="https://docs.factorcode.org/content/vocab-generic.math.html">math
dispatch</a> by
specifying that the argument is an
<a href="https://docs.factorcode.org/content/word-integer,math.html">integer</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"><span class="gd">-:: loops-benchmark ( u -- )
</span></span></span><span class="line"><span class="cl"><span class="gi">+TYPED:: loops-benchmark ( u: integer -- )
</span></span></span><span class="line"><span class="cl"> 10,000 random :> r
</span></span><span class="line"><span class="cl"> 10,000 0 <array> :> al
</span></span><span class="line"><span class="cl"> 10,000 [| i |
</span></span><span class="line"><span class="cl"> 100,000 [| j |
</span></span><span class="line"><span class="cl"> i a [ j u mod + ] change-nth-unsafe
</span></span><span class="line"><span class="cl"> ] each-integer
</span></span><span class="line"><span class="cl"> i a [ r + ] change-nth-unsafe
</span></span><span class="line"><span class="cl"> ] each-integer r a nth-unsafe . ;
</span></span></code></pre></div><p>This takes <strong>4.5 seconds</strong> or about <strong>3.5x</strong> of Zig.</p>
<h3 id="version-4">Version 4</h3>
<p>The <a href="https://github.com/bddicken/languages/blob/main/loops/zig/code.zig#L28">Zig
version</a>
operates on <em>32-bit unsigned ints</em>, so we could enforce using
<a href="https://docs.factorcode.org/content/word-fixnum,math.html">fixnum</a>
integers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"><span class="gd">-TYPED:: loops-benchmark ( u: integer -- )
</span></span></span><span class="line"><span class="cl"><span class="gi">+TYPED:: loops-benchmark ( u: fixnum -- )
</span></span></span><span class="line"><span class="cl"><span class="gd">- 10,000 random :> r
</span></span></span><span class="line"><span class="cl"><span class="gi">+ 10,000 random { fixnum } declare :> r
</span></span></span><span class="line"><span class="cl"> 10,000 0 <array> :> a
</span></span><span class="line"><span class="cl"> 10,000 [| i |
</span></span><span class="line"><span class="cl"> 100,000 [| j |
</span></span><span class="line"><span class="cl"><span class="gd">- i a [ j u mod + ] change-nth-unsafe
</span></span></span><span class="line"><span class="cl"><span class="gi">+ i a [ j u mod fixnum+fast ] change-nth-unsafe
</span></span></span><span class="line"><span class="cl"> ] each-integer
</span></span><span class="line"><span class="cl"><span class="gd">- i a [ r + ] change-nth-unsafe
</span></span></span><span class="line"><span class="cl"><span class="gi">+ i a [ r fixnum+fast ] change-nth-unsafe
</span></span></span><span class="line"><span class="cl"> ] each-integer r a nth-unsafe . ;
</span></span></code></pre></div><p>This takes <strong>3.5 seconds</strong> or about <strong>2.7x</strong> of Zig.</p>
<h3 id="version-5">Version 5</h3>
<p>We could operate on those same <em>32-bit unsigned ints</em> using
<a href="https://docs.factorcode.org/content/article-specialized-arrays.html">specialized-arrays</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"><span class="gi">+SPECIALIZED-ARRAY: uint32_t
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">TYPED:: loops-benchmark ( u: fixnum -- )
</span></span><span class="line"><span class="cl"> 10,000 random { fixnum } declare :> r
</span></span><span class="line"><span class="cl"><span class="gd">- 10,000 0 <array> :> a
</span></span></span><span class="line"><span class="cl"><span class="gi">+ 10,000 uint32_t <c-array> :> a
</span></span></span><span class="line"><span class="cl"> 10,000 [| i |
</span></span><span class="line"><span class="cl"> 100,000 [| j |
</span></span><span class="line"><span class="cl"> i a [ j u mod fixnum+fast ] change-nth-unsafe
</span></span><span class="line"><span class="cl"> ] each-integer
</span></span><span class="line"><span class="cl"> i a [ r fixnum+fast ] change-nth-unsafe
</span></span><span class="line"><span class="cl"> ] each-integer r a nth-unsafe . ;
</span></span></code></pre></div><p>This takes <strong>3.1 seconds</strong> or about <strong>2.4x</strong> of Zig.</p>
<h3 id="version-6">Version 6</h3>
<p>We could notice that the value added to the array index can be computed first
and then added:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl">TYPED:: loops-benchmark ( u: fixnum -- )
</span></span><span class="line"><span class="cl"> 10,000 random { fixnum } declare :> r
</span></span><span class="line"><span class="cl"> 10,000 uint32_t <c-array> :> a
</span></span><span class="line"><span class="cl"> 10,000 [| i |
</span></span><span class="line"><span class="cl"><span class="gd">- 100,000 [| j |
</span></span></span><span class="line"><span class="cl"><span class="gd">- i a [ j u mod fixnum+fast ] change-nth-unsafe
</span></span></span><span class="line"><span class="cl"><span class="gd">- ] each-integer
</span></span></span><span class="line"><span class="cl"><span class="gi">+ 100,000 <iota> [ u mod ] map-sum :> v
</span></span></span><span class="line"><span class="cl"><span class="gi">+ i a [ v + ] change-nth-unsafe
</span></span></span><span class="line"><span class="cl"> i a [ r fixnum+fast ] change-nth-unsafe
</span></span><span class="line"><span class="cl"> ] each-integer r a nth-unsafe . ;
</span></span></code></pre></div><p>This takes <strong>2.4 seconds</strong> or about <strong>1.8x</strong> of Zig.</p>
<h3 id="version-7">Version 7</h3>
<p>While the previous version did the <em>same number</em> of loops, it did <em>fewer
modifications</em> to the array memory. It turns out that not only can the value
added to the array index be computed first – it can be computed outside the
loop. And once you do that, you’ll notice that each element of the array
gets the same value, and you don’t need to compute the array at all. This is
effectively a <a href="https://en.wikipedia.org/wiki/Optimizing_compiler">compiler
optimization</a> that the
compiler isn’t doing and, after <a href="https://the-nerve-blog.ghost.io/to-be-a-better-programmer-write-little-proofs-in-your-head/">writing a little proof in your
head</a>,
it can reduce down to:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">loops-benchmark</span> <span class="nf">( </span><span class="nv">u</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="m">10,000 </span>random <span class="m">100,000 </span><iota> [ u <span class="nb">mod </span>] <span class="nb">map-sum + </span><span class="m">. </span><span class="k">;
</span></span></span></code></pre></div><p>This takes <strong>0.0003 seconds</strong> which is now <strong>4300x</strong> faster than Zig.</p>
<h3 id="conclusion">Conclusion</h3>
<p>I’ve always thought of Factor as able to have about <strong>2x-4x</strong> the performance
of <a href="https://en.wikipedia.org/wiki/C_(programming_language)">C</a> with reasonable
looking generic and dynamic code. This depends somewhat on which benchmark is
being considered, and on occasion we can get <a href="https://re.factorcode.org/2011/11/wc-l.html">as fast as
C</a>.</p>
<p>We can easily profile code using the <a href="https://docs.factorcode.org/content/article-tools.profiler.sampling.html">sampling
profiler</a>,
visualize profiles using the <a href="https://docs.factorcode.org/content/vocab-flamegraph.html">flamegraph
vocabulary</a>, print
<a href="https://docs.factorcode.org/content/word-optimized.%2Ccompiler.tree.debugger.html">optimized
output</a>
from the compiler, as well as <a href="https://docs.factorcode.org/content/article-tools.disassembler.html">disassemble
words</a> to
investigate the actual machine code that our <a href="https://docs.factorcode.org/content/article-compiler.html">optimizing
compiler</a> generates.</p>
<p>In addition to all the <a href="https://github.com/factor/factor/issues?q=state%3Aopen%20label%3Aperformance">open issues about
performance</a>,
we have an <a href="https://github.com/factor/factor/issues/788">open issue to improve fixnum
iteration</a> that would likely help
this benchmark and I hope to someday get resolved. And, there are likely many
other improvements we could make to our use of <em>tagged fixnum</em> and <em>integer
unions</em> or <em>generic dispatch</em> to improve the un-typed arithmetic in the
examples above.</p>
<p>Some interesting results on the relative value of different Factor
optimizations!</p>
Command Arguments
https://re.factorcode.org/2025/07/command-arguments.html
Thu, 10 Jul 2025 08:00:00 -0700https://re.factorcode.org/2025/07/command-arguments.html<p>A <a href="https://sourceforge.net/p/factor/mailman/factor-talk/thread/DB2N4ECK6R20.1UX4BLEUSFZKH%40posteo.net/#msg59202185">question was asked
recently</a>
on the <a href="https://concatenative.org/wiki/view/Factor/Mailing%20list">Factor mailing
list</a> about the
<a href="https://re.factorcode.org/2024/05/argument-parser.html">Argument Parser</a> that I had previously
implemented in <a href="https://factorcode.org">Factor</a>:</p>
<blockquote>
<p>I have been trying to hack on
<a href="https://docs.factorcode.org/content/article-command-line.parser.html">command-line.parser</a>
to add the ability to call it with commands.</p>
</blockquote>
<p>The specific feature they want is similar to the
<a href="https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.add_subparsers">ArgumentParser.add_subparsers</a>
function in Python’s
<a href="https://docs.python.org/3/library/argparse.html">argparse</a> module. I spent a
little bit of time thinking about a quick implementation that can get us
started, and <a href="https://github.com/factor/factor/commit/666a44bfc2b24b811bfde3a971e09a56ae7ad581">applied this patch to support
commands</a>.</p>
<p>Here’s their example of a
<a href="https://docs.factorcode.org/content/word-MAIN__colon__,syntax.html">MAIN</a> with
two commands with different options using
<a href="https://docs.factorcode.org/content/word-with-commands,command-line.parser.html">with-commands</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">[</span>
</span></span><span class="line"><span class="cl"> H{
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> <span class="s">"add"</span>
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> T{ option
</span></span><span class="line"><span class="cl"> { name <span class="s">"a"</span> }
</span></span><span class="line"><span class="cl"> { type <span class="nb">integer </span>}
</span></span><span class="line"><span class="cl"> { #args <span class="m">1 </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> <span class="s">"subtract"</span>
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> T{ option
</span></span><span class="line"><span class="cl"> { name <span class="s">"s"</span> }
</span></span><span class="line"><span class="cl"> { type <span class="nb">integer </span>}
</span></span><span class="line"><span class="cl"> { #args <span class="m">1 </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> } [ ] with-commands
</span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><p>We currently produce <em>no output</em> by default when no command is specified:</p>
<pre tabindex="0"><code>$ ./factor foo.factor
</code></pre><p>The default help prints the possible commands:</p>
<pre tabindex="0"><code>$ ./factor foo.factor --help
Usage:
factor foo.factor [--help] [command]
Arguments:
command {add,subtract}
Options:
--help show this help and exit
</code></pre><p>Or get default help for a command:</p>
<pre tabindex="0"><code>$ ./factor foo.factor add --help
Usage:
factor foo.factor add [--help] [a]
Arguments:
a
Options:
--help show this help and exit
</code></pre><p>Or print an error if the argument is not a valid command:</p>
<pre tabindex="0"><code>$ ./factor foo.factor multiply
ERROR: Invalid value 'multiply' for option 'command'
</code></pre><p>There are other features we might want to add to this including per-command
metadata with a brief description of the command, support for additional
top-level options besides just the <code>command</code>, and perhaps a different way of
handling the <em>no command</em> case rather than empty output.</p>
<p>This is available in the latest <a href="https://github.com/factor/factor">developer version</a>!</p>
Fibonacci Style
https://re.factorcode.org/2025/07/fibonacci-style.html
Tue, 08 Jul 2025 08:00:00 -0700https://re.factorcode.org/2025/07/fibonacci-style.html<p>About 14 years ago, I wrote about <a href="https://re.factorcode.org/2011/05/fibonacci-wars.html">Fibonacci
Wars</a> which described the relative performance of
three different methods of calculating <a href="https://en.wikipedia.org/wiki/Fibonacci_number">Fibonacci
numbers</a>. Today, I wanted to
address a style question that someone in the <a href="https://discord.gg/QxJYZx3QDf">Factor Discord
server</a> asked:</p>
<blockquote>
<p>How could I write this better?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fib</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">f(n)</span> <span class="nf">) </span><span class="m">0 1 </span><span class="nb">rot </span><span class="m">1 </span><span class="nb">- </span>[ tuck <span class="nb">+ </span>] <span class="nb">times nip </span><span class="k">;
</span></span></span></code></pre></div><p>In a more concatenative style.</p>
</blockquote>
<p>I’ve written before about <a href="https://re.factorcode.org/2011/04/verbosity.html">conciseness</a>,
<a href="https://re.factorcode.org/2011/07/concatenative-thinking.html">concatenative thinking</a> and
<a href="https://re.factorcode.org/2012/02/readability.html">readability</a>. I found this question to be a good
prompt that provides another opportunity to address these topics.</p>
<p>Their suggested solution is an iterative one and is fairly minimal when it
comes to “short code”. It uses less common <a href="https://docs.factorcode.org/content/article-shuffle-words.html">shuffle
words</a> like
<a href="https://docs.factorcode.org/content/word-tuck,kernel.html">tuck</a> that users
might not understand easily. It is probably true that even
<a href="https://docs.factorcode.org/content/word-rot,kernel.html">rot</a> is more
inscrutable to people coming from other languages.</p>
<p>Let’s look at some potential variations!</p>
<p>You could use simpler stack shuffling:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fib</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">f(n)</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">1 0 </span>] <span class="nb">dip </span><span class="m">1 </span><span class="nb">- </span>[ <span class="nb">over + swap </span>] <span class="nb">times drop </span><span class="k">;
</span></span></span></code></pre></div><p>You could factor out the <em>inner logic</em> to another word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fib+</span> <span class="nf">( </span><span class="nv">f(n-2)</span> <span class="nv">f(n-1)</span> <span class="nf">-- </span><span class="nv">f(n-1)</span> <span class="nv">f(n)</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">+ </span>] <span class="nb">keep swap </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fib</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">f(n)</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">0 1 </span>] <span class="nb">dip </span><span class="m">1 </span><span class="nb">- </span>[ fib+ ] <span class="nb">times nip </span><span class="k">;
</span></span></span></code></pre></div><p>You could use <em>higher-level</em> words like <a href="https://docs.factorcode.org/content/word-keepd,kernel.html">keepd</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fib</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">f(n)</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">1 0 </span>] <span class="nb">dip </span><span class="m">1 </span><span class="nb">- </span>[ [ <span class="nb">+ </span>] keepd ] <span class="nb">times drop </span><span class="k">;
</span></span></span></code></pre></div><p>You could use locals and use index <code>0</code> as the “first” fib number:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">fib</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">f(n)</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1 0 </span>n [ [ <span class="nb">+ </span>] keepd ] <span class="nb">times drop </span><span class="k">;
</span></span></span></code></pre></div><p>You could write a recursive solution using
<a href="https://docs.factorcode.org/content/article-memoize.html">memoization</a> for
improved performance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MEMO:</span> <span class="nf">fib</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">f(n)</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">2 </span><span class="nb">< </span>[ <span class="nb">drop </span><span class="m">1 </span>] [ [ <span class="m">2 </span><span class="nb">- </span>fib ] [ <span class="m">1 </span><span class="nb">- </span>fib ] <span class="nb">bi + </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>You could use <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a> to make it
look nicer:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MEMO::</span> <span class="nf">fib</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">f(n)</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> n <span class="m">2 </span><span class="nb">< </span>[ <span class="m">1 </span>] [ n <span class="m">2 </span><span class="nb">- </span>fib n <span class="m">1 </span><span class="nb">- </span>fib <span class="nb">+ </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>But, in many cases, <a href="https://www.merriam-webster.com/dictionary/beauty%20is%20in%20the%20eye%20of%20the%20beholder">beauty is in the eye of the
beholder</a>.
And so you could start at a place where <em>you</em> find the code most readable, and
that might even be something more conventional looking like this version that
uses mutable locals and comments and whitespace to describe what is happening:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">fib</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">f(n)</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>:> f(n-1)!
</span></span><span class="line"><span class="cl"> <span class="m">1 </span>:> f(n)!
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! loop to calculate</span>
</span></span><span class="line"><span class="cl"> n [
</span></span><span class="line"><span class="cl"> <span class="c">! compute the next number</span>
</span></span><span class="line"><span class="cl"> f(n-1) f(n) <span class="nb">+ </span>:> f(n+1)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! save the previous</span>
</span></span><span class="line"><span class="cl"> f(n) f(n-1)!
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! save the next</span>
</span></span><span class="line"><span class="cl"> f(n+1) f(n)!
</span></span><span class="line"><span class="cl"> ] <span class="nb">times
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! return the result</span>
</span></span><span class="line"><span class="cl"> f(n) <span class="k">;
</span></span></span></code></pre></div><p>Are any of these clearly better than the original version?</p>
<p>Are there other variations we should consider?</p>
<p>There are often multiple competing priorities when improving code style –
including readability, performance, simplicity, and aesthetics. I encourage
everyone to spend some time iterating on these various axes as they learn more
about <a href="https://factorcode.org">Factor</a>!</p>
Jaro-Winkler
https://re.factorcode.org/2025/06/jaro-winkler.html
Sat, 21 Jun 2025 08:00:00 -0700https://re.factorcode.org/2025/06/jaro-winkler.html<p><a href="https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance">Jaro-Winkler distance</a> is
a measure of string similarity and <a href="https://en.wikipedia.org/wiki/Edit_distance">edit
distance</a> between two sequences:</p>
<blockquote>
<p>The higher the Jaro–Winkler distance for two strings is, the less similar the
strings are. The score is normalized such that 0 means an exact match and 1
means there is no similarity. The original paper actually defined the metric
in terms of similarity, so the distance is defined as the inversion of that
value (distance = 1 − similarity).</p>
</blockquote>
<p>There are actually two different concepts – and <a href="https://rosettacode.org/wiki/Category:Solutions_by_Programming_Task">RosettaCode
tasks</a> –
implied by this algorithm:</p>
<ol>
<li><a href="https://rosettacode.org/wiki/Jaro_similarity">Jaro similarity</a> and Jaro distance</li>
<li>Jaro-Winkler similarity and <a href="https://rosettacode.org/wiki/Jaro-Winkler_distance">Jaro-Winkler distance</a>.</li>
</ol>
<p>Let’s build an implementation of these in <a href="https://factorcode.org">Factor</a>!</p>
<h3 id="jaro-similarity">Jaro Similarity</h3>
<p>The base that all of these are built upon is the <em>Jaro similarity</em>. It is
calculated as a score by measuring the number of <em>matches</em> (<code>m</code>) between the
strings, counting the number of <em>transpositions</em> divided by 2 (<code>t</code>), and then returning a
weighted score using the formula using the lengths of each sequence (<code>|s1|</code>
and <code>|s2|</code>):</p>
<p>
<img src="https://re.factorcode.org/images/2025-06-21-jaro-similarity.png" alt="" width="402" height="86" />
</p>
<p>In particular, it considers a <em>matching character</em> to be one that is found in
the other string within a <em>match distance</em> away, calculated by the formula:</p>
<p>
<img src="https://re.factorcode.org/images/2025-06-21-match-distance.png" alt="" width="214" height="71" />
</p>
<p>There are multiple ways to go about this, with varying performance, but I
decided one longer function was simpler to understand than breaking out the
steps into their own
<a href="https://docs.factorcode.org/content/article-words.html">words</a>. We use a
<a href="https://docs.factorcode.org/content/article-bit-arrays.html">bit-array</a> to
efficiently track which characters have been matched already as we iterate:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">jaro-similarity</span> <span class="nf">( </span><span class="nv">s1</span> <span class="nv">s2</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> s1 s2 [ <span class="nb">length </span>] <span class="nb">bi@ </span> :> <span class="nf">( </span><span class="nv">len1</span> <span class="nv">len2</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> len1 len2 max <span class="nb">2/ </span><span class="m">1 </span>[-] :> delta
</span></span><span class="line"><span class="cl"> len2 <bit-array> :> flags
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> s1 [| ch i |
</span></span><span class="line"><span class="cl"> i delta [-] :> from
</span></span><span class="line"><span class="cl"> i delta <span class="nb">+ </span><span class="m">1 </span><span class="nb">+ </span>len2 min :> to
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> from to [| j |
</span></span><span class="line"><span class="cl"> j flags <span class="nb">nth </span>[ <span class="no">f </span>] [
</span></span><span class="line"><span class="cl"> ch j s2 <span class="nb">nth = dup </span>j flags <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] find-integer-from
</span></span><span class="line"><span class="cl"> ] filter-index
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ <span class="m">0 </span>] [
</span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>] <span class="nb">keep </span>s2 flags [ <span class="nb">nip </span>] 2filter [ <span class="nb">= not </span>] 2count
</span></span><span class="line"><span class="cl"> :> <span class="nf">( </span><span class="nv">#matches</span> <span class="nv">#transpositions</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> #matches len1 <span class="nb">/f </span>#matches len2 <span class="nb">/f +
</span></span></span><span class="line"><span class="cl"> #matches #transpositions <span class="nb">2/ - </span>#matches <span class="nb">/f + </span><span class="m">3 </span><span class="nb">/
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-empty </span><span class="k">;
</span></span></span></code></pre></div><p>The <em>Jaro distance</em> is then just a subtraction:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">jaro-distance</span> <span class="nf">( </span><span class="nv">s1</span> <span class="nv">s2</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> jaro-similarity <span class="m">1.0 </span><span class="nb">swap - </span><span class="k">;
</span></span></span></code></pre></div><p>I’m curious if anyone else has a simpler implementation – please share!</p>
<h3 id="jaro-winkler-similarity">Jaro-Winkler Similarity</h3>
<p>The <em>Jaro-Winkler similarity</em> builds upon this by factoring in the length of
the common prefix (<code>l</code>) times a constant scaling factor (<code>p</code>) that is
usually set to <code>0.1</code> in most implementations I’ve seen:</p>
<p>
<img src="https://re.factorcode.org/images/2025-06-21-jaro-winkler-similarity.png" alt="" width="279" height="58" />
</p>
<p>We can implement this by calcuting the <em>Jaro similarity</em> and then computing the
<em>common prefix</em> and then generating the result:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">jaro-winkler-similarity</span> <span class="nf">( </span><span class="nv">s1</span> <span class="nv">s2</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> s1 s2 jaro-similarity :> jaro
</span></span><span class="line"><span class="cl"> s1 s2 <span class="nb">min-length </span><span class="m">4 </span>min :> len
</span></span><span class="line"><span class="cl"> s1 s2 [ len <span class="nb">head-slice </span>] <span class="nb">bi@ </span>[ <span class="nb">= </span>] 2count :> #common
</span></span><span class="line"><span class="cl"> <span class="m">1 </span>jaro <span class="nb">- </span>#common <span class="m">0.1 </span><span class="nb">* * </span>jaro <span class="nb">+ </span><span class="k">;
</span></span></span></code></pre></div><p>The <em>Jaro-Winkler distance</em> is again just a subtraction:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">jaro-winkler-distance</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> jaro-winkler-similarity <span class="m">1.0 </span><span class="nb">swap - </span><span class="k">;
</span></span></span></code></pre></div><h3 id="try-it-out">Try it out</h3>
<p>The <a href="https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance">Wikipedia article</a>
compares the similarity of <code>FARMVILLE</code> and <code>FAREMVIEL</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"FARMVILLE"</span> <span class="s">"FAREMVIEL"</span> jaro-similarity <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">0.8842592592592592
</span></span></span></code></pre></div><p>We can also see that the algorithm considers the transposition of two close
characters to be less of a penalty than the transposition of two characters
farther away from each other. It also penalizes additions and substitutions of
characters that cannot be expressed as transpositions.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"My string"</span> <span class="s">"My tsring"</span> jaro-winkler-similarity <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">0.9740740740740741
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"My string"</span> <span class="s">"My ntrisg"</span> jaro-winkler-similarity <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">0.8962962962962963
</span></span></span></code></pre></div><p>We can compare the rough performance of <a href="https://julialang.org">Julia</a> using
the <a href="https://paste.factorcode.org/paste?id=4742">same algorithm</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-julia" data-lang="julia"><span class="line"><span class="cl"><span class="n">julia</span><span class="o">></span> <span class="k">using</span> <span class="n">Random</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">julia</span><span class="o">></span> <span class="n">s</span> <span class="o">=</span> <span class="n">randstring</span><span class="p">(</span><span class="mi">10_000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">julia</span><span class="o">></span> <span class="n">t</span> <span class="o">=</span> <span class="n">randstring</span><span class="p">(</span><span class="mi">10_000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">julia</span><span class="o">></span> <span class="nd">@time</span> <span class="n">jarowinklerdistance</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">t</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="mf">1.492011</span> <span class="n">seconds</span> <span class="p">(</span><span class="mf">108.32</span> <span class="n">M</span> <span class="n">allocations</span><span class="o">:</span> <span class="mf">2.178</span> <span class="n">GiB</span><span class="p">,</span> <span class="mf">1.87</span><span class="o">%</span> <span class="n">gc</span> <span class="n">time</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mf">0.19016926812348256</span>
</span></span></code></pre></div><p><em>Note: I’m not a Julia developer, I just <a href="https://www.youtube.com/watch?v=ts0XG6qDIco">play one on
TV</a>. I adapted <a href="https://rosettacode.org/wiki/Jaro-Winkler_distance#Julia">this
implementation in
Julia</a>, which
originally took over 4.5 seconds. A better developer could probably improve it
quite a bit. In fact, it was pointed out that we are indexing UTF-8 <code>String</code>
in a loop, and should instead collect the <code>Char</code> into a <code>Vector</code> first.
That does indeed make it super fast.</em></p>
<p>To the implementation in <a href="https://factorcode.org">Factor</a> that we built above,
which runs quite a bit faster:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">random.data</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10,000 </span>random-string
</span></span><span class="line"><span class="cl"> <span class="m">10,000 </span>random-string
</span></span><span class="line"><span class="cl"> gc [ jaro-winkler-distance ] time <span class="m">.
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">0.259643166 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="m">0.1952856823031448
</span></span></span></code></pre></div><p>Thats not bad for a first version that uses safe indexing with unnecessary
<a href="https://en.wikipedia.org/wiki/Bounds_checking">bounds-checking</a>, generic
iteration on
<a href="https://docs.factorcode.org/content/article-integers.html">integers</a> when
usually the indices are
<a href="https://docs.factorcode.org/content/word-fixnum,math.html">fixnum</a> (something
I hope to <a href="https://github.com/factor/factor/issues/788">fix someday</a>
automatically), and should probably order the input sequences by length for
consistency.</p>
<p>If we fix those problems, it gets even faster:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">random.data</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10,000 </span>random-string
</span></span><span class="line"><span class="cl"> <span class="m">10,000 </span>random-string
</span></span><span class="line"><span class="cl"> gc [ jaro-winkler-distance ] time <span class="m">.
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">0.068086625 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="m">0.19297898770334765
</span></span></span></code></pre></div><p>This is available in the <a href="https://github.com/factor/factor">development
version</a> in the
<a href="https://docs.factorcode.org/content/vocab-math.similarity.html">math.similarity</a>
and the
<a href="https://docs.factorcode.org/content/vocab-math.distances.html">math.distances</a>
vocabularies.</p>
Best Shuffle
https://re.factorcode.org/2025/06/best-shuffle.html
Wed, 18 Jun 2025 08:00:00 -0700https://re.factorcode.org/2025/06/best-shuffle.html<p>The “<a href="https://rosettacode.org/wiki/Best_shuffle">Best shuffle</a>” is a <a href="https://rosettacode.org/wiki/Rosetta_Code">Rosetta
Code</a> task that <a href="https://rosettacode.org/wiki/Tasks_not_implemented_in_Factor">was not yet
implemented in
Factor</a>:</p>
<blockquote>
<p><strong>Task</strong></p>
<p>Shuffle the characters of a string in such a way that as many of the
character values are in a different position as possible.</p>
<p>A shuffle that produces a randomized result among the best choices is to
be preferred. A deterministic approach that produces the same sequence every
time is acceptable as an alternative.</p>
<p>Display the result as follows:</p>
<pre tabindex="0"><code>original string, shuffled string, (score)
</code></pre><p>The score gives the number of positions whose character value did not
change.</p>
</blockquote>
<p>There are multiple ways to approach this problem, but the way that most
solutions seem to take is to shuffle two sets of indices, and then iterate
through them swapping the characters in the result if they are different.</p>
<p>I wanted to contribute a solution in <a href="https://factorcode.org">Factor</a>, using
<a href="https://docs.factorcode.org/content/article-locals.html">local variables</a> and
<a href="https://docs.factorcode.org/content/article-combinators.short-circuit.html">short-circuit
combinators</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">best-shuffle</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> str <span class="nb">clone </span>:> new-str
</span></span><span class="line"><span class="cl"> str <span class="nb">length </span>:> n
</span></span><span class="line"><span class="cl"> n <iota> <span class="nb">>array </span>randomize :> range1
</span></span><span class="line"><span class="cl"> n <iota> <span class="nb">>array </span>randomize :> range2
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> range1 [| i |
</span></span><span class="line"><span class="cl"> range2 [| j |
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ i j <span class="nb">= </span>]
</span></span><span class="line"><span class="cl"> [ i new-str <span class="nb">nth </span>j new-str <span class="nb">nth = </span>]
</span></span><span class="line"><span class="cl"> [ i str <span class="nb">nth </span>j new-str <span class="nb">nth = </span>]
</span></span><span class="line"><span class="cl"> [ i new-str <span class="nb">nth </span>j str <span class="nb">nth = </span>]
</span></span><span class="line"><span class="cl"> } 0|| [
</span></span><span class="line"><span class="cl"> i j new-str <span class="nb">exchange
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> new-str <span class="k">;
</span></span></span></code></pre></div><p>And we can write some code to display the result as requested:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">best-shuffle.</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>best-shuffle <span class="nb">2dup </span>[ <span class="nb">= </span>] 2count <span class="s">"%s, %s, (%d)\n"</span> printf <span class="k">;
</span></span></span></code></pre></div><p>And then print some test cases:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> {
</span></span><span class="line"><span class="cl"> <span class="s">"abracadabra"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"seesaw"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"elk"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"grrrrrr"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"up"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"a"</span>
</span></span><span class="line"><span class="cl"> } [ best-shuffle. ] <span class="nb">each
</span></span></span><span class="line"><span class="cl">abracadabra, raabaracdab, (0)
</span></span><span class="line"><span class="cl">seesaw, easwse, (0)
</span></span><span class="line"><span class="cl">elk, lke, (0)
</span></span><span class="line"><span class="cl">grrrrrr, rrrrgrr, (5)
</span></span><span class="line"><span class="cl">up, pu, (0)
</span></span><span class="line"><span class="cl">a, a, (1)
</span></span></code></pre></div><p>This is reminiscent to the recent work I had done on
<a href="https://re.factorcode.org/2024/12/derangements.html">derangements</a> and generating a <a href="https://re.factorcode.org/2024/12/random-derangement.html">random
derangement</a>. While this approach does not
generate a perfect derangement of the indices – and happens to be
<a href="https://www.tumblr.com/accidentallyquadratic">accidentally quadratic</a> – it is
somewhat similar with the additional step that we look to make sure not only
are the indices different, but that the contents are different as well before
swapping.</p>
Dotenv
https://re.factorcode.org/2025/06/dotenv.html
Tue, 17 Jun 2025 08:00:00 -0700https://re.factorcode.org/2025/06/dotenv.html<p><a href="https://www.dotenv.org/">Dotenv</a> is an informal file specification, a
collection of implementations in different languages, and an organization
providing cloud-hosting services. They describe the <a href="https://www.dotenv.org/docs/security/env">.env file
format</a> and some extensions:</p>
<blockquote>
<p>The .env file format is central to good DSX and has been since it was
<a href="https://12factor.net/">introduced by Heroku</a> in 2012 and popularized by the
<a href="https://www.npmjs.com/package/dotenv">dotenv node module</a> (and other
libraries) in 2013.</p>
<p>The .env file format starts where the developer starts - in development. It
is added to each project but NOT committed to source control. This gives the
developer a single secure place to store sensitive application secrets.</p>
<p>Can you believe that prior to introducing the .env file, almost all
developers stored their secrets as hardcoded strings in source control. That
was only 10 years ago!</p>
</blockquote>
<p>Besides official and many unofficial <code>.env</code> parsers available in a <a href="https://github.com/topics/dotenv">lot of
languages</a>, the <a href="https://github.com/dotenv-org">Dotenv
organization</a> provides support for <a href="https://www.dotenv.org/docs/quickstart">dotenv-vault
cloud services</a> in Node.js, Python,
Ruby, Go, PHP, and Rust.</p>
<p>Today, I wanted to show how you might implement a <code>.env</code> parser in
<a href="https://factorcode.org">Factor</a>.</p>
<h3 id="file-format">File Format</h3>
<p>The <code>.env</code> files are relatively simple formats with key-value pairs that
are separated by an equal sign. These values can be un-quoted,
single-quoted, double-quoted, or backtick-quoted strings:</p>
<pre tabindex="0"><code>SIMPLE=xyz123
INTERPOLATED="Multiple\nLines"
NON_INTERPOLATED='raw text without variable interpolation'
MULTILINE = `long text here,
e.g. a private SSH key`
</code></pre><h3 id="parsing">Parsing</h3>
<p>There are a lot of ways to build a parser – everything from manually spinning
through bytes using a hand-coded state machine, higher-level parsing grammars
like <a href="https://docs.factorcode.org/content/vocab-peg.html">PEG</a>, or explicit
parsing syntax forms like
<a href="https://docs.factorcode.org/content/article-peg.ebnf.html">EBNF</a>.</p>
<p>We are going to implement a <code>.env</code> parser using standard <em>PEG parsers</em>,
beginning with some parsers that look for whitespace, comment lines, and
newlines:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">ws</span> <span class="nf">( -- </span><span class="nv">parser</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="s">" \t"</span> <span class="nb">member? </span>] satisfy repeat0 <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">comment</span> <span class="nf">( -- </span><span class="nv">parser</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"#"</span> token [ <span class="sc">CHAR: \n </span><span class="nb">= not </span>] satisfy repeat0 2seq hide <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">newline</span> <span class="nf">( -- </span><span class="nv">parser</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"\n"</span> token <span class="s">"\r\n"</span> token 2choice <span class="k">;
</span></span></span></code></pre></div><h3 id="keys">Keys</h3>
<p>The <a href="https://dotenvx.com/docs/env-file#keys">.env keys</a> are specified simply:</p>
<blockquote>
<p>For the sake of portability (and sanity), environment variable names
(keys) must consist solely of letters, digits, and the underscore ( _ ) and
must not begin with a digit. In regex-speak, the names must match the
following pattern:</p>
<pre tabindex="0"><code>[a-zA-Z_]+[a-zA-Z0-9_]*
</code></pre></blockquote>
<p>We can build a <em>key parser</em> by looking for those characters:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">key-parser</span> <span class="nf">( -- </span><span class="nv">parser</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="sc">CHAR: A CHAR: Z </span>range
</span></span><span class="line"><span class="cl"> <span class="sc">CHAR: a CHAR: z </span>range
</span></span><span class="line"><span class="cl"> [ <span class="sc">CHAR: _ </span><span class="nb">= </span>] satisfy 3choice
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="sc">CHAR: A CHAR: Z </span>range
</span></span><span class="line"><span class="cl"> <span class="sc">CHAR: a CHAR: z </span>range
</span></span><span class="line"><span class="cl"> <span class="sc">CHAR: 0 CHAR: 9 </span>range
</span></span><span class="line"><span class="cl"> [ <span class="sc">CHAR: _ </span><span class="nb">= </span>] satisfy 4choice repeat0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> 2seq [ <span class="nb">first2 swap prefix </span><span class="s">""</span> <span class="nb">like </span>] action <span class="k">;
</span></span></span></code></pre></div><h3 id="values">Values</h3>
<p>The <a href="https://dotenvx.com/docs/env-file#values">.env values</a> can be un-quoted,
single-quoted, double-quoted, or backtick-quoted strings. Only double-quoted
strings support escape characters, but single-quoted and backtick-quoted
strings support escaping either single-quotes or backtick characters.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">single-quote</span> <span class="nf">( -- </span><span class="nv">parser</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"\\"</span> token hide [ <span class="s">"\\'"</span> <span class="nb">member? </span>] satisfy 2seq [ <span class="nb">first </span>] action
</span></span><span class="line"><span class="cl"> [ <span class="sc">CHAR: ' </span><span class="nb">= not </span>] satisfy 2choice repeat0 <span class="s">"'"</span> <span class="nb">dup </span>surrounded-by <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">backtick</span> <span class="nf">( -- </span><span class="nv">parser</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"\\"</span> token hide [ <span class="s">"\\`"</span> <span class="nb">member? </span>] satisfy 2seq [ <span class="nb">first </span>] action
</span></span><span class="line"><span class="cl"> [ <span class="sc">CHAR: ` </span><span class="nb">= not </span>] satisfy 2choice repeat0 <span class="s">"`"</span> <span class="nb">dup </span>surrounded-by <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">double-quote</span> <span class="nf">( -- </span><span class="nv">parser</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"\\"</span> token hide [ <span class="s">"\"\\befnrt"</span> <span class="nb">member? </span>] satisfy 2seq [ <span class="nb">first </span>escape ] action
</span></span><span class="line"><span class="cl"> [ <span class="sc">CHAR: " </span><span class="nb">= not </span>] satisfy 2choice repeat0 <span class="s">"\""</span> <span class="nb">dup </span>surrounded-by <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">literal</span> <span class="nf">( -- </span><span class="nv">parser</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="s">" \t\r\n"</span> <span class="nb">member? not </span>] satisfy repeat0 <span class="k">;
</span></span></span></code></pre></div><p>Before we implement our <em>value parser</em>, we should note that some values can be
<a href="https://dotenvx.com/docs/env-file#interpolation">interpolated</a>:</p>
<blockquote>
<p>Interpolation (also known as variable expansion) is supported in environment
files. Interpolation is applied for unquoted and double-quoted values. Both
braced (<code>${VAR}</code>) and unbraced (<code>$VAR</code>) expressions are supported.</p>
<p>Direct interpolation</p>
<ul>
<li><code>${VAR}</code> -> value of VAR</li>
</ul>
<p>Default value</p>
<ul>
<li><code>${VAR:-default}</code> -> value of <code>VAR</code> if set and non-empty, otherwise default</li>
<li><code>${VAR-default}</code> -> value of <code>VAR</code> if set, otherwise default</li>
</ul>
</blockquote>
<p>And some values can have <a href="https://dotenvx.com/docs/env-file#command-substitution">command
substitution</a>:</p>
<blockquote>
<p>Add the output of a command to one of your variables in your .env file.
Command substitution is applied for unquoted and double-quoted values.</p>
<p>Direct substitution</p>
<ul>
<li><code>$(whoami)</code> -> value of <code>$ whoami</code></li>
</ul>
</blockquote>
<p>We can implement an <em>interpolate parser</em> that acts on strings and replaces
observed variables with their interpolated or command-substituted values. This
uses a <a href="https://docs.factorcode.org/content/article-regexp.html">regular
expressions</a> and
<a href="https://docs.factorcode.org/content/word-re-replace-with,regexp.html">re-replace-with</a>
to substitute values appropriately.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">interpolate-value</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">string'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> R/ \$\([^)]+\)|\$\{[^\}:-]+(:?-[^\}]*)?\}|\$[^(^{].+/ [
</span></span><span class="line"><span class="cl"> <span class="s">"$("</span> ?head [
</span></span><span class="line"><span class="cl"> <span class="s">")"</span> ?tail <span class="nb">drop </span>process-contents [ blank? ] <span class="nb">trim
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="s">"${"</span> ?head [ <span class="s">"}"</span> ?tail <span class="nb">drop </span>] [ <span class="s">"$"</span> ?head <span class="nb">drop </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> <span class="s">":-"</span> split1 [
</span></span><span class="line"><span class="cl"> [ os-env [ <span class="nb">empty? not </span>] <span class="nb">keep </span>] <span class="nb">dip ?
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="s">"-"</span> split1 [ [ os-env ] <span class="nb">dip or </span>] [ os-env ] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] re-replace-with <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">interpolate</span> <span class="nf">( </span><span class="nv">parser</span> <span class="nf">-- </span><span class="nv">parser</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="s">""</span> <span class="nb">like </span>interpolate-value ] action <span class="k">;
</span></span></span></code></pre></div><p>We can use that to build a <em>value parser</em>, remembering that only un-quoted and
double-quoted values are <em>interpolated</em>, and making sure to convert the result
to a <a href="https://docs.factorcode.org/content/article-strings.html">string</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">value-parser</span> <span class="nf">( -- </span><span class="nv">parser</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> single-quote ,
</span></span><span class="line"><span class="cl"> double-quote interpolate ,
</span></span><span class="line"><span class="cl"> backtick ,
</span></span><span class="line"><span class="cl"> literal interpolate ,
</span></span><span class="line"><span class="cl"> ] choice* [ <span class="s">""</span> <span class="nb">like </span>] action <span class="k">;
</span></span></span></code></pre></div><h3 id="key-values">Key-Values</h3>
<p>Combining those, we can make a <em>key-value parser</em>, that ignores whitespace
around the <code>=</code> token and uses
<a href="https://docs.factorcode.org/content/word-set-os-env,environment.html">set-os-env</a>
to update the <a href="https://docs.factorcode.org/content/article-environment.html">environment
variables</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">key-value-parser</span> <span class="nf">( -- </span><span class="nv">parser</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> key-parser ,
</span></span><span class="line"><span class="cl"> ws hide ,
</span></span><span class="line"><span class="cl"> <span class="s">"="</span> token hide ,
</span></span><span class="line"><span class="cl"> ws hide ,
</span></span><span class="line"><span class="cl"> value-parser ,
</span></span><span class="line"><span class="cl"> ] seq* [ <span class="nb">first2 swap </span>set-os-env ignore ] action <span class="k">;
</span></span></span></code></pre></div><p>And finally, we can build a parsing word that looks for these <em>key-value</em> pairs
while ignoring optional comments and whitespace:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">PEG: parse-dotenv <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">ast</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> ws hide key-value-parser optional
</span></span><span class="line"><span class="cl"> ws hide comment optional hide 4seq
</span></span><span class="line"><span class="cl"> newline list-of hide <span class="k">;
</span></span></span></code></pre></div><h3 id="loading-files">Loading Files</h3>
<p>We can load a file by reading the
<a href="https://docs.factorcode.org/content/word-file-contents,io.files.html">file-contents</a>
and then parsing it into environment variables:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">load-dotenv-file</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> utf8 file-contents parse-dotenv <span class="nb">drop </span><span class="k">;
</span></span></span></code></pre></div><p>These <code>.env</code> files are usually located somewhere above the current directory,
typically at a project root. For now, we make a word that traverses from the
current directory up to the root, looking for the first <code>.env</code> file that
exists:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-dotenv-file</span> <span class="nf">( -- </span><span class="nv">path/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span>current-directory <span class="nb">get </span>absolute-path [
</span></span><span class="line"><span class="cl"> <span class="nb">nip
</span></span></span><span class="line"><span class="cl"> [ <span class="s">".env"</span> append-path <span class="nb">dup </span>file-exists? [ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">unless </span>]
</span></span><span class="line"><span class="cl"> [ ?parent-directory ] <span class="nb">bi over </span>[ <span class="no">f </span>] [ <span class="nb">dup </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop drop </span><span class="k">;
</span></span></span></code></pre></div><p>And now, finally, we can find and then load the relevant <code>.env</code> file, if
there is one:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">load-dotenv</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> find-dotenv-file [ load-dotenv-file ] <span class="nb">when* </span><span class="k">;
</span></span></span></code></pre></div><h3 id="try-it-out">Try it out</h3>
<p>We can make a simple <code>.env</code> file:</p>
<pre tabindex="0"><code>$ cat .env
HOST="${HOST:-localhost}"
PORT="${PORT:-80}"
URL="https://${HOST}:${PORT}/index.html"
</code></pre><p>And then try it out, overriding the <code>PORT</code> environment variable:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="no">$ PORT=8080</span> ./factor
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">dotenv</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> load-dotenv
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"URL"</span> os-env <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"https://localhost:8080/index.html"</span>
</span></span></code></pre></div><p>Some additional features that we might want to follow up on:</p>
<ul>
<li>investigate the <a href="https://github.com/php-xdg/dotenv-spec">POSIX-compliant dotenv syntax
specification</a> and included test
cases</li>
<li>optionally use a <a href="https://docs.factorcode.org/content/word-STARTUP-HOOK__colon__,syntax.html">startup
hook</a>
to automatically load <code>.env</code> files</li>
<li>support for <a href="https://www.dotenv.org/docs/security/env-vault">dotenv-vaults</a>
and <a href="https://www.dotenv.org/docs/features/encrypted-deploys">encrypted
deploys</a></li>
<li>command-line support similar to <a href="https://dotenvx.com">dotenvx</a></li>
</ul>
<p>This is available in the latest <a href="https://github.com/factor/factor/blob/master/extra/dotenv/dotenv.factor">development
version</a>.
Check it out!</p>
Color Prettyprint
https://re.factorcode.org/2025/06/color-prettyprint.html
Sun, 15 Jun 2025 08:00:00 -0700https://re.factorcode.org/2025/06/color-prettyprint.html<p><a href="https://factorcode.org">Factor</a> has a neat feature in the <a href="https://docs.factorcode.org/content/article-prettyprint.html">prettyprint
vocabulary</a>
that allows printing objects, typically as valid source literal expressions.
There are small caveats to that regarding circularity, depth limits, and
other <a href="https://docs.factorcode.org/content/article-prettyprint-variables.html">prettyprint control
variables</a>,
but it’s roughly true that you can <code>pprint</code> most everything and have it be
useful.</p>
<p>At some point in the past few years, I noticed that
<a href="https://developer.apple.com/xcode/">Xcode</a> and <a href="https://developer.apple.com/swift-playground/">Swift
Playground</a> have support for
color literals that are rendered in the source code. You can see that in
this short video describing how it works:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/84H80y5iHLI?autoplay=0&controls=1&end=0&loop=0&mute=0&start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
</div>
<p>Inspired by that – and a past effort at <a href="https://re.factorcode.org/2012/09/color-tab.html">color tab
completion</a> – I thought it would be fun to show
how you might extend our <a href="https://re.factorcode.org/2013/10/color-support.html">color support</a> to
allow colors to be prettyprinted with a little gadget in the UI that renders
their colors.</p>
<p>First, we need to define a <a href="https://docs.factorcode.org/content/word-section,prettyprint.sections.html">section
object</a>
that holds a color and renders it using a colored border gadget.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">color-section</span> < <span class="nc">section</span> <span class="nv">color</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><color-section></span> <span class="nf">( </span><span class="nv">color</span> <span class="nf">-- </span><span class="nv">color-section</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span>color-section new-section <span class="nb">swap </span>>>color <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">color-section</span> <span class="nf">short-section</span>
</span></span><span class="line"><span class="cl"> <span class="s">" "</span> <label> { <span class="m">5 0 </span>} <border>
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span>color>> <solid> >>interior
</span></span><span class="line"><span class="cl"> COLOR: black <solid> >>boundary
</span></span><span class="line"><span class="cl"> <span class="nb">output-stream get </span>write-gadget <span class="k">;
</span></span></span></code></pre></div><p>Next, we extend <code>pprint*</code> with a custom implementation for any color type
as well as our <a href="https://docs.factorcode.org/content/article-colors.constants.html">named
colors</a>
that adds a <em>color section</em> to the output block:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">color</span> <span class="nf">pprint*</span>
</span></span><span class="line"><span class="cl"> <block
</span></span><span class="line"><span class="cl"> [ call-next-method ]
</span></span><span class="line"><span class="cl"> [ <color-section> add-section ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> block> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">parsed-color</span> <span class="nf">pprint*</span>
</span></span><span class="line"><span class="cl"> <block
</span></span><span class="line"><span class="cl"> [ <span class="no">\ COLOR:</span> pprint-word string>> text ]
</span></span><span class="line"><span class="cl"> [ <color-section> add-section ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> block> <span class="k">;
</span></span></span></code></pre></div><p>And, now that we have that, we can push some different colors to the stack
and see how they are all displayed:</p>
<p>
<img src="https://re.factorcode.org/images/2025-06-15-color-prettyprint.png" alt="" width="265" height="129" />
</p>
<p>Pretty cool.</p>
<p>I did not commit this yet – partly because I’m not sure we want this as-is
and also partly because it needs to only display the gadget if the <a href="https://docs.factorcode.org/content/word-ui-running__que__,ui.html">UI is
running</a>.
We also might want to consider the UI theme and choose a nice <a href="https://docs.factorcode.org/content/word-contrast-text-color,colors.contrast.html">contrasting
color</a>
for the border element.</p>
Tracking Dict
https://re.factorcode.org/2025/06/tracking-dict.html
Sat, 14 Jun 2025 08:00:00 -0700https://re.factorcode.org/2025/06/tracking-dict.html<p><a href="https://www.peterbe.com">Peter Bengtsson</a> wrote about building <a href="https://www.peterbe.com/plog/a-python-dict-that-can-report-which-keys-you-did-not-use">a Python dict
that can report which keys you did not
use</a>:</p>
<blockquote>
<p>This can come in handy if you’re working with large Python objects and you
want to be certain that you’re either unit testing everything you retrieve or
certain that all the data you draw from a database is actually used in a
report.</p>
<p>For example, you might have a <code>SELECT fieldX, fieldY, fieldZ FROM ...</code> SQL
query, but in the report you only use <code>fieldX, fieldY</code> in your CSV export.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">TrackingDict</span><span class="p">(</span><span class="nb">dict</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">_accessed_keys</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">_accessed_keys</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__getitem__</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nd">@property</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">accessed_keys</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_accessed_keys</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nd">@property</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">never_accessed_keys</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">set</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">keys</span><span class="p">())</span> <span class="o">-</span> <span class="bp">self</span><span class="o">.</span><span class="n">_accessed_keys</span>
</span></span></code></pre></div></blockquote>
<p>We can build a version of this in <a href="https://factorcode.org">Factor</a> intended to
show off a few language features. The original version in Python used
inheritance versus composition to implement the data structure. Instead, we
build a data structure that will wrap an existing
<a href="https://docs.factorcode.org/content/article-assocs.html">assoc</a> and delegate
to it.</p>
<p>First, a simple <a href="https://docs.factorcode.org/content/article-tuples.html">tuple
definition</a> that will
have the <em>underlying assoc</em> and a set of <em>accessed keys</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">tracking-assoc</span> <span class="nv">underlying</span> <span class="nv">accessed-keys</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><tracking-assoc></span> <span class="nf">( </span><span class="nv">underlying</span> <span class="nf">-- </span><span class="nv">tracking-assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> HS{ } <span class="nb">clone </span>tracking-assoc <span class="nb">boa </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">INSTANCE:</span> <span class="nc">tracking-assoc</span> <span class="nc">assoc</span>
</span></span></code></pre></div><p>We then implement the <a href="https://docs.factorcode.org/content/article-assocs-protocol.html">assoc
protocol</a> by
using <a href="https://docs.factorcode.org/content/article-delegate.html">delegation</a>
to the <em>underlying assoc</em>, with an override for tracking <em>accessed keys</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">CONSULT: assoc-protocol tracking-assoc underlying>> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">tracking-assoc</span> <span class="nf">at*</span>
</span></span><span class="line"><span class="cl"> [ underlying>> <span class="nb">at* </span>] [ accessed-keys>> adjoin ] <span class="nb">2bi </span><span class="k">;
</span></span></span></code></pre></div><p>And for fun – since we could have built a normal word to do this – we define
a <a href="https://docs.factorcode.org/content/article-protocol-slots.html">protocol
slot</a> that we
then implement to compute the <em>never accessed keys</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SLOT:</span> <span class="nf">never-accessed-keys</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">tracking-assoc</span> <span class="nf">never-accessed-keys>></span>
</span></span><span class="line"><span class="cl"> [ underlying>> <span class="nb">keys </span>] [ accessed-keys>> ] <span class="nb">bi </span>diff <span class="k">;
</span></span></span></code></pre></div><p>And we show it works using a simple example from the original blog post:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">H{
</span></span><span class="line"><span class="cl"> { <span class="s">"name"</span> <span class="s">"John Doe"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"age"</span> <span class="m">30 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"email"</span> <span class="s">"[email protected]"</span> }
</span></span><span class="line"><span class="cl">} <tracking-assoc>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="s">"name"</span> <span class="nb">over at </span><span class="s">"John Doe"</span> <span class="nb">assert=
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">[ accessed-keys>> <span class="s">"Accessed keys: %u\n"</span> printf ]
</span></span><span class="line"><span class="cl">[ never-accessed-keys>> <span class="s">"Never accessed keys: %u\n"</span> printf ] <span class="nb">bi
</span></span></span></code></pre></div><p>Which prints this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">Accessed keys: HS{ <span class="s">"name"</span> }
</span></span><span class="line"><span class="cl">Never accessed keys: HS{ <span class="s">"email"</span> <span class="s">"age"</span> }
</span></span></code></pre></div><p>Fun!</p>
bab=aaa, bbb=bb
https://re.factorcode.org/2025/06/bab-aaa-bb-bbb.html
Wed, 04 Jun 2025 08:00:00 -0700https://re.factorcode.org/2025/06/bab-aaa-bb-bbb.html<p>Admittedly, I struggle sometimes when I read the word
“<a href="https://en.wikipedia.org/wiki/Monoid">monoid</a>”. It seems to always remind
me of that saying <a href="https://medium.com/@felix.kuehl/a-monad-is-just-a-monoid-in-the-category-of-endofunctors-lets-actually-unravel-this-f5d4b7dbe5d6">“A Monad is just a Monoid in the Category of
Endofunctors”</a>
which is both a tongue-twister, requires repeated effort to understand, and
is sometimes used in casual conversation when jealously describing the
features and capabilities of the <a href="https://www.haskell.org">Haskell programming
language</a>.</p>
<p>In any event, the topic of <em>monoids</em> came up recently on the <a href="https://discord.gg/QxJYZx3QDf">Factor Discord
server</a>. <a href="https://factorcode.org/slava/">Slava
Pestov</a>, the original creator of the <a href="https://factorcode.org">Factor
programming language</a>, was describing <a href="https://mathstodon.xyz/@slava/114604760937529631">recent
work</a> he was doing on some
fun mathematical problems:</p>
<blockquote>
<p>I’m searching for examples of finitely-presented monoids that cannot be
presented by finite complete rewriting systems:</p>
<ul>
<li><a href="https://factorcode.org/slava/abaaabaaaab.pdf">⟨a, b | aba=aa, baa=aab⟩</a> – my first result in this space.</li>
<li><a href="https://factorcode.org/slava/babaaabbbbb">⟨a, b | bab=aaa, bbb=bb⟩</a> – explore the equivalence class of a<sup>8</sup> in this remarkable monoid.</li>
</ul>
</blockquote>
<p>He clarified in the discussion that “<em>the <a href="https://en.wikipedia.org/wiki/Knuth%E2%80%93Bendix_completion_algorithm">Knuth-Bendix
algorithm</a> can
solve many cases but not these two, which is how I found them in the first
place</em>”.</p>
<p>The second link above – made extra fun because it uses <code>a=🍎</code> and <code>b=🍌</code>
to make a more emojiful experience – describes this specific problem in more
detail and presents it as a simple game to play. You can see the available
rules, the current state, and the next possible states achieved by applying
either of the rules, which are bi-directional.</p>
<blockquote>
<p>Your pie recipe calls for <strong>10 apples</strong>, but you only have <strong>8 apples</strong>.</p>
<p>Can you turn your 8 apples into 10 apples with these two magic spells?</p>
<ul>
<li>🍌🍎🍌 ↔️ 🍎🍎🍎</li>
<li>🍌🍌🍌 ↔️ 🍌🍌</li>
</ul>
<p>Current state:</p>
<p>🍎🍎🍎🍎🍎🍎🍎🍎</p>
<p>Tap to cast a spell:</p>
<ol>
<li>🍌🍎🍌🍎🍎🍎🍎🍎</li>
<li>🍎🍌🍎🍌🍎🍎🍎🍎</li>
<li>🍎🍎🍌🍎🍌🍎🍎🍎</li>
<li>🍎🍎🍎🍌🍎🍌🍎🍎</li>
<li>🍎🍎🍎🍎🍌🍎🍌🍎</li>
<li>🍎🍎🍎🍎🍎🍌🍎🍌</li>
</ol>
</blockquote>
<p>When exploring things like this, many questions come to mind. For example:</p>
<ol>
<li>Is this even solvable?</li>
<li>What is the shortest solution?</li>
<li>How many short solutions exist?</li>
<li>How many of the possible states lead to the solution?</li>
<li>How large is the set of all possible states?</li>
</ol>
<p>After the link was shared, I must have clicked through about 5000 different
state transitions hoping to randomly stumble upon the solution. And,
eventually, recognized that it might be a good idea – or even possibly poetic
– to do that exploration using <a href="https://factorcode.org">Factor</a>.</p>
<p><em>Warning: Spoilers ahead!</em></p>
<p>Let’s start by writing the rules:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">rules</span> {
</span></span><span class="line"><span class="cl"> { <span class="s">"bab"</span> <span class="s">"aaa"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"bbb"</span> <span class="s">"bb"</span> }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>For convenience, we will make a sequence containing all the rules – since
these are bi-directional and can be applied in either direction – using a
<a href="https://docs.factorcode.org/content/word-%24%5B%2Cliterals.html">literal
expression</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">all-rules</span> $[
</span></span><span class="line"><span class="cl"> rules <span class="nb">dup </span>[ <span class="nb">swap </span>] <span class="nb">assoc-map append
</span></span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><p>We can make a word that takes a <code>from</code> node and applies a
<a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a> to the
result of rules <code>a -> b</code>. Notice that we’re able to use our previous work on
<a href="https://re.factorcode.org/2024/11/finding-subsequences.html">finding subsequences</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">each-move</span> <span class="nf">( </span><span class="nv">from</span> <span class="nv">a</span> <span class="nv">b</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">next</span> <span class="nf">-- ) -- )
</span></span></span><span class="line"><span class="cl"> from <span class="nb">dup </span>a subseq-indices [
</span></span><span class="line"><span class="cl"> <span class="nb">cut </span>a <span class="nb">length tail </span>b <span class="nb">glue </span>quot <span class="nb">call
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">with each </span><span class="k">; inline
</span></span></span></code></pre></div><p>And then a word that returns all the next states:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-moves</span> <span class="nf">( </span><span class="nv">from</span> <span class="nf">-- </span><span class="nv">moves</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ all-rules [ <span class="nb">first2 </span>[ , ] each-move ] <span class="nb">with each </span>] { } make <span class="k">;
</span></span></span></code></pre></div><p>It’s often good practice to try each step out during development, so let’s do
that and show the first six possible next states match the ones from the game:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"aaaaaaaa"</span> all-moves <span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> <span class="s">"babaaaaa"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"ababaaaa"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"aababaaa"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"aaababaa"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"aaaababa"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"aaaaabab"</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>The next state is nice to have, but we’re generally going to be accumulating
<em>paths</em> which are a series of states achieved by traversing the graph of all
possible states:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">all-paths%</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> path <span class="nb">last </span>all-rules [
</span></span><span class="line"><span class="cl"> <span class="nb">first2 </span>[ path <span class="nb">swap suffix </span>, ] each-move
</span></span><span class="line"><span class="cl"> ] <span class="nb">with each </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-paths</span> <span class="nf">( </span><span class="nv">paths</span> <span class="nf">-- </span><span class="nv">paths'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ all-paths% ] <span class="nb">each </span>] { } make members <span class="k">;
</span></span></span></code></pre></div><p>So, these are the first two steps in traversing the graph. You can see that
some of the possible second moves end up circling back to the starting
position, which makes sense since the rules are bi-directional and if <em>applied</em>
can be <em>un-applied</em> on the next step.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { { <span class="s">"aaaaaaaa"</span> } } all-paths <span class="nb">dup </span><span class="m">. </span><span class="nb">nl </span>all-paths <span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"babaaaaa"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"ababaaaa"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"aababaaa"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"aaababaa"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"aaaababa"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"aaaaabab"</span> }
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"babaaaaa"</span> <span class="s">"aaaaaaaa"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"babaaaaa"</span> <span class="s">"babbabaa"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"babaaaaa"</span> <span class="s">"babababa"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"babaaaaa"</span> <span class="s">"babaabab"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"ababaaaa"</span> <span class="s">"aaaaaaaa"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"ababaaaa"</span> <span class="s">"ababbaba"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"ababaaaa"</span> <span class="s">"abababab"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"aababaaa"</span> <span class="s">"aaaaaaaa"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"aababaaa"</span> <span class="s">"aababbab"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"aaababaa"</span> <span class="s">"aaaaaaaa"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"aaababaa"</span> <span class="s">"babbabaa"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"aaaababa"</span> <span class="s">"aaaaaaaa"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"aaaababa"</span> <span class="s">"babababa"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"aaaababa"</span> <span class="s">"ababbaba"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"aaaaabab"</span> <span class="s">"aaaaaaaa"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"aaaaabab"</span> <span class="s">"babaabab"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"aaaaabab"</span> <span class="s">"abababab"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"aaaaaaaa"</span> <span class="s">"aaaaabab"</span> <span class="s">"aababbab"</span> }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Let’s solve for the shortest paths, we keep track of states we’ve previously
seen to avoid cycles, and we iterate using
<a href="https://en.wikipedia.org/wiki/Breadth-first_search">breadth-first-search</a>
until we find any solutions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">shortest-paths</span> <span class="nf">( </span><span class="nv">from</span> <span class="nv">to</span> <span class="nf">-- </span><span class="nv">moves</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> HS{ from } <span class="nb">clone </span>:> seen
</span></span><span class="line"><span class="cl"> { { from } } :> stack!
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="no">f </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">drop
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! find all next possibilities</span>
</span></span><span class="line"><span class="cl"> stack all-paths
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! reject ones that circle back to visited nodes</span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">last </span>seen in? ] reject
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! reject any that are over the length of ``to``</span>
</span></span><span class="line"><span class="cl"> to <span class="nb">length </span>'[ <span class="nb">last length </span>_ <span class="nb">> </span>] reject stack!
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! add the newly visited nodes</span>
</span></span><span class="line"><span class="cl"> stack [ <span class="nb">last </span>seen adjoin ] <span class="nb">each
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! stop when we find any solutions</span>
</span></span><span class="line"><span class="cl"> stack [ <span class="nb">last </span>to <span class="nb">= </span>] <span class="nb">filter dup empty?
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop </span><span class="k">;
</span></span></span></code></pre></div><p><em>Note: we reject any states that are longer than our goal state. This provides
a nice way to cull the graph and make the search performance more reasonable.
You could also choose not do that, and exhaustively search into that area.
However, while this is not generally a valid approach to solving these types of
problems, it is specifically a valid approach to this one.</em></p>
<p>There are quite a few <strong>shortest</strong> paths:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"aaaaaaaa"</span> <span class="s">"aaaaaaaaaa"</span> shortest-paths <span class="nb">length </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">560
</span></span></span></code></pre></div><p>Each of those contain 16 nodes, which means 15 rules were applied:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"aaaaaaaa"</span> <span class="s">"aaaaaaaaaa"</span> shortest-paths <span class="nb">first length </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">16
</span></span></span></code></pre></div><p>But they only go through a seemingly small number of nodes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"aaaaaaaa"</span> <span class="s">"aaaaaaaaaa"</span> shortest-paths <span class="nb">concat </span>members <span class="nb">length </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">43
</span></span></span></code></pre></div><p>How many nodes are there in total in the graph? Let’s find out!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">full-graph</span> <span class="nf">( </span><span class="nv">from</span> <span class="nv">to</span> <span class="nf">-- </span><span class="nv">seen</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> HS{ from } <span class="nb">clone </span>:> seen
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> { { from } } [
</span></span><span class="line"><span class="cl"> <span class="c">! find all next possibilities</span>
</span></span><span class="line"><span class="cl"> all-paths
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! reject any that are over the length of ``to``</span>
</span></span><span class="line"><span class="cl"> to <span class="nb">length </span>'[ <span class="nb">last length </span>_ <span class="nb">> </span>] reject
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! only include ones that visit new nodes</span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">last </span>seen ?adjoin ] <span class="nb">filter
</span></span></span><span class="line"><span class="cl"> ] until-empty seen <span class="k">;
</span></span></span></code></pre></div><p>We can see that the shortest solutions go through about 15% of the nodes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"aaaaaaaa"</span> <span class="s">"aaaaaaaaaa"</span> full-graph cardinality <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">279
</span></span></span></code></pre></div><p>We can use our <a href="https://en.wikipedia.org/wiki/Graph_traversal">graph
traversal</a> approach and
<a href="https://graphviz.org/documentation/">Graphviz</a> to visualize where solutions
are found, showing how some areas of the graph are quite hard to randomly
get out of and then on the correct path to a solution. We draw the starting
node <strong><font color=lightgreen>green</font></strong>, the ending node <strong><font
color=deepskyblue>blue</font></strong>, and the nodes that involved in the shortest
path as <strong><font color=gray>gray</font></strong>:</p>
<p>
<img src="https://re.factorcode.org/images/2025-06-04-graph1.png" alt="" width="1400" height="1183" />
</p>
<p>And that’s kind of interesting, but if we cluster nodes by their depth when
first discovered, some other patterns show up:</p>
<p>
<img src="https://re.factorcode.org/images/2025-06-04-graph2.png" alt="" width="643" height="1538" />
</p>
<p>Such a fun problem!</p>
Bitcask
https://re.factorcode.org/2025/06/bitcask.html
Tue, 03 Jun 2025 08:00:00 -0700https://re.factorcode.org/2025/06/bitcask.html<p><a href="https://eatonphil.com">Phil Eaton</a> issued a
<a href="https://x.com/eatonphil/status/1929654227451736477">HYIBY?</a> – <em>have you
implemented bitcask yet?</em> – challenge yesterday. Of course, I immediately
realized that I <strong>have not</strong> and also that it would be fun to build in
<a href="https://factorcode.org">Factor</a>.</p>
<p><a href="https://en.wikipedia.org/wiki/Bitcask">Bitcask</a> is described in <a href="https://riak.com/assets/bitcask-intro.pdf">the original
Bitcask paper</a> as a “<em>log-structured
hash-table for fast key/value data</em>”, and was part of the software developed by
Basho Technologies as part of the <a href="https://en.wikipedia.org/wiki/Riak">Riak distributed
database</a>. Besides the original paper,
various developers over the years have bumped into Bitcask and implemented it
in different programming languages. <a href="https://arpitbhayani.me">Arpit Bhayani</a>,
for example, has a nice <a href="https://arpitbhayani.me/blogs/bitcask/">blog post describing
Bitcask</a> that is worth reading for
more background.</p>
<p>At its core, Bitcask describes an
<a href="https://en.wikipedia.org/wiki/Append-only">append-only</a> storage mechanism for
building a <a href="https://en.wikipedia.org/wiki/Key%E2%80%93value_database">key-value
database</a>. It consists of
one-or-more <em>data files</em>, each of which has an optional <em>index file</em> to allow
faster recovery when initializing the database, and generally supports <em>GET</em>,
<em>PUT</em>, and <em>DELETE</em> operations.</p>
<h2 id="data-files">Data Files</h2>
<p>Our <em>data file</em> contains a series of <em>entry</em> records. Each record consists of a
<code>key length</code>, <code>value length</code>, <code>key bytes</code>, and <code>value bytes</code>. A simple
word provides a way to write these bytes to a file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-entry-bytes</span> <span class="nf">( </span><span class="nv">key</span> <span class="nv">value</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup length </span><span class="m">4 </span>>be <span class="nb">write </span>] <span class="nb">bi@ </span>[ <span class="nb">write </span>] <span class="nb">bi@ </span><span class="k">;
</span></span></span></code></pre></div><p>Then, using the <a href="https://docs.factorcode.org/content/article-serialize.html">serialize
vocabulary</a> we can
store Factor objects quite simply:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-entry</span> <span class="nf">( </span><span class="nv">key</span> <span class="nv">value</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ object>bytes ] <span class="nb">bi@ </span>write-entry-bytes <span class="k">;
</span></span></span></code></pre></div><p>We need the ability to store <em>tombstone</em> records which indicate that a key has
been deleted from the database. In this case, we choose to store a <em>zero-sized
value</em> to indicate that:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-tombstone</span> <span class="nf">( </span><span class="nv">key</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> object>bytes <span class="no">f </span>write-entry-bytes <span class="k">;
</span></span></span></code></pre></div><p>Assuming that a <em>data file</em> has had it’s <a href="https://docs.factorcode.org/content/word-stream-seek%2Cio.html">seek
position</a> moved
to the beginning of an entry record, we can read the value that it contains, or
return a boolean indicating that it is not found because it was stored as a
<em>tombstone</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-entry</span> <span class="nf">( -- </span><span class="nv">value/f</span> <span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>be> <span class="m">4 </span><span class="nb">read </span>be> [
</span></span><span class="line"><span class="cl"> <span class="nb">drop </span><span class="no">f f
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> [ <span class="nb">seek-relative seek-input </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">read </span>bytes>object <span class="no">t </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-zero </span><span class="k">;
</span></span></span></code></pre></div><h2 id="index-files">Index Files</h2>
<p>Our <em>index file</em> contains <em>hints</em> that provide a way to recover the record
offsets into the <em>data files</em>. These hints consist of a series of <em>index</em>
records. Each record consists of a <code>key length</code>, <code>key bytes</code>, and <code>file offset</code>.</p>
<p>We can write our <code>index</code> mapping of keys to offsets:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-index</span> <span class="nf">( </span><span class="nv">index</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ object>bytes <span class="nb">dup length </span><span class="m">4 </span>>be <span class="nb">write write </span>]
</span></span><span class="line"><span class="cl"> [ <span class="m">4 </span>>be <span class="nb">write </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">assoc-each </span><span class="k">;
</span></span></span></code></pre></div><p>And then read it back into memory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-index</span> <span class="nf">( -- </span><span class="nv">index</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> H{ } <span class="nb">clone </span>[
</span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>[
</span></span><span class="line"><span class="cl"> be> <span class="nb">read </span>bytes>object <span class="m">4 </span><span class="nb">read </span>be>
</span></span><span class="line"><span class="cl"> <span class="nb">swap pick set-at </span><span class="no">t
</span></span></span><span class="line"><span class="cl"> ] [ <span class="no">f </span>] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop </span><span class="k">;
</span></span></span></code></pre></div><p>We want to make the <em>index files</em> optional, continuing to recover the index by
first seeking to the last entry that we have in the index, and then continuing
to iterate across the records in the <em>data file</em> to recover the full index,
making sure to delete any items that are subsequently observed to contain
<em>tombstone</em> entries:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">recover-index</span> <span class="nf">( </span><span class="nv">index</span> <span class="nf">-- </span><span class="nv">index'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup values </span>[ maximum <span class="nb">seek-absolute seek-input </span>] <span class="nb">unless-empty
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="nb">tell-input </span><span class="m">4 </span><span class="nb">read </span>[
</span></span><span class="line"><span class="cl"> be> <span class="m">4 </span><span class="nb">read </span>be> [ <span class="nb">read </span>bytes>object ] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">pick delete-at drop </span>] [
</span></span><span class="line"><span class="cl"> [ <span class="nb">pick set-at </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">seek-relative seek-input </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-zero </span><span class="no">t
</span></span></span><span class="line"><span class="cl"> ] [ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop </span><span class="k">;
</span></span></span></code></pre></div><h2 id="bitcask-implementation">Bitcask Implementation</h2>
<p>The <a href="https://docs.factorcode.org/content/article-assocs-protocol.html">associative mapping
protocol</a>
describes the words that an <code>assoc</code> should support. This type of object
provides a mapping of <code>key</code> to <code>value</code>, with ways to add, update, and
delete these mappings.</p>
<p>We want our <code>bitcask</code> type to use a single <em>data file</em>, reading and
recovering from an <em>index file</em>, and then providing ways to modify – by
appending to the <em>data file</em> – the database.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">bitcask</span> <span class="nv">path</span> <span class="nv">index</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf"><bitcask></span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">bitcask</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> path <span class="nb">dup </span>touch-file
</span></span><span class="line"><span class="cl"> path <span class="s">".idx"</span> <span class="nb">append dup </span>touch-file
</span></span><span class="line"><span class="cl"> binary [ read-index ] with-file-reader
</span></span><span class="line"><span class="cl"> path binary [ recover-index ] with-file-reader
</span></span><span class="line"><span class="cl"> bitcask <span class="nb">boa </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">INSTANCE:</span> <span class="nc">bitcask</span> <span class="nc">assoc</span>
</span></span></code></pre></div><p>The application should control when and how these <em>index files</em> are persisted:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">save-index</span> <span class="nf">( </span><span class="nv">bitcask</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>path>> <span class="s">".idx"</span> <span class="nb">append </span>binary
</span></span><span class="line"><span class="cl"> [ index>> write-index ] with-file-writer <span class="k">;
</span></span></span></code></pre></div><p>The first operation we support will be <code>set-at</code>, updating the index after
writing the entry.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M::</span> <span class="nc">bitcask</span> <span class="nf">set-at</span> <span class="nf">( </span><span class="nv">value</span> <span class="nv">key</span> <span class="nv">bitcask</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> bitcask path>> binary [
</span></span><span class="line"><span class="cl"> <span class="nb">tell-output
</span></span></span><span class="line"><span class="cl"> key value write-entry
</span></span><span class="line"><span class="cl"> key bitcask index>> <span class="nb">set-at
</span></span></span><span class="line"><span class="cl"> ] with-file-appender <span class="k">;
</span></span></span></code></pre></div><p>Next, we support <code>at*</code>, to lookup a value by seeking in the <em>data file</em> and
reading the entry:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M::</span> <span class="nc">bitcask</span> <span class="nf">at*</span> <span class="nf">( </span><span class="nv">key</span> <span class="nv">bitcask</span> <span class="nf">-- </span><span class="nv">value/f</span> <span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> key bitcask index>> <span class="nb">at* </span>[
</span></span><span class="line"><span class="cl"> bitcask path>> binary [
</span></span><span class="line"><span class="cl"> <span class="nb">seek-absolute seek-input </span>read-entry
</span></span><span class="line"><span class="cl"> ] with-file-reader
</span></span><span class="line"><span class="cl"> ] [ <span class="nb">drop </span><span class="no">f f </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>And finally, <code>delete-at</code> removes a key from the index after writing a
tombstone:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M::</span> <span class="nc">bitcask</span> <span class="nf">delete-at</span> <span class="nf">( </span><span class="nv">key</span> <span class="nv">bitcask</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> key bitcask index>> <span class="nb">key? </span>[
</span></span><span class="line"><span class="cl"> bitcask path>> binary [
</span></span><span class="line"><span class="cl"> key write-tombstone
</span></span><span class="line"><span class="cl"> key bitcask index>> <span class="nb">delete-at
</span></span></span><span class="line"><span class="cl"> ] with-file-appender
</span></span><span class="line"><span class="cl"> ] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><p>The <code>assoc-size</code> of our database is the size of the index:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">bitcask</span> <span class="nf">assoc-size</span>
</span></span><span class="line"><span class="cl"> index>> <span class="nb">assoc-size </span><span class="k">;
</span></span></span></code></pre></div><p>It is helpful to implement <code>>alist</code> to provide a conversion to an <a href="https://docs.factorcode.org/content/article-alists.html">assocation
list</a>, although if the
database gets quite large, this might be of less practical value:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M::</span> <span class="nc">bitcask</span> <span class="nf">>alist</span> <span class="nf">( </span><span class="nv">bitcask</span> <span class="nf">-- </span><span class="nv">alist</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> bitcask path>> binary [
</span></span><span class="line"><span class="cl"> bitcask index>> [
</span></span><span class="line"><span class="cl"> <span class="nb">seek-absolute seek-input </span>read-entry <span class="no">t </span><span class="nb">assert=
</span></span></span><span class="line"><span class="cl"> ] { } <span class="nb">assoc-map-as
</span></span></span><span class="line"><span class="cl"> ] with-file-reader <span class="k">;
</span></span></span></code></pre></div><p>And a way to <code>clear-assoc</code> by writing tombstones and clearing the index:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M::</span> <span class="nc">bitcask</span> <span class="nf">clear-assoc</span> <span class="nf">( </span><span class="nv">bitcask</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> bitcask path>> binary [
</span></span><span class="line"><span class="cl"> bitcask index>>
</span></span><span class="line"><span class="cl"> <span class="nb">dup keys </span>[ write-tombstone ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> <span class="nb">clear-assoc
</span></span></span><span class="line"><span class="cl"> ] with-file-appender <span class="k">;
</span></span></span></code></pre></div><p>There are some elements desirable in a <em>production</em> database that are not
implemented, for example:</p>
<ul>
<li>reducing the amount of opening and closing files to increase performance</li>
<li>controlling when file writes are flushed to disk</li>
<li>storing other metadata such as timestamps for each entry</li>
<li>rolling over data log files as they reach a maximum size</li>
<li>providing a way to vacuum the database files to remove tombstone entries</li>
<li>compressing the database entries or data log files if size is a consideration</li>
<li>protocol for accessing it over the network by other parts of the application</li>
</ul>
<p>This is now available in the <a href="https://github.com/factor/factor">development
version</a> in the <a href="https://github.com/factor/factor/blob/master/extra/bitcask/bitcask.factor">bitcask
vocabulary</a>!</p>
Game of Life
https://re.factorcode.org/2025/05/game-of-life.html
Tue, 27 May 2025 08:00:00 -0700https://re.factorcode.org/2025/05/game-of-life.html<p>One of my first and most memorable graphical programs was implementing <a href="https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life">John
Conway’s Game of Life</a>.
At the time, that implementation was as a <a href="https://en.wikipedia.org/wiki/Java_applet">Java
applet</a>. I’ve revisited it
periodically in different <a href="https://en.wikipedia.org/wiki/Programming_language">programming
languages</a> including
several years ago when I started to <a href="https://github.com/factor/factor/commit/17eabacd2b939f82211b29942daa701ac6c8ceae">implement the Game of Life in
Factor</a>
– something I’ve always wanted to write about.</p>
<p>The <em>Game of Life</em> is a two-dimensional grid of square cells with fairly simple
logic. Each cell can be either <em>live</em> or <em>dead</em>. Each cell interacts with its
eight neighboring cells with the following rules determining the next state of
the game board:</p>
<ul>
<li>Any live cell with fewer than two live neighbours dies, as if by underpopulation.</li>
<li>Any live cell with two or three live neighbours lives on to the next generation.</li>
<li>Any live cell with more than three live neighbours dies, as if by overpopulation.</li>
<li>Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.</li>
</ul>
<p>You can run this in any release since <a href="https://re.factorcode.org/2018/07/factor-0-98-now-available.html">Factor 0.98</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"game-of-life"</span> run
</span></span></code></pre></div><p>And it will look something like this:</p>
<video preload="" controls>
<source src="https://re.factorcode.org/videos/game-of-life.mp4" type="video/mp4">
There should have been a video here but your browser does not seem
to support it.
</video>
<p>Let’s go ahead and build it!</p>
<h2 id="game-logic">Game Logic</h2>
<p>We will model our two-dimensional <em>game board</em> as an <em>array of arrays</em>. And
in particular, since each cell has only two states, we will use
<a href="https://docs.factorcode.org/content/article-bit-arrays.html">bit-arrays</a> to
reduce the memory requirements by efficiently storing the state, one bit for
each cell.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><grid></span> <span class="nf">( </span><span class="nv">rows</span> <span class="nv">cols</span> <span class="nf">-- </span><span class="nv">grid</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ _ <bit-array> ] <span class="nb">replicate </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">grid-dim</span> <span class="nf">( </span><span class="nv">grid</span> <span class="nf">-- </span><span class="nv">rows</span> <span class="nv">cols</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>] [ <span class="nb">first length </span>] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>Making a random grid, which is useful in testing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-grid</span> <span class="nf">( </span><span class="nv">rows</span> <span class="nv">cols</span> <span class="nf">-- </span><span class="nv">grid</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ _ { <span class="no">t f </span>} ?{ } randoms-as ] <span class="nb">replicate </span><span class="k">;
</span></span></span></code></pre></div><p>And a word we can use for debugging, to print a grid out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">grid.</span> <span class="nf">( </span><span class="nv">grid</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ [ <span class="sc">CHAR: # CHAR: . </span><span class="nb">? </span>] <span class="s">""</span> <span class="nb">map-as print </span>] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>Some implementations choose to make the game boards <em>infinite</em>, but we are
instead going to build a <em>wraparound</em> game board. This allows, for example, a
<a href="https://conwaylife.com/wiki/Glider">glider</a> shape to fly off the bottom right
and then re-appear on the top left of the board, which is a lot more fun to
watch.</p>
<p>A useful word calculates <em>adjacent indices</em> for a cell – that wrap at a
<code>max</code> value of <em>rows</em> or <em>columns</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">adjacent-indices</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">max</span> <span class="nf">-- </span><span class="nv">n-1</span> <span class="nv">n</span> <span class="nv">n+1</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> n [ max ] <span class="nb">when-zero </span><span class="m">1 </span><span class="nb">-
</span></span></span><span class="line"><span class="cl"> n
</span></span><span class="line"><span class="cl"> n <span class="m">1 </span><span class="nb">+ dup </span>max <span class="nb">= </span>[ <span class="nb">drop </span><span class="m">0 </span>] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><p>Test it out, showing how it might work in a hypothetical <code>10 x 10</code> grid:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="c">! in the middle</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">3 10 </span>adjacent-indices <span class="nb">3array </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">2 3 4 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">! at the start, wrapped around</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">0 10 </span>adjacent-indices <span class="nb">3array </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">9 0 1 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">! at the end, wrapped around</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">9 10 </span>adjacent-indices <span class="nb">3array </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">8 9 0 </span>}
</span></span></code></pre></div><p>The main game logic requires <em>counting neighbors</em> for each cell. Since each
cell can have 8 neighbors, we can store this count in a <em>half-byte</em> – a
<a href="https://en.wikipedia.org/wiki/Nibble">nibble</a> – which can hold the values
<code>[0..15]</code>. In the <em>batteries-included</em> <a href="https://docs.factorcode.org/content/article-vocab-index.html">standard
library</a>, we
have a <a href="https://docs.factorcode.org/content/vocab-nibble-arrays.html">nibble-arrays
vocabulary</a>
that makes this easy.</p>
<p>The simplest implementation would just iterate across the game board, and
for each cell that is <em>live</em>, increment the count for the neighboring
indices around it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">count-neighbors</span> <span class="nf">( </span><span class="nv">grid</span> <span class="nf">-- </span><span class="nv">counts</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> grid grid-dim :> <span class="nf">( </span><span class="nv">rows</span> <span class="nv">cols</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> rows [ cols <nibble-array> ] <span class="nb">replicate </span>:> neighbors
</span></span><span class="line"><span class="cl"> grid [| row j |
</span></span><span class="line"><span class="cl"> j rows adjacent-indices
</span></span><span class="line"><span class="cl"> [ neighbors <span class="nb">nth </span>] <span class="nb">tri@ </span>:> <span class="nf">( </span><span class="nv">above</span> <span class="nv">same</span> <span class="nv">below</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> row [| cell i |
</span></span><span class="line"><span class="cl"> cell [
</span></span><span class="line"><span class="cl"> i cols adjacent-indices
</span></span><span class="line"><span class="cl"> [ [ above [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">change-nth </span>] <span class="nb">tri@ </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">nip </span>[ same [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">change-nth </span>] <span class="nb">bi@ </span>]
</span></span><span class="line"><span class="cl"> [ [ below [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">change-nth </span>] <span class="nb">tri@ </span>]
</span></span><span class="line"><span class="cl"> <span class="nb">3tri
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-index
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-index </span>neighbors <span class="k">;
</span></span></span></code></pre></div><p>Then the last piece of game logic we need is to adjust the grid cells according
to the rules – making some transition from <em>live</em> to <em>dead</em>, and others from
<em>dead</em> to <em>live</em> based on their state and the neighboring counts.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">next-step</span> <span class="nf">( </span><span class="nv">grid</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> grid count-neighbors :> neighbors
</span></span><span class="line"><span class="cl"> grid [| row j |
</span></span><span class="line"><span class="cl"> j neighbors <span class="nb">nth </span>:> neighbor-row
</span></span><span class="line"><span class="cl"> row [| cell i |
</span></span><span class="line"><span class="cl"> i neighbor-row <span class="nb">nth
</span></span></span><span class="line"><span class="cl"> cell [
</span></span><span class="line"><span class="cl"> <span class="m">2 3 </span>between? i row <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="m">3 </span><span class="nb">= </span>[ <span class="no">t </span>i row <span class="nb">set-nth </span>] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-index
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-index </span><span class="k">;
</span></span></span></code></pre></div><p>Before we move on to creating a <a href="https://en.wikipedia.org/wiki/Graphical_user_interface">graphical user
interface</a> for the
game, let’s try it out in the <a href="https://docs.factorcode.org/content/article-listener.html">Factor
listener</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="c">! Create a random 10x10 grid</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 10 </span>random-grid
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">! Print it out</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup </span>grid.
</span></span><span class="line"><span class="cl">#..#..#.##
</span></span><span class="line"><span class="cl">##....####
</span></span><span class="line"><span class="cl">..###.####
</span></span><span class="line"><span class="cl">.##...#..#
</span></span><span class="line"><span class="cl">.##....###
</span></span><span class="line"><span class="cl">..###..#.#
</span></span><span class="line"><span class="cl">...###.#..
</span></span><span class="line"><span class="cl">.###....##
</span></span><span class="line"><span class="cl">#...###.##
</span></span><span class="line"><span class="cl">.##..#.#..
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">! Compute the neighbors for each cell</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup </span>count-neighbors <span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> N{ <span class="m">5 5 4 1 2 3 4 6 5 5 </span>}
</span></span><span class="line"><span class="cl"> N{ <span class="m">5 3 4 4 3 4 4 7 7 7 </span>}
</span></span><span class="line"><span class="cl"> N{ <span class="m">6 5 4 3 1 4 4 6 6 5 </span>}
</span></span><span class="line"><span class="cl"> N{ <span class="m">5 4 5 5 2 3 3 6 7 4 </span>}
</span></span><span class="line"><span class="cl"> N{ <span class="m">5 4 5 5 2 2 3 3 5 3 </span>}
</span></span><span class="line"><span class="cl"> N{ <span class="m">3 3 4 5 4 3 4 3 6 2 </span>}
</span></span><span class="line"><span class="cl"> N{ <span class="m">3 3 6 6 5 2 3 2 5 3 </span>}
</span></span><span class="line"><span class="cl"> N{ <span class="m">4 2 3 4 6 5 4 4 4 4 </span>}
</span></span><span class="line"><span class="cl"> N{ <span class="m">4 5 5 4 3 3 3 4 4 4 </span>}
</span></span><span class="line"><span class="cl"> N{ <span class="m">5 3 2 3 4 4 5 4 5 6 </span>}
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">! Compute the next generation</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup </span>next-step
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">! Print it out</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup </span>grid.
</span></span><span class="line"><span class="cl">.....#....
</span></span><span class="line"><span class="cl">.#..#.....
</span></span><span class="line"><span class="cl">...#......
</span></span><span class="line"><span class="cl">.....##...
</span></span><span class="line"><span class="cl">......##.#
</span></span><span class="line"><span class="cl">##...#.#.#
</span></span><span class="line"><span class="cl">##...###.#
</span></span><span class="line"><span class="cl">.##.......
</span></span><span class="line"><span class="cl">....###...
</span></span><span class="line"><span class="cl">.###......
</span></span></code></pre></div><p>It works!</p>
<h2 id="game-interface">Game Interface</h2>
<p>In Factor, one of the ways we can <a href="https://docs.factorcode.org/content/article-building-ui.html">build user
interfaces</a> is
using <em>gadgets</em> and <a href="https://docs.factorcode.org/content/article-gl-utilities.html">OpenGL rendering
instructions</a>.
We start by modeling our game as a
<a href="https://docs.factorcode.org/content/word-gadget,ui.gadgets.html">gadget</a> with
a <code>grid</code> object, a <code>size</code> that specifies the rendered pixels-per-cell, and
a <code>timer</code> to control the speed of repainting new generations.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">grid-gadget</span> < <span class="nc">gadget</span> <span class="nv">grid</span> <span class="nv">size</span> <span class="nv">timer</span> <span class="k">;
</span></span></span></code></pre></div><p>Our default gadget will have cells that are 20 pixels square, and repaint 10
times per second:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><grid-gadget></span> <span class="nf">( </span><span class="nv">grid</span> <span class="nf">-- </span><span class="nv">gadget</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> grid-gadget <span class="nb">new
</span></span></span><span class="line"><span class="cl"> <span class="nb">swap </span>>>grid
</span></span><span class="line"><span class="cl"> <span class="m">20 </span>>>size
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>'[ _ <span class="nb">dup </span>grid>> next-step relayout-1 ]
</span></span><span class="line"><span class="cl"> <span class="no">f </span>1/10 seconds <timer> >>timer <span class="k">;
</span></span></span></code></pre></div><p>Gadgets are <em>grafted</em> onto the render hierarchy, and then later <em>ungrafted</em>
when they are removed. We handle that state change by stopping the timer
before delegating to the parent to cleanup further:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">grid-gadget</span> <span class="nf">ungraft*</span>
</span></span><span class="line"><span class="cl"> [ timer>> stop-timer ] [ call-next-method ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>The default dimension for our gadget is the <em>grid dimension</em> times the pixel size:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">grid-gadget</span> <span class="nf">pref-dim*</span>
</span></span><span class="line"><span class="cl"> [ grid>> grid-dim <span class="nb">swap </span>] [ size>> '[ _ <span class="nb">* </span>] <span class="nb">bi@ 2array </span>] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>If the grid <code>size</code> changes – for example, by using the mouse scroll wheel to
zoom in or out – we can create and store a new grid, keeping the cells that
are visible in the same state they were in:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">update-grid</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> gadget dim>> <span class="nb">first2 </span>:> <span class="nf">( </span><span class="nv">w</span> <span class="nv">h</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> gadget size>> :> size
</span></span><span class="line"><span class="cl"> h w [ size <span class="nb">/i </span>] <span class="nb">bi@ </span>:> <span class="nf">( </span><span class="nv">new-rows</span> <span class="nv">new-cols</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> gadget grid>> :> grid
</span></span><span class="line"><span class="cl"> grid grid-dim :> <span class="nf">( </span><span class="nv">rows</span> <span class="nv">cols</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> rows new-rows <span class="nb">= not </span>cols new-cols <span class="nb">= not or </span>[
</span></span><span class="line"><span class="cl"> new-rows new-cols <grid> :> new-grid
</span></span><span class="line"><span class="cl"> rows new-rows min [| j |
</span></span><span class="line"><span class="cl"> cols new-cols min [| i |
</span></span><span class="line"><span class="cl"> i j grid <span class="nb">nth nth
</span></span></span><span class="line"><span class="cl"> i j new-grid <span class="nb">nth set-nth
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-integer
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-integer
</span></span></span><span class="line"><span class="cl"> new-grid gadget grid<<
</span></span><span class="line"><span class="cl"> ] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><p>We can draw the cells that are <em>live</em> as <code>black</code> squares:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">draw-cells</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> COLOR: black gl-color
</span></span><span class="line"><span class="cl"> gadget size>> :> size
</span></span><span class="line"><span class="cl"> gadget grid>> [| row j |
</span></span><span class="line"><span class="cl"> row [| cell i |
</span></span><span class="line"><span class="cl"> cell [
</span></span><span class="line"><span class="cl"> i j [ size <span class="nb">* </span>] <span class="nb">bi@ 2array </span>{ size size } gl-fill-rect
</span></span><span class="line"><span class="cl"> ] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-index
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-index </span><span class="k">;
</span></span></span></code></pre></div><p>And then draw the <code>gray</code> lines that define the grid of cells:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">draw-lines</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> gadget size>> :> size
</span></span><span class="line"><span class="cl"> gadget grid>> grid-dim :> <span class="nf">( </span><span class="nv">rows</span> <span class="nv">cols</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> COLOR: gray gl-color
</span></span><span class="line"><span class="cl"> cols rows [ size <span class="nb">* </span>] <span class="nb">bi@ </span>:> <span class="nf">( </span><span class="nv">w</span> <span class="nv">h</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> rows <span class="m">1 </span><span class="nb">+ </span>[| j |
</span></span><span class="line"><span class="cl"> j size <span class="nb">* </span>:> y
</span></span><span class="line"><span class="cl"> { <span class="m">0 </span>y } { w y } gl-line
</span></span><span class="line"><span class="cl"> ] <span class="nb">each-integer
</span></span></span><span class="line"><span class="cl"> cols <span class="m">1 </span><span class="nb">+ </span>[| i |
</span></span><span class="line"><span class="cl"> i size <span class="nb">* </span>:> x
</span></span><span class="line"><span class="cl"> { x <span class="m">0 </span>} { x h } gl-line
</span></span><span class="line"><span class="cl"> ] <span class="nb">each-integer </span><span class="k">;
</span></span></span></code></pre></div><p>Putting this together, we draw our <em>gadget</em> by updating the grid, drawing the
cells, and drawing the lines:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">grid-gadget</span> <span class="nf">draw-gadget*</span>
</span></span><span class="line"><span class="cl"> [ update-grid ] [ draw-cells ] [ draw-lines ] <span class="nb">tri </span><span class="k">;
</span></span></span></code></pre></div><p>And, with the <a href="https://re.factorcode.org/2010/09/visual-repl.html">“visual REPL”</a>, you can directly
render the <em>grid gadget</em>, to see it work:</p>
<p>
<img src="https://re.factorcode.org/images/2025-05-27-game-of-life.png" alt="" width="615" height="249" />
</p>
<p>We now need to build the <em>interactive</em> parts. Let’s first start by handling a
<em>click</em>, to toggle the state of a cell, and storing which state it was toggled
to in the <code>last-click</code> variable:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">last-click</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">on-click</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> gadget grid>> :> grid
</span></span><span class="line"><span class="cl"> gadget size>> :> size
</span></span><span class="line"><span class="cl"> grid grid-dim :> <span class="nf">( </span><span class="nv">rows</span> <span class="nv">cols</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> gadget hand-rel <span class="nb">first2 </span>[ size <span class="nb">/i </span>] <span class="nb">bi@ </span>:> <span class="nf">( </span><span class="nv">i</span> <span class="nv">j</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> i <span class="m">0 </span>cols <span class="m">1 </span><span class="nb">- </span>between?
</span></span><span class="line"><span class="cl"> j <span class="m">0 </span>rows <span class="m">1 </span><span class="nb">- </span>between? <span class="nb">and </span>[
</span></span><span class="line"><span class="cl"> i j grid <span class="nb">nth
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">not dup </span>last-click <span class="nb">set </span>] <span class="nb">change-nth
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">when </span>gadget relayout-1 <span class="k">;
</span></span></span></code></pre></div><p>That allows us to build a <em>drag</em> feature, where as we drag, we continue to
either set cells to <em>live</em> or <em>dead</em> according to what the first click was
doing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">on-drag</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> gadget grid>> :> grid
</span></span><span class="line"><span class="cl"> gadget size>> :> size
</span></span><span class="line"><span class="cl"> grid grid-dim :> <span class="nf">( </span><span class="nv">rows</span> <span class="nv">cols</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> gadget hand-rel <span class="nb">first2 </span>[ size <span class="nb">/i </span>] <span class="nb">bi@ </span>:> <span class="nf">( </span><span class="nv">i</span> <span class="nv">j</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> i <span class="m">0 </span>cols <span class="m">1 </span><span class="nb">- </span>between?
</span></span><span class="line"><span class="cl"> j <span class="m">0 </span>rows <span class="m">1 </span><span class="nb">- </span>between? <span class="nb">and </span>[
</span></span><span class="line"><span class="cl"> last-click <span class="nb">get </span>i j
</span></span><span class="line"><span class="cl"> grid <span class="nb">nth set-nth
</span></span></span><span class="line"><span class="cl"> gadget relayout-1
</span></span><span class="line"><span class="cl"> ] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><p>We implement a scrolling feature to adjust the <code>size</code> of the rendered cells,
clamping the value when it gets <em>too small</em> or <em>too large</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">on-scroll</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> scroll-direction <span class="nb">get second </span>{
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0 </span><span class="nb">> </span>] [ <span class="m">-2 </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0 </span><span class="nb">< </span>] [ <span class="m">2 </span>] }
</span></span><span class="line"><span class="cl"> [ <span class="m">0 </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond nip + </span><span class="m">4 30 </span>clamp
</span></span><span class="line"><span class="cl"> ] change-size relayout-1 <span class="k">;
</span></span></span></code></pre></div><p>And we store these as <code>"gestures"</code> that are supported by the gadget:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">grid-gadget <span class="s">"gestures"</span> [
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { T{ button-down { # <span class="m">1 </span>} } [ on-click ] }
</span></span><span class="line"><span class="cl"> { T{ drag { # <span class="m">1 </span>} } [ on-drag ] }
</span></span><span class="line"><span class="cl"> { mouse-scroll [ on-scroll ] }
</span></span><span class="line"><span class="cl"> } <span class="nb">assoc-union
</span></span></span><span class="line"><span class="cl">] change-word-prop
</span></span></code></pre></div><p>The last bit we need is to make the toolbar, which has a few <em>commands</em> we can
run:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">com-play</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> gadget timer>> restart-timer <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">com-stop</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> gadget timer>> stop-timer <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">com-clear</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> gadget <span class="nb">dup </span>grid>> [ clear-bits ] <span class="nb">each </span>relayout-1 <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">com-random</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> gadget <span class="nb">dup </span>grid>> [ [ <span class="nb">drop </span>{ <span class="no">t f </span>} random ] <span class="nb">map! drop </span>] <span class="nb">each </span>relayout-1 <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">com-glider</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> gadget <span class="nb">dup </span>grid>> :> grid
</span></span><span class="line"><span class="cl"> { { <span class="m">2 1 </span>} { <span class="m">3 2 </span>} { <span class="m">1 3 </span>} { <span class="m">2 3 </span>} { <span class="m">3 3 </span>} }
</span></span><span class="line"><span class="cl"> [ grid <span class="nb">nth </span><span class="no">t </span><span class="nb">-rot set-nth </span>] <span class="nb">assoc-each </span>relayout-1 <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">com-step</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> gadget <span class="nb">dup </span>grid>> next-step relayout-1 <span class="k">;
</span></span></span></code></pre></div><p>And then store these as the <code>"toolbar"</code> command map:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">grid-gadget <span class="s">"toolbar"</span> <span class="no">f </span>{
</span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">"1"</span> } } com-play }
</span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">"2"</span> } } com-stop }
</span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">"3"</span> } } com-clear }
</span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">"4"</span> } } com-random }
</span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">"5"</span> } } com-glider }
</span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">"6"</span> } } com-step }
</span></span><span class="line"><span class="cl">} define-command-map
</span></span></code></pre></div><p>And finally, we can wrap the <em>grid gadget</em> with something that makes a toolbar,
and creates a <a href="https://docs.factorcode.org/content/word-MAIN-WINDOW__colon__,ui.html">main
window</a>
when launched:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">life-gadget</span> < <span class="nc">track</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><life-gadget></span> <span class="nf">( -- </span><span class="nv">gadget</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> vertical life-gadget new-track
</span></span><span class="line"><span class="cl"> <span class="m">20 20 </span>make-grid <grid-gadget>
</span></span><span class="line"><span class="cl"> [ <toolbar> format-toolbar <span class="no">f </span>track-add ]
</span></span><span class="line"><span class="cl"> [ <span class="m">1 </span>track-add ] <span class="nb">bi </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">life-gadget</span> <span class="nf">focusable-child*</span> children>> <span class="nb">second </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">MAIN-WINDOW: life-window
</span></span><span class="line"><span class="cl"> { { title <span class="s">"Game of Life"</span> } }
</span></span><span class="line"><span class="cl"> <life-gadget> >>gadgets <span class="k">;
</span></span></span></code></pre></div><p>As with anything, there are probably things we could continue to improve in our
<a href="https://docs.factorcode.org/content/article-ui.html">UI framework</a>, but one of
the biggest missing pieces are examples of working code, which is largely what
motivated writing about this today.</p>
<p>Check it out!</p>
<p>And maybe think about how you might adjust it to be an <em>infinite</em> game board,
or to increase performance when computing the next generation, to improve the
OpenGL rendering logic, persist the game board between launches, or do things
like communicate age of each cell by the color that it is rendered with.</p>
Sorting IPv6
https://re.factorcode.org/2025/05/sorting-ipv6.html
Fri, 23 May 2025 08:00:00 -0700https://re.factorcode.org/2025/05/sorting-ipv6.html<p>Recently, <a href="https://utcc.utoronto.ca/~cks/">Chris Siebenmann</a> was lamenting
the <a href="https://utcc.utoronto.ca/~cks/space/blog/unix/SortingIPv6Addresses">lack of a good command line way to sort IPv6
addresses</a>.
This followed a post of his a few years ago about how <code>sort -V</code> can
<a href="https://utcc.utoronto.ca/~cks/space/blog/unix/SortingIPv4Addresses">easily sort IPv4
addresses</a>.
Since I had some fun talking about <a href="https://re.factorcode.org/2025/05/roman-sort.html">sorting Roman
numerals</a> recently – and we have an extensive
<a href="https://docs.factorcode.org/content/article-vocab-index.html">standard
library</a> – I
thought I’d talk about how you might solve this problem with
<a href="https://factorcode.org">Factor</a>.</p>
<p>As a reminder, <a href="https://en.wikipedia.org/wiki/IPv6">IPv6</a> uses a 128-bit
address space with more theoretical addresses than the older – but still quite
commonly used – <a href="https://en.wikipedia.org/wiki/IPv4">IPv4</a> 32-bit address space.</p>
<p>The internal network address of your computer is sometimes referred to as
<a href="https://en.wikipedia.org/wiki/Localhost">localhost</a> or a <a href="https://www.geeksforgeeks.org/what-is-a-loopback-address/">loopback
address</a>, and
represented as <code>127.0.0.1</code> in IPv4, or <code>::1</code> in IPv6. We have an
<a href="https://docs.factorcode.org/content/vocab-ip-parser.html">ip-parser
vocabulary</a> with
words for parsing and manipulating IP addresses as well as IP network strings
written in <a href="https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing">CIDR
notation</a>. We can
use these words to show how to translate these addresses to their byte
representation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"127.0.0.1"</span> parse-ipv4 <span class="m">.
</span></span></span><span class="line"><span class="cl">B{ <span class="m">127 0 0 1 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"::1"</span> parse-ipv6 <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">0 0 0 0 0 0 0 1 </span>}
</span></span></code></pre></div><p>And, we could use that to sort a list of addresses pretty easily:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> {
</span></span><span class="line"><span class="cl"> <span class="s">"127.0.0.1"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"1.1.1.1"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"8.8.8.8"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"192.168.10.40"</span>
</span></span><span class="line"><span class="cl"> } [ parse-ipv4 ] sort-by <span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> <span class="s">"1.1.1.1"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"8.8.8.8"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"127.0.0.1"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"192.168.10.40"</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> {
</span></span><span class="line"><span class="cl"> <span class="s">"2620:0:1cfe:face:b00c::3"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"2001:4860:4860::8844"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"2620:0:ccc::2"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"::1"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"2001:4860:4860::8888"</span>
</span></span><span class="line"><span class="cl"> } [ parse-ipv6 ] sort-by <span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> <span class="s">"::1"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"2001:4860:4860::8844"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"2001:4860:4860::8888"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"2620:0:ccc::2"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"2620:0:1cfe:face:b00c::3"</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>And so, now that <a href="https://re.factorcode.org/2022/02/speedrun-feedback.html">some great
feedback</a> encouraged
us to do command-line eval with
<a href="https://docs.factorcode.org/content/word-auto-use__que__%2Cparser.html">auto-use?</a>
enabled, we can run this easily as a <em>one-line script</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="c1"># make a file full of unsorted IPv6 addresses</span>
</span></span><span class="line"><span class="cl">$ cat <span class="s"><<EOF > ips.txt
</span></span></span><span class="line"><span class="cl"><span class="s">2620:0:1cfe:face:b00c::3
</span></span></span><span class="line"><span class="cl"><span class="s">2001:4860:4860::8844
</span></span></span><span class="line"><span class="cl"><span class="s">2620:0:ccc::2
</span></span></span><span class="line"><span class="cl"><span class="s">::1
</span></span></span><span class="line"><span class="cl"><span class="s">2001:4860:4860::8888
</span></span></span><span class="line"><span class="cl"><span class="s">EOF</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># show that you can parse the file as strings</span>
</span></span><span class="line"><span class="cl">$ cat ips.txt <span class="p">|</span> ./factor -e<span class="o">=</span><span class="s2">"read-lines ."</span>
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"2620:0:1cfe:face:b00c::3"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"2001:4860:4860::8844"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"2620:0:ccc::2"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"::1"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"2001:4860:4860::8888"</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># sort and print the sorted output</span>
</span></span><span class="line"><span class="cl">$ cat ips.txt <span class="p">|</span> ./factor -e<span class="o">=</span><span class="s2">"read-lines [ parse-ipv6 ] sort-by [ print ] each"</span>
</span></span><span class="line"><span class="cl">::1
</span></span><span class="line"><span class="cl">2001:4860:4860::8844
</span></span><span class="line"><span class="cl">2001:4860:4860::8888
</span></span><span class="line"><span class="cl">2620:0:ccc::2
</span></span><span class="line"><span class="cl">2620:0:1cfe:face:b00c::3
</span></span></code></pre></div><p>Pretty cool!</p>
Faster Leap Year?
https://re.factorcode.org/2025/05/faster-leap-year.html
Wed, 21 May 2025 08:00:00 -0700https://re.factorcode.org/2025/05/faster-leap-year.html<p>Last week, <a href="https://hueffner.de/falk/">Falk Hüffner</a> wrote about making <a href="https://hueffner.de/falk/blog/a-leap-year-check-in-three-instructions.html">a leap
year check in three
instructions</a>:</p>
<blockquote>
<p>With the following code, we can check whether a year 0 ≤ y ≤ 102499 is a
leap year with only about 3 CPU instructions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="nf">is_leap_year_fast</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">((</span><span class="n">y</span> <span class="o">*</span> <span class="mi">1073750999</span><span class="p">)</span> <span class="o">&</span> <span class="mi">3221352463</span><span class="p">)</span> <span class="o"><=</span> <span class="mi">126976</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>How does this work? The answer is surprisingly complex. This article
explains it, mostly to have some fun with bit-twiddling; at the end, I’ll
briefly discuss the practical use.</p>
<p>This is how a leap year check is typically implemented:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="nf">is_leap_year</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">((</span><span class="n">y</span> <span class="o">%</span> <span class="mi">4</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">((</span><span class="n">y</span> <span class="o">%</span> <span class="mi">100</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">((</span><span class="n">y</span> <span class="o">%</span> <span class="mi">400</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></blockquote>
<p>It would be fun to see how that works in <a href="https://factorcode.org">Factor</a> and
compare the relative performance between a simple version and the new
<em>super-fast-highly-optimized</em> 3 instruction version. To do that, we can use the
<a href="https://docs.factorcode.org/content/word-benchmark,tools.time.html">benchmark</a>
word to record execution time by calling it repeatedly and returning an average
<em>time-per-call</em> in nanoseconds:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TYPED:</span> <span class="nf">average-benchmark</span> <span class="nf">( </span><span class="nv">n:</span> <span class="nv">fixnum</span> <span class="nv">quot</span> <span class="nf">-- </span><span class="nv">nanos-per-call</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">over </span>[ '[ _ _ <span class="nb">times </span>] benchmark ] <span class="nb">dip /f </span><span class="k">; inline
</span></span></span></code></pre></div><p><em>Note: We are forcing the iteration loop above to be <code>fixnum</code> to reduce its
overhead, and due to the design of the benchmark words below, are going to have
code blocks with predictable inputs. Testing your program with random inputs is
also important to see the impact of CPU optimizations such as cache and branch
predictions, or across multiple CPU architectures. Performance is also impacted
by use of code generation features such as
<a href="https://docs.factorcode.org/content/word-inline,syntax.html">inline</a> and
compiler steps such as dead-code elimination. Benchmarking is hard.</em></p>
<h2 id="simple-implementation">Simple implementation</h2>
<p>The simple – and <em>typical</em> – implementation can be easily written as:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">leap-year?</span> <span class="nf">( </span><span class="nv">year</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">100 </span>divisor? <span class="m">400 4 </span><span class="nb">? </span>divisor? <span class="k">;
</span></span></span></code></pre></div><p>And in fact, that’s how <a href="https://docs.factorcode.org/content/word-leap-year__que__,calendar.html">it is
implemented</a>
in the <a href="https://docs.factorcode.org/content/article-vocab-index.html">standard
library</a>.</p>
<p>We can write a quick benchmarking word. This ensures we are using the
<a href="https://docs.factorcode.org/content/article-compiler.html">optimizing
compiler</a> and also
asserts that the result of the word is as expected:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bench-leap-year</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">year</span> <span class="nv">?</span> <span class="nf">-- </span><span class="nv">nanos</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ _ leap-year? _ <span class="nb">assert= </span>] average-benchmark <span class="k">;
</span></span></span></code></pre></div><p>And then call it one hundred million times, to see how long it takes each call
on average:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">100,000,000 2028 </span><span class="no">t </span>bench-leap-year <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">10.53904317
</span></span></span></code></pre></div><p>Just under 11 nanoseconds, including the loop and the assert…</p>
<h2 id="fast-implementation">Fast implementation</h2>
<p>The fast implementation suggested by Falk can be written directly as:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fast-leap-year?</span> <span class="nf">( </span><span class="nv">year</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1073750999 </span><span class="nb">* </span><span class="m">3221352463 </span><span class="nb">bitand </span><span class="m">126976 </span><span class="nb"><= </span><span class="k">;
</span></span></span></code></pre></div><p>And then write a benchmarking word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bench-fast-leap-year</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">year</span> <span class="nv">?</span> <span class="nf">-- </span><span class="nv">nanos</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ _ fast-leap-year? _ <span class="nb">assert= </span>] average-benchmark <span class="k">;
</span></span></span></code></pre></div><p>And see how long it takes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">100,000,000 2028 </span><span class="no">t </span>bench-fast-leap-year <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">4.74783302
</span></span></span></code></pre></div><p>Just under 5 nanoseconds…</p>
<h2 id="faster-implementation">Faster implementation</h2>
<p>Well, generally <a href="https://factorcode.org">Factor</a> supports <a href="https://docs.factorcode.org/content/article-integers.html">arbitrarily large
integers</a> by
allowing integers to implicitly promote from <em>word-sized</em> <code>fixnum</code> to
overflow into <code>bignum</code>. And, as they say, you can write the <a href="https://en.wikipedia.org/wiki/C_(programming_language)">C programming
language</a> in any
language.</p>
<p>A faster implementation might check the input is a <code>fixnum</code> and then force
math without overflow:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TYPED:</span> <span class="nf">faster-leap-year?</span> <span class="nf">( </span><span class="nv">year:</span> <span class="nv">fixnum</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1073750999 </span>fixnum*fast <span class="m">3221352463 </span>fixnum-bitand <span class="m">126976 </span>fixnum<= <span class="k">;
</span></span></span></code></pre></div><p>And write a benchmark word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bench-faster-leap-year</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">year</span> <span class="nv">?</span> <span class="nf">-- </span><span class="nv">nanos</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ _ faster-leap-year? _ <span class="nb">assert= </span>] average-benchmark <span class="k">;
</span></span></span></code></pre></div><p>It’s a bit faster:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">100,000,000 2028 </span><span class="no">t </span>bench-faster-leap-year <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">3.24267145
</span></span></span></code></pre></div><p>Just under 4 nanoseconds…</p>
<h2 id="fastest-implementation">Fastest implementation</h2>
<p>But, to make sure that we take advantage of the least amount of instructions
possible, we can make it <em>slightly-less-safe</em> by declaring the input to be a
<code>fixnum</code> to avoid the run-time type checks. This could cause issues if it is
called with other types on the stack.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fastest-leap-year?</span> <span class="nf">( </span><span class="nv">year</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { <span class="nb">fixnum </span>} declare
</span></span><span class="line"><span class="cl"> <span class="m">1073750999 </span>fixnum*fast <span class="m">3221352463 </span>fixnum-bitand <span class="m">126976 </span>fixnum<= <span class="k">;
</span></span></span></code></pre></div><p>And write a benchmark word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bench-fastest-leap-year</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">year</span> <span class="nv">?</span> <span class="nf">-- </span><span class="nv">nanos</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ _ fastest-leap-year? _ <span class="nb">assert= </span>] average-benchmark <span class="k">;
</span></span></span></code></pre></div><p>And then you can see it gets quite fast indeed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">100,000,000 2028 </span><span class="no">t </span>bench-fastest-leap-year <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">2.82150476
</span></span></span></code></pre></div><p>Just under 3 nanoseconds!</p>
<p>But, is it also just 3 instructions?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="no">\ fastest-leap-year?</span> disassemble
</span></span><span class="line"><span class="cl">000075f0afa19490: 89056a5bd1fe mov [rip-0x12ea496], eax
</span></span><span class="line"><span class="cl">000075f0afa19496: 498b06 mov rax, [r14]
</span></span><span class="line"><span class="cl">000075f0afa19499: 48c1f804 sar rax, <span class="m">0x4
</span></span></span><span class="line"><span class="cl">000075f0afa1949d: 4869c0d7230040 imul rax, rax, <span class="m">0x400023d7
</span></span></span><span class="line"><span class="cl">000075f0afa194a4: bb0ff001c0 mov ebx, <span class="m">0xc001f00f
</span></span></span><span class="line"><span class="cl">000075f0afa194a9: 4821d8 <span class="nb">and </span>rax, rbx
</span></span><span class="line"><span class="cl">000075f0afa194ac: 4881f800f00100 cmp rax, <span class="m">0x1f000
</span></span></span><span class="line"><span class="cl">000075f0afa194b3: b801000000 mov eax, <span class="m">0x1
</span></span></span><span class="line"><span class="cl">000075f0afa194b8: 48bb5c0e388cf0750000 mov rbx, <span class="m">0x75f08c380e5c
</span></span></span><span class="line"><span class="cl">000075f0afa194c2: 480f4ec3 cmovle rax, rbx
</span></span><span class="line"><span class="cl">000075f0afa194c6: <span class="m">498906 </span> mov [r14], rax
</span></span><span class="line"><span class="cl">000075f0afa194c9: 8905315bd1fe mov [rip-0x12ea4cf], eax
</span></span><span class="line"><span class="cl">000075f0afa194cf: c3 ret
</span></span></code></pre></div><p>Pretty close!</p>
<p>There is an extra instruction near the beginning to <em>untag</em> our <code>fixnum</code>
input. Due to the convention around handling <a href="https://docs.factorcode.org/content/article-booleans.html">booleans in
Factor</a>, there
are a couple of extra instructions at the end for converting the result into
a return value of either <code>t</code> or <code>f</code>.</p>
<p>And it could get even faster if either the <code>assert=</code> was removed, or the code
was made <a href="https://docs.factorcode.org/content/word-inline,syntax.html">inline</a>
so the function <a href="https://en.wikipedia.org/wiki/Function_prologue_and_epilogue">prologue and
epilogue</a> could
be elided into the outer scope.</p>
<p>So much fun.</p>
Raylib
https://re.factorcode.org/2025/05/raylib.html
Tue, 20 May 2025 08:00:00 -0700https://re.factorcode.org/2025/05/raylib.html<p><a href="https://raylib.com">Raylib</a> is a very neat C library that has become popular
as a “<em>simple and easy-to-use library to enjoy videogames programming</em>”.
Originally released in 2014, it has seen lots of updates including with the
latest <a href="https://github.com/raysan5/raylib/releases/tag/5.5">version 5.5</a>
representing <strong>11 years</strong> of updates since the original version 1.0 release.</p>
<p>You can sense the love this library has received from the extensive feature list:</p>
<blockquote>
<ul>
<li><strong>NO external dependencies</strong>, all required libraries included with raylib</li>
<li>Multiplatform: <strong>Windows, Linux, MacOS, RPI, Android, HTML5… and more!</strong></li>
<li>Written in plain C code (C99) using PascalCase/camelCase notation</li>
<li>Hardware accelerated with OpenGL (<strong>1.1, 2.1, 3.3, 4.3 or ES 2.0</strong>)</li>
<li><strong>Unique OpenGL abstraction</strong> layer: <a href="https://github.com/raysan5/raylib/blob/master/src/rlgl.h">rlgl</a></li>
<li>Powerful <strong>Fonts</strong> module (SpriteFonts, BMfonts, TTF, SDF)</li>
<li>Multiple texture formats support, including compressed formats (DXT, ETC, ASTC)</li>
<li><strong>Full 3d support</strong> for 3d Shapes, Models, Billboards, Heightmaps and more!</li>
<li>Flexible Materials system, supporting classic maps and <strong>PBR maps</strong></li>
<li><strong>Animated 3d models</strong> supported (skeletal bones animation)</li>
<li>Shaders support, including <strong>Model shaders</strong> and <strong>Postprocessing shaders</strong></li>
<li><strong>Powerful math module</strong> for Vector, Matrix and Quaternion operations: <a href="https://github.com/raysan5/raylib/blob/master/src/raymath.h">raymath</a></li>
<li>Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD)</li>
<li><strong>VR stereo rendering</strong> support with configurable HMD device parameters</li>
<li>Huge examples collection with <a href="https://www.raylib.com/examples.html">+120 code examples</a>!</li>
<li>Bindings to <a href="https://github.com/raysan5/raylib/blob/master/BINDINGS.md">+60 programming languages</a>!</li>
<li>Free and open source. Check [<a href="https://www.raylib.com/license.html">LICENSE</a>].</li>
</ul>
</blockquote>
<p>In 2019, we first included <a href="https://github.com/factor/factor/commit/a8200d61b28ed78399ce7cc5838b22bd18667651">support for version
2.5</a>
of <code>raylib</code>. Over the years, we have updated our bindings to include new
functions and features of new versions. Our most recent two releases, <a href="https://re.factorcode.org/2023/08/factor-0-99-now-available.html">Factor
0.99</a> and <a href="https://re.factorcode.org/2024/09/factor-0-100-now-available.html">Factor
0.100</a> included support for version
4.5. And in the current development cycle, we have <a href="https://github.com/factor/factor/commit/dc4d35974a3d6a148f552c954d0b02043001f018">updated to version
5.0</a>
and then again <a href="https://github.com/factor/factor/commit/9a3f6b9bc648f1539dba288bec77f8864fc3749e">updated to version
5.5</a>.</p>
<p>It is possible to maintain support for multiple versions, but we have chosen
for now to just target the most recent stable release as best we can.
Generally, the <code>raylib</code> library has been quite stable, with the
occasional deprecation or breaking change which seem to be easy to adjust
for.</p>
<p>As a simple demonstration, we can start with the <a href="https://www.raylib.com/examples/core/loader.html?name=core_basic_window">basic window
example</a>
– an extremely simple program in the spirit of achieving a <a href="https://rampantgames.com/blog/?p=7745">black
triangle</a> moment – to make sure
everything works. We can directly translate this example into
<a href="https://factorcode.org">Factor</a> using our <a href="https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor">Raylib
bindings</a>
– which import the C functions using our convention for
<a href="https://developer.mozilla.org/en-US/docs/Glossary/Kebab_case">kebab-case</a> word
names.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USE:</span> <span class="nn">raylib</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">basic-window</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="m">800 450 </span><span class="s">"raylib [core] example - basic window"</span> init-window
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="m">60 </span>set-target-fps
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ window-should-close ] [
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> begin-drawing
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> RAYWHITE clear-background
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"Congrats! You created your first window!"</span>
</span></span><span class="line"><span class="cl"> <span class="m">190 200 20 </span>LIGHTGRAY draw-text
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> end-drawing
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> ] <span class="nb">until </span>close-window <span class="k">;
</span></span></span></code></pre></div><p>And, if you run the example – you’ll end up with this:</p>
<p>
<img src="https://re.factorcode.org/images/2025-05-20-raylib-basic-window.png" alt="" width="798" height="449" />
</p>
<p><em>Note: if you’re wondering why the background isn’t white, it’s because
<code>RAYWHITE</code> is a special version of not-quite-white that matches the Raylib
logo.</em></p>
<p>Of course, your examples can become more complex. For example, <a href="https://github.com/JosephOziel">Joseph
Oziel</a> released a fun game called
<a href="https://re.factorcode.org/2024/07/bitguessr.html">BitGuessr</a> written in Factor using Raylib. And,
given the extensive feature list, your <em>simple</em> program written quickly for
a <a href="https://en.wikipedia.org/wiki/Game_jam">game jam</a> might end up including
great audio, images, keyboard, mouse, gamepad, 3d rendering, and other
features relatively easily!</p>
<p>I’d love to see more demos written in <a href="https://factorcode.org">Factor</a> and
<a href="https://raylib.com">Raylib</a>. Happy coding!</p>
Shuffle Syntax
https://re.factorcode.org/2025/05/shuffle-syntax.html
Sun, 18 May 2025 08:00:00 -0700https://re.factorcode.org/2025/05/shuffle-syntax.html<p>Some might describe <a href="https://docs.factorcode.org/content/article-shuffle-words.html">shuffle
words</a> as one
of the fundamental building blocks of <a href="https://factorcode.org">Factor</a>. Others
might describe them as a <a href="https://en.wikipedia.org/wiki/Code_smell">code smell</a>
and seek to use <a href="https://docs.factorcode.org/content/article-dataflow-combinators.html">dataflow
combinators</a>
or other higher-level words to reduce code complexity.</p>
<p>Whatever your opinion is, they are useful concepts in a <a href="https://concatenative.org/wiki/view/Concatenative%20language">concatenative
language</a>.
Besides the basic shuffle words – like <code>dup</code>, <code>swap</code>, <code>rot</code> – we have
had the <a href="https://docs.factorcode.org/content/vocab-shuffle.html">shuffle
vocabulary</a> which
provides some “additional shuffle words” for awhile, as well as a syntax word
that can perform arbitrary shuffles:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">shuffle</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1 2 3 4 5 </span>} [
</span></span><span class="line"><span class="cl"> shuffle( a b c d e -- d e c a b )
</span></span><span class="line"><span class="cl"> ] <span class="nb">with-datastack </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">4 5 3 1 2 </span>}
</span></span></code></pre></div><p>This would be quite useful, except that it has had a fundamental issue – the
way it is implemented uses a macro to curry the stack arguments into an array,
and then pull the stack arguments back out of the array onto the stack in the
requested order.</p>
<p>For example, we can look at a simple <code>swap</code> and a complex shuffle:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ shuffle( x y -- y x ) ] optimized.
</span></span><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl"> <span class="m">2 0 </span><span class="nb"><array> dup </span>>R >R R> <span class="m">3 </span>set-slot
</span></span><span class="line"><span class="cl"> R> >R R> <span class="nb">dup </span>>R >R R> <span class="m">2 </span>set-slot
</span></span><span class="line"><span class="cl"> R> >R R> <span class="nb">dup </span>>R >R R> >R R> <span class="m">3 </span>slot R> >R R> >R R> <span class="m">2 </span>slot
</span></span><span class="line"><span class="cl">]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ shuffle( a b c d e -- b a d c e ) ] optimized.
</span></span><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl"> <span class="m">5 0 </span><span class="nb"><array> dup </span>>R >R R> <span class="m">6 </span>set-slot
</span></span><span class="line"><span class="cl"> R> >R R> <span class="nb">dup </span>>R >R R> <span class="m">5 </span>set-slot
</span></span><span class="line"><span class="cl"> R> >R R> <span class="nb">dup </span>>R >R R> <span class="m">4 </span>set-slot
</span></span><span class="line"><span class="cl"> R> >R R> <span class="nb">dup </span>>R >R R> <span class="m">3 </span>set-slot
</span></span><span class="line"><span class="cl"> R> >R R> <span class="nb">dup </span>>R >R R> <span class="m">2 </span>set-slot
</span></span><span class="line"><span class="cl"> R> >R R> <span class="nb">dup </span>>R >R R> >R R> <span class="m">3 </span>slot
</span></span><span class="line"><span class="cl"> R> <span class="nb">dup </span>>R >R R> >R R> <span class="m">2 </span>slot R> <span class="nb">dup </span>>R >R R> >R R> <span class="m">5 </span>slot
</span></span><span class="line"><span class="cl"> R> <span class="nb">dup </span>>R >R R> >R R> <span class="m">4 </span>slot R> >R R> >R R> <span class="m">6 </span>slot
</span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><p>And not only would this be <em>less-than-efficient</em>, it would also turn literal
arguments that were on the stack into run-time arguments and potentially cause
a <code>Cannot apply 'call' to a run-time computed value</code> error if one of the
shuffled arguments is a
<a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a> they
hope to use.</p>
<p>This bug was described on <a href="https://github.com/factor/factor/issues/3110">our issue
tracker</a> and I spent some time
recently looking into it.</p>
<p>It turns out that we can use the <a href="https://docs.factorcode.org/content/article-inference.html">stack
checker</a> to
indicate that a <em>shuffle</em> is taking place, and use some <code>"special"</code> machinery
to allow the optimizing compiler to generate efficient and correct code for
these arbitrary shuffles.</p>
<p>After <a href="https://github.com/factor/factor/commit/1d6a5c7731140969400a691a92013bbebb0d0a41">applying a small
fix</a>,
we can see that the earlier examples are now quite simple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ shuffle( x y -- y x ) ] optimized.
</span></span><span class="line"><span class="cl">[ <span class="nb">swap </span>]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ shuffle( a b c d e -- b a d c e ) ] optimized.
</span></span><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl"> <span class="nf">( </span><span class="nv">10791205</span> <span class="nv">10791206</span> <span class="nv">10791207</span> <span class="nv">10791208</span> <span class="nv">10791209</span> <span class="nf">-- </span><span class="nv">10791206</span> <span class="nv">10791205</span> <span class="nv">10791208</span> <span class="nv">10791207</span> <span class="nv">10791209</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><p>This is available in the latest <a href="https://github.com/factor/factor">development
version</a>.</p>
Even More Brainf*ck
https://re.factorcode.org/2025/05/even-more-brainf-ck.html
Sat, 17 May 2025 08:00:00 -0700https://re.factorcode.org/2025/05/even-more-brainf-ck.html<p>I was distracted a little by some recent explorations <a href="https://re.factorcode.org/2025/05/more-brainf-ck.html">building a Brainfuck
interpreter</a> in <a href="https://factorcode.org">Factor</a>
and had a couple of follow-ups to add to the conversation.</p>
<p>First, I realized my initial <em>quick-and-dirty</em> Brainfuck interpreter didn’t
support nested loops. Specifically, the logic for beginning or ending a
loop just searched for the nearest <code>[</code> or <code>]</code> character without
considering nesting. This was <a href="https://github.com/factor/factor/commit/1ee37749ea692132423bf46707d0c6a45b677fec">fixed
today</a>
so that will no longer be an issue.</p>
<p>Second, despite the Brainfuck compiler implicitly making an AST (<a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree">abstract syntax
tree</a>) for
<a href="https://en.wikipedia.org/wiki/Brainfuck">Brainfuck</a> by virtue of generating a
<a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a>, I
thought it would be more fun to build and generate one intentionally.</p>
<p>We can model the <a href="https://brainfuck.org/brainfuck.html">Brainfuck commands</a> as
operations using the following
<a href="https://docs.factorcode.org/content/article-tuples.html">tuples</a> and
<a href="https://docs.factorcode.org/content/article-singletons.html">singletons</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">ptr</span> <span class="nv">n</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">mem</span> <span class="nv">n</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">SINGLETONS:</span> <span class="nc">output</span> <span class="nc">input</span> <span class="nc">debug</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">loop</span> <span class="nv">ops</span> <span class="k">;
</span></span></span></code></pre></div><p>Next, we can build a parser using
<a href="https://docs.factorcode.org/content/article-peg.ebnf.html">EBNF</a> to convert
the textual commands into our Brainfuck AST:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">EBNF: ast-brainfuck [=[
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">inc-ptr <span class="nb">= </span>(">")+ => [[ <span class="nb">length </span>ptr <span class="nb">boa </span>]]
</span></span><span class="line"><span class="cl">dec-ptr <span class="nb">= </span>("<")+ => [[ <span class="nb">length neg </span>ptr <span class="nb">boa </span>]]
</span></span><span class="line"><span class="cl">inc-mem <span class="nb">= </span>("+")+ => [[ <span class="nb">length </span>mem <span class="nb">boa </span>]]
</span></span><span class="line"><span class="cl">dec-mem <span class="nb">= </span>("-")+ => [[ <span class="nb">length neg </span>mem <span class="nb">boa </span>]]
</span></span><span class="line"><span class="cl">output <span class="nb">= </span><span class="s">"."</span> => [[ output ]]
</span></span><span class="line"><span class="cl">input <span class="nb">= </span><span class="s">","</span> => [[ input ]]
</span></span><span class="line"><span class="cl">debug <span class="nb">= </span><span class="s">"#"</span> => [[ debug ]]
</span></span><span class="line"><span class="cl">space <span class="nb">= </span>[ \t\n\r]+ => [[ <span class="no">f </span>]]
</span></span><span class="line"><span class="cl">unknown <span class="nb">= </span>(.) => [[ <span class="s">"Invalid input"</span> <span class="nb">throw </span>]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">ops <span class="nb">= </span>inc-ptr|dec-ptr|inc-mem|dec-mem|output|input|debug|space
</span></span><span class="line"><span class="cl"><span class="nb">loop </span> <span class="nb">= </span><span class="s">"["</span> {loop|ops}+ <span class="s">"]"</span> => [[ <span class="nb">second sift loop boa </span>]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">code <span class="nb">= </span>(loop|ops|unknown)* => [[ <span class="nb">sift </span>]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">]=]
</span></span></code></pre></div><p>This is interesting, because now we can more easily analyze a piece of
Brainfuck code, such as the <em>Hello, World</em> example that I have been frequently
using:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"
</span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[>+++++++>++++++++++>+++>+<<<<-]
</span></span></span><span class="line"><span class="cl"><span class="s"> >++.>+.+++++++..+++.>++.<<+++++++++++++++
</span></span></span><span class="line"><span class="cl"><span class="s"> .>.+++.------.--------.>+.>.
</span></span></span><span class="line"><span class="cl"><span class="s"> "</span> ast-brainfuck <span class="m">.
</span></span></span><span class="line"><span class="cl">V{
</span></span><span class="line"><span class="cl"> T{ mem { n <span class="m">10 </span>} }
</span></span><span class="line"><span class="cl"> T{ <span class="nb">loop
</span></span></span><span class="line"><span class="cl"> { ops
</span></span><span class="line"><span class="cl"> V{
</span></span><span class="line"><span class="cl"> T{ ptr { n <span class="m">1 </span>} }
</span></span><span class="line"><span class="cl"> T{ mem { n <span class="m">7 </span>} }
</span></span><span class="line"><span class="cl"> T{ ptr { n <span class="m">1 </span>} }
</span></span><span class="line"><span class="cl"> T{ mem { n <span class="m">10 </span>} }
</span></span><span class="line"><span class="cl"> T{ ptr { n <span class="m">1 </span>} }
</span></span><span class="line"><span class="cl"> T{ mem { n <span class="m">3 </span>} }
</span></span><span class="line"><span class="cl"> T{ ptr { n <span class="m">1 </span>} }
</span></span><span class="line"><span class="cl"> T{ mem { n <span class="m">1 </span>} }
</span></span><span class="line"><span class="cl"> T{ ptr { n <span class="m">-4 </span>} }
</span></span><span class="line"><span class="cl"> T{ mem { n <span class="m">-1 </span>} }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ ptr { n <span class="m">1 </span>} }
</span></span><span class="line"><span class="cl"> T{ mem { n <span class="m">2 </span>} }
</span></span><span class="line"><span class="cl"> output
</span></span><span class="line"><span class="cl"> T{ ptr { n <span class="m">1 </span>} }
</span></span><span class="line"><span class="cl"> T{ mem { n <span class="m">1 </span>} }
</span></span><span class="line"><span class="cl"> output
</span></span><span class="line"><span class="cl"> T{ mem { n <span class="m">7 </span>} }
</span></span><span class="line"><span class="cl"> output
</span></span><span class="line"><span class="cl"> output
</span></span><span class="line"><span class="cl"> T{ mem { n <span class="m">3 </span>} }
</span></span><span class="line"><span class="cl"> output
</span></span><span class="line"><span class="cl"> T{ ptr { n <span class="m">1 </span>} }
</span></span><span class="line"><span class="cl"> T{ mem { n <span class="m">2 </span>} }
</span></span><span class="line"><span class="cl"> output
</span></span><span class="line"><span class="cl"> T{ ptr { n <span class="m">-2 </span>} }
</span></span><span class="line"><span class="cl"> T{ mem { n <span class="m">15 </span>} }
</span></span><span class="line"><span class="cl"> output
</span></span><span class="line"><span class="cl"> T{ ptr { n <span class="m">1 </span>} }
</span></span><span class="line"><span class="cl"> output
</span></span><span class="line"><span class="cl"> T{ mem { n <span class="m">3 </span>} }
</span></span><span class="line"><span class="cl"> output
</span></span><span class="line"><span class="cl"> T{ mem { n <span class="m">-6 </span>} }
</span></span><span class="line"><span class="cl"> output
</span></span><span class="line"><span class="cl"> T{ mem { n <span class="m">-8 </span>} }
</span></span><span class="line"><span class="cl"> output
</span></span><span class="line"><span class="cl"> T{ ptr { n <span class="m">1 </span>} }
</span></span><span class="line"><span class="cl"> T{ mem { n <span class="m">1 </span>} }
</span></span><span class="line"><span class="cl"> output
</span></span><span class="line"><span class="cl"> T{ ptr { n <span class="m">1 </span>} }
</span></span><span class="line"><span class="cl"> output
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>And then we can implement those operations against a <code>brainfuck</code> state
object, by deferring to words from our <a href="https://github.com/factor/factor/blob/master/extra/brainfuck/brainfuck.factor">current
implementation</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">GENERIC:</span> <span class="nf">op</span> <span class="nf">( </span><span class="nv">brainfuck</span> <span class="nv">op</span> <span class="nf">-- </span><span class="nv">brainfuck</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">ptr</span> <span class="nf">op</span> n>> (>) <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">mem</span> <span class="nf">op</span> n>> (+) <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">output</span> <span class="nf">op</span> <span class="nb">drop </span>(.) <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">input</span> <span class="nf">op</span> <span class="nb">drop </span>(,) <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">debug</span> <span class="nf">op</span> <span class="nb">drop </span>(#) <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">loop</span> <span class="nf">op</span> [ get-memory <span class="nb">zero? </span>] <span class="nb">swap </span>ops>> '[ _ [ op ] <span class="nb">each </span>] <span class="nb">until </span><span class="k">;
</span></span></span></code></pre></div><p>And now this Brainfuck AST represents a hybrid execution model somewhere
between the <em>compiled</em> and <em>interpreted</em> versions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">hybrid-brainfuck</span> <span class="nf">( </span><span class="nv">code</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <brainfuck> ] <span class="nb">dip </span>ast-brainfuck [ op ] <span class="nb">each drop </span><span class="k">;
</span></span></span></code></pre></div><p>And see that it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"
</span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[>+++++++>++++++++++>+++>+<<<<-]
</span></span></span><span class="line"><span class="cl"><span class="s"> >++.>+.+++++++..+++.>++.<<+++++++++++++++
</span></span></span><span class="line"><span class="cl"><span class="s"> .>.+++.------.--------.>+.>.
</span></span></span><span class="line"><span class="cl"><span class="s"> "</span> hybrid-brainfuck
</span></span><span class="line"><span class="cl">Hello World!
</span></span></code></pre></div><p>We also gain some potential for building code optimization techniques that
operate on an AST as a step before actual compilation or execution – for
example, coalescing adjacent increment and decrement operations or some
other more complex analysis.</p>
<p>That, however, is likely to remain an exercise for the reader!</p>
More Brainf*ck
https://re.factorcode.org/2025/05/more-brainf-ck.html
Fri, 16 May 2025 08:00:00 -0700https://re.factorcode.org/2025/05/more-brainf-ck.html<p>Almost 16 years ago, I wrote about <a href="https://re.factorcode.org/2009/06/brainf-ck.html">implementing the Brainfuck programming
language</a> in <a href="https://factorcode.org">Factor</a>. It is a
curious programming language, sometimes considered one of the most famous
<a href="https://esolangs.org/wiki/Esoteric_programming_language">esoteric programming
languages</a>.</p>
<p>In any event – and encouraged by a question I was asked recently – I spent
some time thinking about the current process of “compiling” the Brainfuck into
<a href="https://docs.factorcode.org/content/article-quotations.html">quotations</a>
versus how an
<a href="https://en.wikipedia.org/wiki/Interpreter_(computing)">interpreter</a> might work
instead.</p>
<p>As a quick reminder, our <a href="https://github.com/factor/factor/blob/master/extra/brainfuck/brainfuck.factor">current
implementation</a>
expands a program written in <a href="https://esolangs.org/wiki/Brainfuck">Brainfuck</a>
into an equivalent form in Factor, and then allows it to be run:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">brainfuck</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"
</span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[>+++++++>++++++++++>+++>+<<<<-]
</span></span></span><span class="line"><span class="cl"><span class="s"> >++.>+.+++++++..+++.>++.<<+++++++++++++++
</span></span></span><span class="line"><span class="cl"><span class="s"> .>.+++.------.--------.>+.>.
</span></span></span><span class="line"><span class="cl"><span class="s"> "</span> run-brainfuck
</span></span><span class="line"><span class="cl">Hello World!
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [
</span></span><span class="line"><span class="cl"> <span class="s">"
</span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[>+++++++>++++++++++>+++>+<<<<-]
</span></span></span><span class="line"><span class="cl"><span class="s"> >++.>+.+++++++..+++.>++.<<+++++++++++++++
</span></span></span><span class="line"><span class="cl"><span class="s"> .>.+++.------.--------.>+.>.
</span></span></span><span class="line"><span class="cl"><span class="s"> "</span> run-brainfuck
</span></span><span class="line"><span class="cl"> ] expand-macros <span class="m">.
</span></span></span><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl"> <brainfuck> <span class="m">10 </span>(+)
</span></span><span class="line"><span class="cl"> [ get-memory <span class="nb">zero? </span>] [
</span></span><span class="line"><span class="cl"> <span class="m">1 </span>(>) <span class="m">7 </span>(+) <span class="m">1 </span>(>) <span class="m">10 </span>(+) <span class="m">1 </span>(>) <span class="m">3 </span>(+) <span class="m">1 </span>(>) <span class="m">1 </span>(+) <span class="m">4 </span>(<)
</span></span><span class="line"><span class="cl"> <span class="m">1 </span>(-)
</span></span><span class="line"><span class="cl"> ] <span class="nb">until </span><span class="m">1 </span>(>) <span class="m">2 </span>(+) (.) <span class="m">1 </span>(>) <span class="m">1 </span>(+) (.) <span class="m">7 </span>(+) (.) (.) <span class="m">3 </span>(+)
</span></span><span class="line"><span class="cl"> (.) <span class="m">1 </span>(>) <span class="m">2 </span>(+) (.) <span class="m">2 </span>(<) <span class="m">15 </span>(+) (.) <span class="m">1 </span>(>) (.) <span class="m">3 </span>(+)
</span></span><span class="line"><span class="cl"> (.) <span class="m">6 </span>(-) (.) <span class="m">8 </span>(-) (.) <span class="m">1 </span>(>) <span class="m">1 </span>(+) (.) <span class="m">1 </span>(>) (.) <span class="nb">drop flush
</span></span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><p>Notice the <em>coalescing</em> that it performs to collapse multiple identical
operators into a single action.</p>
<p>But, this got me curious about a couple of things:</p>
<ol>
<li>How could we cleanly write a Factor
<a href="https://docs.factorcode.org/content/article-words.html">word</a> that is
implemented in Brainfuck?</li>
<li>How could we write a Factor <em>interpreter</em> for Brainfuck, and what
benefits would it have?</li>
</ol>
<h3 id="building-a-syntax-word">Building a syntax word</h3>
<p>Thankfully, the answer to the first question is simple using <a href="https://docs.factorcode.org/content/article-parsing-words.html">parsing
words</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYNTAX: </span>BRAINFUCK:
</span></span><span class="line"><span class="cl"> scan-new-word <span class="s">";"</span> parse-tokens <span class="nb">concat
</span></span></span><span class="line"><span class="cl"> '[ _ run-brainfuck ] <span class="nf">( -- ) </span>define-declared <span class="k">;
</span></span></span></code></pre></div><p>Now, we can define a <a href="https://en.wikipedia.org/wiki/%22Hello,_World!%22_program">Hello,
World</a>, complete
with inline comments:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">BRAINFUCK: hello
</span></span><span class="line"><span class="cl"> +++++ +++ <span class="c">! Set Cell #0 to 8</span>
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> >++++ <span class="c">! Add 4 to Cell #1; this will always set Cell #1 to 4</span>
</span></span><span class="line"><span class="cl"> [ <span class="c">! as the cell will be cleared by the loop</span>
</span></span><span class="line"><span class="cl"> >++ <span class="c">! Add 4*2 to Cell #2</span>
</span></span><span class="line"><span class="cl"> >+++ <span class="c">! Add 4*3 to Cell #3</span>
</span></span><span class="line"><span class="cl"> >+++ <span class="c">! Add 4*3 to Cell #4</span>
</span></span><span class="line"><span class="cl"> >+ <span class="c">! Add 4 to Cell #5</span>
</span></span><span class="line"><span class="cl"> <<<<- <span class="c">! Decrement the loop counter in Cell #1</span>
</span></span><span class="line"><span class="cl"> ] <span class="c">! Loop till Cell #1 is zero</span>
</span></span><span class="line"><span class="cl"> >+ <span class="c">! Add 1 to Cell #2</span>
</span></span><span class="line"><span class="cl"> >+ <span class="c">! Add 1 to Cell #3</span>
</span></span><span class="line"><span class="cl"> >- <span class="c">! Subtract 1 from Cell #4</span>
</span></span><span class="line"><span class="cl"> >>+ <span class="c">! Add 1 to Cell #6</span>
</span></span><span class="line"><span class="cl"> [<] <span class="c">! Move back to the first zero cell you find; this will</span>
</span></span><span class="line"><span class="cl"> <span class="c">! be Cell #1 which was cleared by the previous loop</span>
</span></span><span class="line"><span class="cl"> <- <span class="c">! Decrement the loop Counter in Cell #0</span>
</span></span><span class="line"><span class="cl"> ] <span class="c">! Loop till Cell #0 is zero</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! The result of this is:</span>
</span></span><span class="line"><span class="cl"> <span class="c">! Cell No : 0 1 2 3 4 5 6</span>
</span></span><span class="line"><span class="cl"> <span class="c">! Contents: 0 0 72 104 88 32 8</span>
</span></span><span class="line"><span class="cl"> <span class="c">! Pointer : ^</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> >>. <span class="c">! Cell #2 has value 72 which is 'H'</span>
</span></span><span class="line"><span class="cl"> >---. <span class="c">! Subtract 3 from Cell #3 to get 101 which is 'e'</span>
</span></span><span class="line"><span class="cl"> +++++ ++..+++. <span class="c">! Likewise for 'llo' from Cell #3</span>
</span></span><span class="line"><span class="cl"> >>. <span class="c">! Cell #5 is 32 for the space</span>
</span></span><span class="line"><span class="cl"> <-. <span class="c">! Subtract 1 from Cell #4 for 87 to give a 'W'</span>
</span></span><span class="line"><span class="cl"> <. <span class="c">! Cell #3 was set to 'o' from the end of 'Hello'</span>
</span></span><span class="line"><span class="cl"> +++.----- -.----- ---. <span class="c">! Cell #3 for 'rl' and 'd'</span>
</span></span><span class="line"><span class="cl"> >>+. <span class="c">! Add 1 to Cell #5 gives us an exclamation point</span>
</span></span><span class="line"><span class="cl"> >++. <span class="c">! And finally a newline from Cell #6</span>
</span></span><span class="line"><span class="cl"> <span class="k">;
</span></span></span></code></pre></div><p>And, it works!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> hello
</span></span><span class="line"><span class="cl">Hello World!
</span></span></code></pre></div><p><em>Note: we are using <code>!</code> as a comment character which is the convention in
Factor. Some Brainfuck implementations use that character to indicate embedded
program inputs.</em></p>
<p>That’s pretty cool, and a neat example of using <a href="https://docs.factorcode.org/content/article-parser-lexer.html">the
lexer</a>, <a href="https://docs.factorcode.org/content/article-parser.html">the
parser</a>, and
<a href="https://docs.factorcode.org/content/article-macros.html">macros</a>.</p>
<h3 id="building-an-interpreter">Building an interpreter</h3>
<p>The answer to the second question might be more complex and nuanced, but
thankfully we can re-use some of the current implementation to make a
<em>quick-and-dirty</em> interpreter:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">end-loop</span> <span class="nf">( </span><span class="nv">str</span> <span class="nv">i</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nv">j/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="sc">CHAR: ] </span><span class="nb">swap pick index-from dup </span>[ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">when </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">start-loop</span> <span class="nf">( </span><span class="nv">str</span> <span class="nv">i</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nv">j/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="nb">- </span><span class="sc">CHAR: [ </span><span class="nb">swap pick last-index-from dup </span>[ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">when </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">interpret-brainfuck-from</span> <span class="nf">( </span><span class="nv">str</span> <span class="nv">i</span> <span class="nv">brainfuck</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nv">next/f</span> <span class="nv">brainfuck</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">2over swap ?nth </span>[ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">2dip </span>{
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: > </span>[ <span class="m">1 </span>(>) ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: < </span>[ <span class="m">1 </span>(<) ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: + </span>[ <span class="m">1 </span>(+) ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: - </span>[ <span class="m">1 </span>(-) ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: . </span>[ (.) ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: , </span>[ (,) ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: # </span>[ (#) ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: [ </span>[ get-memory <span class="nb">zero? </span>[ [ end-loop ] <span class="nb">dip </span>] <span class="nb">when </span>] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: ] </span>[ get-memory <span class="nb">zero? </span>[ [ start-loop ] <span class="nb">dip </span>] <span class="nb">unless </span>] }
</span></span><span class="line"><span class="cl"> { <span class="no">f </span>[ [ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">dip </span>] }
</span></span><span class="line"><span class="cl"> [ blank? [ <span class="s">"Invalid input"</span> <span class="nb">throw </span>] <span class="nb">unless </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">interpret-brainfuck</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span><brainfuck> [ interpret-brainfuck-from <span class="nb">over </span>] <span class="nb">loop 3drop </span><span class="k">;
</span></span></span></code></pre></div><p>And give it a try:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"
</span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[>+++++++>++++++++++>+++>+<<<<-]
</span></span></span><span class="line"><span class="cl"><span class="s"> >++.>+.+++++++..+++.>++.<<+++++++++++++++
</span></span></span><span class="line"><span class="cl"><span class="s"> .>.+++.------.--------.>+.>.
</span></span></span><span class="line"><span class="cl"><span class="s"> "</span> interpret-brainfuck
</span></span><span class="line"><span class="cl">Hello, World!
</span></span></code></pre></div><p>It works! But now I’m curious about relative performance. Let’s build a silly
benchmark equivalent to <a href="https://en.wikipedia.org/wiki/Cat_(Unix)">cat</a> to
redirect the contents of an input-stream to an output-stream. We can compare
our compiled
<a href="https://docs.factorcode.org/content/word-run-brainfuck,brainfuck.html">run-brainfuck</a>
macro to a version that uses the interpreter we made above and then to a native
version implemented using
<a href="https://docs.factorcode.org/content/word-stream-copy,io.html">stream-copy</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">cat1</span> <span class="nf">( -- ) </span><span class="s">",[.,]"</span> run-brainfuck <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">cat2</span> <span class="nf">( -- ) </span><span class="s">",[.,]"</span> interpret-brainfuck <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">cat3</span> <span class="nf">( -- ) </span><span class="nb">input-stream get output-stream get stream-copy* </span><span class="k">;
</span></span></span></code></pre></div><p>First, we should make sure they both work:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Compiled"</span> [ cat1 ] with-string-reader
</span></span><span class="line"><span class="cl">Compiled
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Interpreted"</span> [ cat2 ] with-string-reader
</span></span><span class="line"><span class="cl">Interpreted
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Factor"</span> [ cat3 ] with-string-reader
</span></span><span class="line"><span class="cl">Factor
</span></span></code></pre></div><p>Okay, so it seems to work!</p>
<p>For quick performance testing, lets compare them outputting to a <a href="https://docs.factorcode.org/content/article-io.streams.null.html">null
stream</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [
</span></span><span class="line"><span class="cl"> <span class="m">1,000,000 </span><span class="sc">CHAR: a </span><span class="nb"><string> </span>[
</span></span><span class="line"><span class="cl"> [ cat1 ] with-null-writer
</span></span><span class="line"><span class="cl"> ] with-string-reader
</span></span><span class="line"><span class="cl"> ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.059820291 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [
</span></span><span class="line"><span class="cl"> <span class="m">1,000,000 </span><span class="sc">CHAR: a </span><span class="nb"><string> </span>[
</span></span><span class="line"><span class="cl"> [ cat2 ] with-null-writer
</span></span><span class="line"><span class="cl"> ] with-string-reader
</span></span><span class="line"><span class="cl"> ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.13840325 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [
</span></span><span class="line"><span class="cl"> <span class="m">1,000,000 </span><span class="sc">CHAR: a </span><span class="nb"><string> </span>[
</span></span><span class="line"><span class="cl"> [ cat3 ] with-null-writer
</span></span><span class="line"><span class="cl"> ] with-string-reader
</span></span><span class="line"><span class="cl"> ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.015008417 </span>seconds
</span></span></code></pre></div><p>The <em>compiled</em> one is a bit more than <code>2x</code> faster than the <em>interpreted</em>
version, but both are slower than the <em>native</em> version.</p>
<p>Let’s try comparing our “Hello, World” example – where the <em>operator coalescing</em>
that the compiled version does might help:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">hello1</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"
</span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[>+++++++>++++++++++>+++>+<<<<-]
</span></span></span><span class="line"><span class="cl"><span class="s"> >++.>+.+++++++..+++.>++.<<+++++++++++++++
</span></span></span><span class="line"><span class="cl"><span class="s"> .>.+++.------.--------.>+.>.
</span></span></span><span class="line"><span class="cl"><span class="s"> "</span> run-brainfuck <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">hello2</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"
</span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[>+++++++>++++++++++>+++>+<<<<-]
</span></span></span><span class="line"><span class="cl"><span class="s"> >++.>+.+++++++..+++.>++.<<+++++++++++++++
</span></span></span><span class="line"><span class="cl"><span class="s"> .>.+++.------.--------.>+.>.
</span></span></span><span class="line"><span class="cl"><span class="s"> "</span> interpret-brainfuck <span class="k">;
</span></span></span></code></pre></div><p>We can see that the compiled one is now more like <code>7x</code> faster:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ [ <span class="m">10,000 </span>[ hello1 ] <span class="nb">times </span>] with-null-writer ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.018075292 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ [ <span class="m">10,000 </span>[ hello2 ] <span class="nb">times </span>] with-null-writer ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.133718416 </span>seconds
</span></span></code></pre></div><p>Obviously, this is somewhat comparing <a href="https://en.wikipedia.org/wiki/Apples_and_oranges">apples and
oranges</a> because it’s
ignoring <em>compilation time</em> in the comparison, and I didn’t spend any time
on optimizing the interpreted version – for example, stripping blanks or
validating inputs before doing the interpreter loop – but it’s a useful
starting point for understanding tradeoffs.</p>
<p>How long does it currently take to compile?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> gensym [
</span></span><span class="line"><span class="cl"> <span class="s">"
</span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[>+++++++>++++++++++>+++>+<<<<-]
</span></span></span><span class="line"><span class="cl"><span class="s"> >++.>+.+++++++..+++.>++.<<+++++++++++++++
</span></span></span><span class="line"><span class="cl"><span class="s"> .>.+++.------.--------.>+.>.
</span></span></span><span class="line"><span class="cl"><span class="s"> "</span> run-brainfuck
</span></span><span class="line"><span class="cl"> ] <span class="nf">( -- ) </span>define-declared
</span></span><span class="line"><span class="cl"> ] with-compilation-unit
</span></span><span class="line"><span class="cl"> ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.02294075 </span>seconds
</span></span></code></pre></div><p>…a bit more time than it takes to call it 10,000 times. Interesting.</p>
Roman Sort
https://re.factorcode.org/2025/05/roman-sort.html
Mon, 12 May 2025 08:00:00 -0700https://re.factorcode.org/2025/05/roman-sort.html<p><a href="https://factorcode.org">Factor</a> has included support for <a href="https://en.wikipedia.org/wiki/Roman_numerals">Roman
numerals</a> since at least 2008.
Sometimes recent events – such as the election of <a href="https://en.wikipedia.org/wiki/Pope_Leo_XIV">Pope Leo
XIV</a> or the <a href="https://en.wikipedia.org/wiki/Super_Bowl_LIX">Super Bowl
LIX</a> – revive modern interest in
how they work and how to computationally work with them.</p>
<p>There was a blog post a few days ago about <a href="https://www.johndcook.com/blog/2025/05/09/sorting-roman-numerals/">sorting Roman
numerals</a>
which pointed out that sorting them <em>alphabetically</em> worked <em>pretty well</em>.
Given that we have a pretty good <a href="https://docs.factorcode.org/content/article-roman.html">roman
vocabulary</a>, I thought
we could explore different methods of
<a href="https://en.wikipedia.org/wiki/Sorting_algorithm">sorting</a> a sequence of
strings representing Roman numbers.</p>
<p>Let’s first try the <em>mostly-but-not-entirely-correct</em> method suggested in the
blog post:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 </span>[1..b) [ >ROMAN ] <span class="nb">map
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> sort <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"I"</span> <span class="s">"II"</span> <span class="s">"III"</span> <span class="s">"IV"</span> <span class="s">"IX"</span> <span class="s">"V"</span> <span class="s">"VI"</span> <span class="s">"VII"</span> <span class="s">"VIII"</span> }
</span></span></code></pre></div><p>Well, that’s <em>almost</em> correct, but the number <code>IX</code> – the number <code>9</code> –
sorts in the middle, rather than at the end. Could we fix this by using
<a href="https://docs.factorcode.org/content/word-sort-by,sorting.html">sort-by</a> to
convert the string to a number before calling
<a href="https://docs.factorcode.org/content/word-compare,math.order.html">compare</a> to
produce a sorted output?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 </span>[1..b) [ >ROMAN ] <span class="nb">map
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ roman> ] sort-by <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"I"</span> <span class="s">"II"</span> <span class="s">"III"</span> <span class="s">"IV"</span> <span class="s">"V"</span> <span class="s">"VI"</span> <span class="s">"VII"</span> <span class="s">"VIII"</span> <span class="s">"IX"</span> }
</span></span></code></pre></div><p>That fixes it, but how often do we end up calling the conversion function?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 </span>[1..b) [ >ROMAN ] <span class="nb">map
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="k">SYMBOL:</span> <span class="nf">calls</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ calls <span class="nb">inc </span>roman> ] sort-by <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"I"</span> <span class="s">"II"</span> <span class="s">"III"</span> <span class="s">"IV"</span> <span class="s">"V"</span> <span class="s">"VI"</span> <span class="s">"VII"</span> <span class="s">"VIII"</span> <span class="s">"IX"</span> }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> calls <span class="nb">get </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">40
</span></span></span></code></pre></div><p>Wow, <code>40</code> times – that seems like a lot!</p>
<p>Perhaps we could try the <a href="https://en.wikipedia.org/wiki/Schwartzian_transform">Schwartzian
transform</a> – also known
as
<a href="https://docs.python.org/3/howto/sorting.html#decorate-sort-undecorate">decorate-sort-undecorate</a>
– at the expense of using intermediate storage by saving the keys for each
element, then sorting, and then returning only the values:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 </span>[1..b) [ >ROMAN ] <span class="nb">map
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ [ roman> ] <span class="nb">keep </span>] map>alist sort-keys <span class="nb">values </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"I"</span> <span class="s">"II"</span> <span class="s">"III"</span> <span class="s">"IV"</span> <span class="s">"V"</span> <span class="s">"VI"</span> <span class="s">"VII"</span> <span class="s">"VIII"</span> <span class="s">"IX"</span> }
</span></span></code></pre></div><p>That seems like a lot of code to do a simple thing. Instead, we can use the
<a href="https://docs.factorcode.org/content/word-map-sort,sorting.extras.html">map-sort</a>
abstraction which implements the same method:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 </span>[1..b) [ >ROMAN ] <span class="nb">map
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ roman> ] map-sort <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"I"</span> <span class="s">"II"</span> <span class="s">"III"</span> <span class="s">"IV"</span> <span class="s">"V"</span> <span class="s">"VI"</span> <span class="s">"VII"</span> <span class="s">"VIII"</span> <span class="s">"IX"</span> }
</span></span></code></pre></div><p>Does this make much of a difference? Let’s compare each method:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">roman-sort1</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">sorted-seq</span> <span class="nf">) </span>[ roman> ] sort-by <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">roman-sort2</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">sorted-seq</span> <span class="nf">) </span>[ roman> ] map-sort <span class="k">;
</span></span></span></code></pre></div><p>We can time sorting a list of <code>100,000</code> random Roman numbers under <code>1,000</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">100,000 1,000 </span>[1..b] randoms [ >ROMAN ] <span class="nb">map
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ [ roman-sort1 ] time ]
</span></span><span class="line"><span class="cl"> [ [ roman-sort2 ] time ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">4.164076625 </span>seconds
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.154227583 </span>seconds
</span></span></code></pre></div><p>You can see that the <em>decorate-sort-undecorate</em> pattern is quite a bit faster
in this case. This is not always true, but generally depends on how much work
the <em>key function</em> is doing.</p>
Recamán’s Sequence
https://re.factorcode.org/2025/05/recamans-sequence.html
Tue, 06 May 2025 10:00:00 -0700https://re.factorcode.org/2025/05/recamans-sequence.html<p>I love reading <a href="https://www.johndcook.com/blog/">John D. Cook’s blog</a> which
covers various mathematical concepts, code snippets, and other curious
explorations. Today, he celebrated his <a href="https://www.johndcook.com/blog/2025/05/06/5000th-post/">5,000th
post</a> which is a pretty
incredible milestone. In honor of that, let’s implement the <a href="https://www.johndcook.com/blog/2025/05/05/recamans-sequence/">Recamán’s
sequence</a> which
he wrote about yesterday:</p>
<blockquote>
<p>I recently ran into Recamán’s sequence. N. J. A. Sloane, the founder of the
Online Encyclopedia of Integer Sequences calls Recamán’s sequence one of his
favorites. It’s sequence <a href="https://oeis.org/A005132">A005132</a> in the OEIS.</p>
<p>This sequence starts at 0 and the nth number in the sequence is the result of
moving forward or backward n steps from the previous number. You are allowed
to move backward if the result is positive and a number you haven’t already
visited. Otherwise you move forward.</p>
<p>Here’s Python code to generate the first N elements of the sequence.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">recaman</span><span class="p">(</span><span class="n">N</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">a</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="n">N</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">N</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">proposal</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">n</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">proposal</span> <span class="o">></span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">proposal</span> <span class="ow">not</span> <span class="ow">in</span> <span class="nb">set</span><span class="p">(</span><span class="n">a</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">a</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">=</span> <span class="n">proposal</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">a</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">n</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">a</span>
</span></span></code></pre></div><p>For example, <code>recaman(10)</code> returns</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">21</span><span class="p">]</span>
</span></span></code></pre></div></blockquote>
<h3 id="direct-implementation">Direct Implementation</h3>
<p>The code for this <em>subtract if you can, add if you can’t</em> sequence is not
particularly complex, but it is often fun to see what an algorithm looks like
when implemented in a different language. So, we’ll try a direct implementation
in <a href="https://factorcode.org">Factor</a> first and then talk about some variations.</p>
<p>Using <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a>, we can
keep our version very similar to the original:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">recaman</span> <span class="nf">( </span><span class="nv">N</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> N <span class="m">0 </span><span class="nb"><array> </span>:> a
</span></span><span class="line"><span class="cl"> N [1..b) [| n |
</span></span><span class="line"><span class="cl"> n <span class="m">1 </span><span class="nb">- </span>a <span class="nb">nth </span>n <span class="nb">- </span>:> proposal
</span></span><span class="line"><span class="cl"> proposal <span class="m">0 </span><span class="nb">> </span>proposal a <span class="nb">member? not and </span>[
</span></span><span class="line"><span class="cl"> proposal n a <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> n <span class="m">1 </span><span class="nb">- </span>a <span class="nb">nth </span>n <span class="nb">+ </span>n a <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span>a <span class="k">;
</span></span></span></code></pre></div><p>And, show that it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 </span>recaman <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">0 1 3 6 2 7 13 20 12 21 </span>}
</span></span></code></pre></div><h3 id="short-circuit-logic">Short-Circuit Logic</h3>
<p>One difference that is subtle is that Python’s <code>and</code> operation is
<a href="https://en.wikipedia.org/wiki/Short-circuit_evaluation">short-circuiting</a> so
that if the first expression returns <code>False</code>, the second expression is not
evaluated. We can make that change ourselves:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"><span class="gd">- proposal 0 > proposal a member? not and [
</span></span></span><span class="line"><span class="cl"><span class="gi">+ proposal 0 > [ proposal a member? not ] [ f ] if [
</span></span></span></code></pre></div><p>Or use <a href="https://docs.factorcode.org/content/article-combinators.short-circuit.html">short-circuit
combinators</a>
to do that for us:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"><span class="gd">- proposal 0 > proposal a member? not and [
</span></span></span><span class="line"><span class="cl"><span class="gi">+ proposal { [ 0 > ] [ a member? not ] } 1&& [
</span></span></span></code></pre></div><h3 id="reduce-repetitions">Reduce Repetitions</h3>
<p>We could also notice repetitions that occur, for example both branches of the
<code>if</code> set the <em>nth</em> value of the array. In addition, we could keep the
proposal on the stack instead of naming it as a local variable, and then just
replace it when the branch evaluates to <code>f</code>.</p>
<p>Incorporating these changes results in this version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">recaman</span> <span class="nf">( </span><span class="nv">N</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> N <span class="m">0 </span><span class="nb"><array> </span>:> a
</span></span><span class="line"><span class="cl"> N [1..b) [| n |
</span></span><span class="line"><span class="cl"> n <span class="m">1 </span><span class="nb">- </span>a <span class="nb">nth </span>n <span class="nb">-
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>{ [ <span class="m">0 </span><span class="nb">> </span>] [ a <span class="nb">member? not </span>] } 1&& [
</span></span><span class="line"><span class="cl"> <span class="nb">drop </span>n <span class="m">1 </span><span class="nb">- </span>a <span class="nb">nth </span>n <span class="nb">+
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">unless </span>n a <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span>a <span class="k">;
</span></span></span></code></pre></div><p>There is still repetition as each iteration looks up the previous value twice.
Instead, we could fix that by storing that value as a local variable:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">recaman</span> <span class="nf">( </span><span class="nv">N</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> N <span class="m">0 </span><span class="nb"><array> </span>:> a
</span></span><span class="line"><span class="cl"> N [1..b) [| n |
</span></span><span class="line"><span class="cl"> n <span class="m">1 </span><span class="nb">- </span>a <span class="nb">nth </span>:> prev
</span></span><span class="line"><span class="cl"> prev n <span class="nb">- dup </span>{ [ <span class="m">0 </span><span class="nb">> </span>] [ a <span class="nb">member? not </span>] } 1&&
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>prev n <span class="nb">+ </span>] <span class="nb">unless </span>n a <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span>a <span class="k">;
</span></span></span></code></pre></div><p>Sometimes it is simpler to extract an inner loop and then see how it looks:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">next-recaman</span> <span class="nf">( </span><span class="nv">prev</span> <span class="nv">n</span> <span class="nv">a</span> <span class="nf">-- </span><span class="nv">next</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> prev n <span class="nb">- dup </span>{ [ <span class="m">0 </span><span class="nb">> </span>] [ a <span class="nb">member? not </span>] } 1&&
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>prev n <span class="nb">+ </span>] <span class="nb">unless </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">recaman</span> <span class="nf">( </span><span class="nv">N</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> N <span class="m">0 </span><span class="nb"><array> </span>:> a
</span></span><span class="line"><span class="cl"> N [1..b) [
</span></span><span class="line"><span class="cl"> [ <span class="m">1 </span><span class="nb">- </span>a <span class="nb">nth </span>] [ a next-recaman ] [ a <span class="nb">set-nth </span>] <span class="nb">tri
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span>a <span class="k">;
</span></span></span></code></pre></div><p>The retrieval of the previous value is un-necessary work on each iteration.
We could remove that by keeping the previous value on the stack and use the
<a href="https://docs.factorcode.org/content/article-namespaces-make.html">make
vocabulary</a>
to accumulate values. We also start from index <code>0</code> instead of <code>1</code> as a
simplification:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">next-recaman</span> <span class="nf">( </span><span class="nv">prev</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">next</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> prev n <span class="nb">- dup </span>{ [ <span class="m">0 </span><span class="nb">> </span>] [ building <span class="nb">get member? not </span>] } 1&&
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>prev n <span class="nb">+ </span>] <span class="nb">unless </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">recaman</span> <span class="nf">( </span><span class="nv">N</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">0 </span>N <iota> [ next-recaman <span class="nb">dup </span>, ] <span class="nb">each drop </span>] { } make <span class="k">;
</span></span></span></code></pre></div><h3 id="constant-time-lookups">Constant-time Lookups</h3>
<p>But, there is a big performance issue. Searching the previous values to see if
it had been generated before uses a linear-time algorithm. It would be much
faster to use a
<a href="https://docs.factorcode.org/content/article-bit-vectors.html">bit-vector</a> to
track the previously seen numbers and a constant-time lookup:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">next-recaman</span> <span class="nf">( </span><span class="nv">prev</span> <span class="nv">n</span> <span class="nv">seen</span> <span class="nf">-- </span><span class="nv">next</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> prev n <span class="nb">- dup </span>{ [ <span class="m">0 </span><span class="nb">> </span>] [ seen <span class="nb">nth not </span>] } 1&&
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>prev n <span class="nb">+ </span>] <span class="nb">unless </span><span class="no">t </span><span class="nb">over </span>seen <span class="nb">set-nth </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">recaman</span> <span class="nf">( </span><span class="nv">N</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>N <span class="nb">dup </span><bit-vector> '[ _ next-recaman <span class="nb">dup </span>] <span class="nb">map-integers nip </span><span class="k">;
</span></span></span></code></pre></div><p>This allows us to generate, for example, one million values in the sequence in
a small amount of time:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">1,000,000 </span>recaman <span class="nb">last </span><span class="m">. </span>] time
</span></span><span class="line"><span class="cl"><span class="m">1057164
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.049441708 </span>seconds
</span></span></code></pre></div><p>Or even calculate the <em>nth</em> value without saving the sequence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">nth-recaman</span> <span class="nf">( </span><span class="nv">N</span> <span class="nf">-- </span><span class="nv">elt</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>N <span class="nb">dup </span><bit-vector> '[ _ next-recaman ] <span class="nb">each-integer </span><span class="k">;
</span></span></span></code></pre></div><h3 id="using-generators">Using Generators</h3>
<p>We could even use the <a href="https://docs.factorcode.org/content/article-generators.html">generators
vocabulary</a> to
make an infinite stream that we can sample from:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">next-recaman</span> <span class="nf">( </span><span class="nv">prev</span> <span class="nv">n</span> <span class="nv">seen</span> <span class="nf">-- </span><span class="nv">next</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> prev n <span class="nb">- dup </span>{ [ <span class="m">0 </span><span class="nb">> </span>] [ seen <span class="nb">nth not </span>] } 1&&
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>prev n <span class="nb">+ </span>] <span class="nb">unless </span><span class="no">t </span><span class="nb">over </span>seen <span class="nb">set-nth </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">GEN: recaman <span class="nf">( -- </span><span class="nv">gen</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 0 </span>?V{ } <span class="nb">clone </span>'[
</span></span><span class="line"><span class="cl"> [ _ next-recaman <span class="nb">dup </span>yield ] [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">bi </span><span class="no">t
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop 2drop </span><span class="k">;
</span></span></span></code></pre></div><p>And then take some values from it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> recaman <span class="m">10 </span>take <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">0 1 3 6 2 7 13 20 12 21 </span>}
</span></span></code></pre></div><p>It is often pleasantly surprising to explore seemingly simple topics – for
example, looking at this algorithm and thinking about different <a href="https://en.wikipedia.org/wiki/Time_complexity">time
complexity</a>, <a href="https://en.wikipedia.org/wiki/Data_structure">data
structures</a>, or <a href="https://en.wikipedia.org/wiki/Library_(computing)">programming
language libraries</a> that
might be useful.</p>
<p>I have <a href="https://github.com/factor/factor/commit/7ff93f0bf15df743cd9f37a0a29549a54b24e834">added this
today</a>
to the <a href="https://github.com/factor/factor">Factor development version</a>.</p>
Filtering Errors
https://re.factorcode.org/2025/05/filtering-errors.html
Mon, 05 May 2025 08:00:00 -0700https://re.factorcode.org/2025/05/filtering-errors.html<p>There was a discussion today on the <a href="https://discord.gg/QxJYZx3QDf">Factor Discord
server</a> about how one might
<a href="https://docs.factorcode.org/content/word-filter,sequences.html">filter</a> or
<a href="https://docs.factorcode.org/content/word-reject,sequences.html">reject</a>
objects in a sequence based on whether a
<a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a>
throws an error or not when applied to each item. We’re going to implement
that now in <a href="https://factorcode.org">Factor</a> and hopefully learn a few
things.</p>
<p>First, we need a way to see if a quotation <a href="https://docs.factorcode.org/content/word-throw,kernel.html">throws an
error</a> or not.
One way would be to <a href="https://docs.factorcode.org/content/word-call,kernel.html">call the
quotation</a> and
use
<a href="https://docs.factorcode.org/content/word-recover,continuations.html">recover</a>
to return a different boolean if an error was thrown. That might look
something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">throws?</span> <span class="nf">( </span><span class="nv">quot</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ @ <span class="no">f </span>] [ <span class="nb">drop </span><span class="no">t </span>] <span class="nb">recover </span><span class="k">; inline
</span></span></span></code></pre></div><p>And that kinda works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 20 </span>[ <span class="nb">+ </span>] throws?
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- Data stack:
</span></span><span class="line"><span class="cl"><span class="m">30
</span></span></span><span class="line"><span class="cl"><span class="no">f
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 </span><span class="s">"20"</span> [ <span class="nb">+ </span>] throws?
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- Data stack:
</span></span><span class="line"><span class="cl"><span class="m">10
</span></span></span><span class="line"><span class="cl"><span class="s">"20"</span>
</span></span><span class="line"><span class="cl"><span class="no">t
</span></span></span></code></pre></div><p>But you can see that in the <em>working</em> first case, it did not throw an error
and the stack has the single output of the quotation and <code>f</code>, but in the
<em>failed</em> second case it has the two inputs to the quotation and a <code>t</code>
indicating that an error was thrown.</p>
<p>So, ideally we could modify this so that when the quotation succeeds, we drop
the outputs, and when the quotation fails, we drop the inputs, and then can
consistently produce a single boolean output for any quotation this applies to.</p>
<p>Luckily for us, we can use the
<a href="https://docs.factorcode.org/content/word-drop-outputs,combinators.smart.html">drop-outputs</a>
and
<a href="https://docs.factorcode.org/content/word-drop-inputs,combinators.smart.html">drop-inputs</a>
words which infer the <a href="https://docs.factorcode.org/content/article-effects.html">stack
effect</a> and
handles each case correctly. This would change our implementation slightly:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">throws?</span> <span class="nf">( </span><span class="nv">quot</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ '[ _ drop-outputs <span class="no">f </span>] ]
</span></span><span class="line"><span class="cl"> [ '[ <span class="nb">drop </span>_ drop-inputs <span class="no">t </span>] ] <span class="nb">bi recover </span><span class="k">; inline
</span></span></span></code></pre></div><p>And now we can see that it handles both cases nicely:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 20 </span>[ <span class="nb">+ </span>] throws?
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- Data stack:
</span></span><span class="line"><span class="cl"><span class="no">f
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 </span><span class="s">"20"</span> [ <span class="nb">+ </span>] throws?
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- Data stack:
</span></span><span class="line"><span class="cl"><span class="no">t
</span></span></span></code></pre></div><p>Now, we can combine that with our <a href="https://docs.factorcode.org/content/article-sequences.html">sequence
operations</a> and
make these useful words:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">filter-errors</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">seq</span> <span class="nv">quot</span> <span class="nf">-- </span><span class="nv">...</span> <span class="nv">subseq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ _ throws? ] <span class="nb">filter </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">reject-errors</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">seq</span> <span class="nv">quot</span> <span class="nf">-- </span><span class="nv">...</span> <span class="nv">subseq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ _ throws? ] reject <span class="k">; inline
</span></span></span></code></pre></div><p>And then see how they work:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="no">t </span><span class="s">"5"</span> <span class="m">123 </span>} [ string>number ] filter-errors <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="no">t </span><span class="m">123 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="no">t </span><span class="s">"5"</span> <span class="m">123 </span>} [ string>number ] reject-errors <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"5"</span> }
</span></span></code></pre></div><p>These words were
<a href="https://github.com/factor/factor/commit/5aa4575407395311f0fc41200c9e1ebce7bf6327">added recently</a>
to the <a href="https://github.com/factor/factor">development version of Factor</a>.</p>
Human Sorting Improved
https://re.factorcode.org/2025/03/human-sorting-improved.html
Sun, 30 Mar 2025 08:00:00 -0700https://re.factorcode.org/2025/03/human-sorting-improved.html<p><a href="https://factorcode.org">Factor</a> has had <a href="https://docs.factorcode.org/content/article-sorting.human.html">human-friendly
sorting</a>
since <a href="https://github.com/factor/factor/commit/d2163097f0aaddf0e1b922f974c5b59217b0f71a">December
2007</a>.
It is unclear if it is related, but <a href="https://nedbatchelder.com/">Ned
Batchelder</a> wrote about <a href="https://nedbatchelder.com/blog/200712/human_sorting.html">“human
sorting”</a> around
the same time with links to a couple of other blog posts discussing similar
topics, so perhaps it was in the
<a href="https://www.merriam-webster.com/dictionary/zeitgeist">zeitgeist</a> of the
time.</p>
<p>In any event, Ned recently wrote about <a href="https://nedbatchelder.com/blog/202503/human_sorting_improved.html">“human sorting
improved”</a>
which deals with the topic of how to sort two strings that are
<em>human-equivalent</em> but are different and should probably have an ordering.
This was the result of fixing a <a href="https://github.com/nedbat/coveragepy/issues/1709">problem that actually
happened</a> in the
<a href="https://github.com/nedbat/coveragepy">coverage.py</a> project.</p>
<p>For example, comparing <code>"x1y"</code> and <code>"x001y"</code> using the original
algorithm would consider these to be equal given the same <em>human</em> keys: <code>{ "x" 1 "y" }</code>.</p>
<p>You can see this in <a href="https://re.factorcode.org/2024/09/factor-0-100-now-available.html">Factor
0.100</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">sorting.human</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"x1y"</span> <span class="s">"x001y"</span> human<=> <span class="m">.
</span></span></span><span class="line"><span class="cl">+eq+
</span></span></code></pre></div><p>Ned suggests that instead – if two strings are equal using the human
sorting method – they should be compared for <a href="https://en.wikipedia.org/wiki/Lexicographic_order">lexicographic
ordering</a> as a
tie-breaker.</p>
<p>I have <a href="https://github.com/factor/factor/commit/0b0a7551b8f7181adb7d71b6d1355e9139a7fd3a">made that
change</a>
in the latest <a href="https://github.com/factor/factor">development version of
Factor</a> so that the behavior is changed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"x1y"</span> <span class="s">"x001y"</span> human<=> <span class="m">.
</span></span></span><span class="line"><span class="cl">+gt+
</span></span></code></pre></div>Sports Betting
https://re.factorcode.org/2025/03/sports-betting.html
Wed, 26 Mar 2025 08:00:00 -0700https://re.factorcode.org/2025/03/sports-betting.html<p>I have lately been curious about the <a href="https://ziglang.org">Zig Programming
Language</a> and how it might be useful to
<a href="https://factorcode.org">Factor</a>. In the process of doing some research, I
bumped into a blog post about <a href="https://vinybrasil.github.io/blog/zigodd/">Converting decimal odds to probabilities with
Zig</a>, which shares some <a href="https://github.com/vinybrasil/zigodd">Zig source
code</a> for calculating <a href="https://en.wikipedia.org/wiki/Sports_betting">sports
betting</a> odds:</p>
<blockquote>
<p>As sports betting expands across the world, more and more players don’t
understand tha math behind it. So, the purpose of this article it’s to
clarify how to convert from decimal odds to the probability behind them. The
calculations are implemented in Zig, a system programming language perfect
to create CLI tools because it’s fast and simple.</p>
</blockquote>
<p>It is particularly interesting given the growth and popularity of both sports
betting on platforms like <a href="https://sportsbook.draftkings.com">DraftKings</a>, but
also prediction markets like <a href="https://polymarket.com">Polymarket</a> and
<a href="https://manifold.markets">Manifold</a>. You can read about some background
information in <a href="https://www.investopedia.com/articles/investing/042115/betting-basics-fractional-decimal-american-moneyline-odds.asp">Sports Betting Odds: How They Work and How To Read
Them</a>.</p>
<p><em>Note: I am not encouraging gambling, and in addition you should do your own
research on the various platforms as there can be
<a href="https://cointelegraph.com/news/polymarket-trump-ukraine-bet-whale-governance-attack">controversy</a>
about potential manipulation and transparency of the markets.</em></p>
<p>I don’t know much about (and do not do any) sports betting, but I thought
it would be fun to learn about and then to translate some of these concepts to
<a href="https://factorcode.org">Factor</a>.</p>
<h3 id="decimal">Decimal</h3>
<p>The “decimal odds” are popular in Europe and are the total returns including
each original $1 bet if you win. These “decimal odds” effectively represent
inverse probabilities. That is, if the odds are <code>4.5</code>, then the
probability is about 22% (which is <code>1/4.5</code>).</p>
<p><em>Note: a variant of this is “fractional odds” popular in Britain which might
instead quote those odds as <code>7/2</code> or <code>7-2</code> and describe the amount won
($7) in addition to the original bet ($2).</em></p>
<p>We can convert decimal odds to probabilities:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">odds>probs</span> <span class="nf">( </span><span class="nv">m</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">) </span><span class="nb">recip </span><span class="m">100 </span><span class="nb">* </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">probs>odds</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">m</span> <span class="nf">) </span><span class="m">100 </span><span class="nb">/ recip </span><span class="k">;
</span></span></span></code></pre></div><p>Sometimes the odds are quoted based on different outcomes – for example, in
many team games there are three outcomes: win, lose, or tie. These
probabilities should sum to 1, and seem to be often quoted with the <em>payout</em>
included (typically less than 100%). The example given in the original blogpost
is this:</p>
<p>
<img src="https://re.factorcode.org/images/2025-03-26-sports-betting.png" alt="" width="439" height="333" />
</p>
<p>We can calculate the payout for a group of <em>odds</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">compute-payout</span> <span class="nf">( </span><span class="nv">odds</span> <span class="nf">-- </span><span class="nv">k</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">recip </span>] <span class="nb">map-sum recip </span><span class="k">;
</span></span></span></code></pre></div><p>And given the example from the blogpost, the payout is about 95%:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1.27 6.00 10.25 </span>} compute-payout <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">0.9509054938365546
</span></span></span></code></pre></div><p>Which means, you could convert odds to probabilities using this payout number:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">compute-probs</span> <span class="nf">( </span><span class="nv">odds</span> <span class="nf">-- </span><span class="nv">probs</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>compute-payout '[ odds>probs _ <span class="nb">* </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>This shows the different probabilities of the outcomes, using
<a href="https://docs.factorcode.org/content/word-printf,formatting.html">printf</a> to
format the sequence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1.27 6.0 10.25 </span>} compute-probs <span class="s">"%[%.2f, %]"</span> printf
</span></span><span class="line"><span class="cl">{ 74.87, 15.85, <span class="m">9.28 </span>}
</span></span></code></pre></div><h3 id="moneyline">Moneyline</h3>
<p>The “moneyline odds” are more popular in the United States. The original
blogpost that I mention above did not go into details, but I was curious, so we
will explore how moneyline payouts work. These are quoted as positive
(<em>underdogs</em>) or negative (<em>favorites</em>) values, and are the amount you would
need to bet to receive $100 of winnings.</p>
<p>Using the <a href="https://www.nfl.com">NFL</a> example from the <a href="https://www.investopedia.com/articles/investing/042115/betting-basics-fractional-decimal-american-moneyline-odds.asp">Investopedia
article</a>.</p>
<blockquote>
<p>Let’s say a betting website (also known as an online sportsbook) priced an
NFL game between the Pittsburgh Steelers and the Kansas City Chiefs with the
following moneyline odds.</p>
<p>Steelers: +585</p>
<p>Chiefs: -760</p>
</blockquote>
<p>We can convert moneyline odds to decimal odds:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">moneyline>odds</span> <span class="nf">( </span><span class="nv">moneyline</span> <span class="nf">-- </span><span class="nv">odds</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">100 </span><span class="nb">/ dup </span><span class="m">1 </span><span class="nb">< </span>[ <span class="nb">recip neg </span>] <span class="nb">when </span><span class="m">1 </span><span class="nb">+ </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">odds>moneyline</span> <span class="nf">( </span><span class="nv">odds</span> <span class="nf">-- </span><span class="nv">moneyline</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="nb">- dup </span><span class="m">1 </span><span class="nb">< </span>[ <span class="nb">recip neg </span>] <span class="nb">when </span><span class="m">100 </span><span class="nb">* </span><span class="k">;
</span></span></span></code></pre></div><p>And use that to calculate the implied relative probabilities of that game’s
winner:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">585 -760 </span>} [ moneyline>odds ] <span class="nb">map
</span></span></span><span class="line"><span class="cl"> compute-probs <span class="s">"%[%.2f, %]"</span> printf
</span></span><span class="line"><span class="cl">{ 14.18, <span class="m">85.82 </span>}
</span></span></code></pre></div><h3 id="parlay">Parlay</h3>
<p>There are also “parlay odds” which are multiple bets linked together as one.</p>
<blockquote>
<p>Let’s say you want to bet three heavy favorites on the moneyline because
you’re confident each team will win, but not sure if they’ll cover the
spread.</p>
<p>So you bet Packers -300 against the Lions, Patriots -200 vs. the Jets, and
Eagles -150 at the Washington Commanders.</p>
</blockquote>
<p>We can convert these to odds:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">-300 -200 -150 </span>} [ moneyline>odds ] <span class="nb">map </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">1+1/3 1+1/2 1+2/3 </span>}
</span></span></code></pre></div><p>And write a word to convert it to a parlay outcome:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">compute-parlay</span> <span class="nf">( </span><span class="nv">moneyline</span> <span class="nf">-- </span><span class="nv">parlay</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ moneyline>odds ] <span class="nb">map product </span>odds>moneyline <span class="k">;
</span></span></span></code></pre></div><p>So, this example would have a parlay payout of <code>233</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">-300 -200 -150 </span>} compute-parlay <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">233+1/3
</span></span></span></code></pre></div><p>And a probability of 30%:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">233+1/3 </span>moneyline>odds odds>probs <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">30
</span></span></span></code></pre></div><p>There are probably aspects of this that I did not cover above, but it’s kinda
fun to explore new topics that you don’t know much about and to learn!</p>
Base256Emoji
https://re.factorcode.org/2025/03/base256emoji.html
Sat, 22 Mar 2025 08:00:00 -0700https://re.factorcode.org/2025/03/base256emoji.html<p>While looking into the
<a href="https://github.com/multiformats/multibase">multibase</a> group of
<em>self-identifying base encodings</em>, I discovered
<a href="https://github.com/multiformats/multibase/blob/master/rfcs/Base256Emoji.md">Base256Emoji</a>
which is an encoding format described by an
<a href="https://en.wikipedia.org/wiki/Emoji">emoji</a> to use for each byte of an
input buffer. This spec is implemented, for example, in both
<a href="https://github.com/multiformats/go-multibase/blob/master/base256emoji.go">Go</a>
and <a href="https://lib.rs/crates/base256emoji">Rust</a></p>
<p>Despite replacing each byte with a typically 3-byte or 4-byte
<a href="https://en.wikipedia.org/wiki/UTF-8">UTF-8</a> sequence – which is unusual
for byte encodings (they often seek to reduce the length of an input
sequence) – there are some nice use cases.</p>
<p>We’re going to implement this in <a href="https://factorcode.org">Factor</a> and then
discuss some variants.</p>
<h2 id="implementation">Implementation</h2>
<p>First, we define a 256 item sequence of emojis, one for each byte:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">base256>emoji</span> <span class="s">"🚀🪐☄🛰🌌🌑🌒🌓🌔🌕🌖🌗🌘🌍🌏🌎🐉☀💻🖥\
</span></span></span><span class="line"><span class="cl"><span class="s">💾💿😂❤😍🤣😊🙏💕😭😘👍😅👏😁🔥🥰💔💖💙😢🤔😆🙄💪😉☺👌🤗💜😔😎😇\
</span></span></span><span class="line"><span class="cl"><span class="s">🌹🤦🎉💞✌✨🤷😱😌🌸🙌😋💗💚😏💛🙂💓🤩😄😀🖤😃💯🙈👇🎶😒🤭❣😜💋\
</span></span></span><span class="line"><span class="cl"><span class="s">👀😪😑💥🙋😞😩😡🤪👊🥳😥🤤👉💃😳✋😚😝😴🌟😬🙃🍀🌷😻😓⭐✅🥺🌈😈\
</span></span></span><span class="line"><span class="cl"><span class="s">🤘💦✔😣🏃💐☹🎊💘😠☝😕🌺🎂🌻😐🖕💝🙊😹🗣💫💀👑🎵🤞😛🔴😤🌼😫⚽🤙\
</span></span></span><span class="line"><span class="cl"><span class="s">☕🏆🤫👈😮🙆🍻🍃🐶💁😲🌿🧡🎁⚡🌞🎈❌✊👋😰🤨😶🤝🚶💰🍓💢🤟🙁🚨💨\
</span></span></span><span class="line"><span class="cl"><span class="s">🤬✈🎀🍺🤓😙💟🌱😖👶🥴▶➡❓💎💸⬇😨🌚🦋😷🕺⚠🙅😟😵👎🤲🤠🤧📌🔵💅🧐\
</span></span></span><span class="line"><span class="cl"><span class="s">🐾🍒😗🤑🌊🤯🐷☎💧😯💆👆🎤🙇🍑❄🌴💣🐸💌📍🥀🤢👅💡💩👐📸👻🤐🤮🎼🥵\
</span></span></span><span class="line"><span class="cl"><span class="s">🚩🍎🍊👼💍📣🥂"</span>
</span></span></code></pre></div><p>And then we compute the reverse mapping:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">emoji>base256</span> $[ base256>emoji H{ } zip-index-as ]
</span></span></code></pre></div><p>With those two data blocks, we can define words to convert into and out of
<em>base256emoji</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>base256emoji</span> <span class="nf">( </span><span class="nv">bytes</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ base256>emoji <span class="nb">nth </span>] <span class="s">""</span> <span class="nb">map-as </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">base256emoji></span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">bytes</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ emoji>base256 <span class="nb">at </span>] B{ } <span class="nb">map-as </span><span class="k">;
</span></span></span></code></pre></div><p>You can try it out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Hello, Factor!"</span> >base256emoji <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"😄✋🍀🍀😓💪😅💓🤤💃😈😓🥺👏"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"😄✋🍀🍀😓💪😅💓🤤💃😈😓🥺👏"</span> base256emoji> <span class="s">""</span> <span class="nb">like </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"Hello, Factor!"</span>
</span></span></code></pre></div><h2 id="use-cases">Use Cases</h2>
<p>The most interesting use case for <em>base256emoji</em> seems to be the <a href="https://eips.ethereum.org/EIPS/eip-7673">ERC-7673:
Distinguishable base256emoji
Addresses</a> proposal for
<a href="https://ethereum.org/en/">Ethereum</a>. This proposal seeks to “<em>address
spoofing attacks [that] have mislead tens of thousands of ether, and
countless other tokens</em>” by using visual emoji-based strings – a similar
justification to the <a href="https://re.factorcode.org/2023/08/drunken-bishop.html">Drunken Bishop</a>
algorithm.</p>
<p>We can try <em>base256emoji</em> out with checksums to yield a similar benefit,
displaying 16 emojis instead of a 32-byte hex-string:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"resource:license.txt"</span> md5 checksum-bytes
</span></span><span class="line"><span class="cl"> bytes>hex-string <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"ebb5ab617e3a88ed43f7d247c6466d95"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"resource:license.txt"</span> md5 checksum-bytes
</span></span><span class="line"><span class="cl"> >base256emoji <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"💌💨🤨🤤😠✨😹🥀😏🎼🤠🤩⬇💓🌷🤙"</span>
</span></span></code></pre></div><h2 id="visual-collisions">Visual Collisions</h2>
<p>Given a desire to use <em>visually dissimilar</em> emojis in identities, it would
be useful to think about the chosen emoji set and how that might be
interpreted in a visual context. Some criticism of this particular group of
emojis, which are sometimes magnified by smaller font sizes, might focus on
the <em>visual similarity</em> of:</p>
<ol>
<li>Similar “globe” emojis (🌍 🌏 🌎)</li>
<li>Similar “face” emojis (🙂 😐 😑 🙁)</li>
<li>Similar “kiss” emojis (😙 😚 😗 😘)</li>
<li>Similar “star” emojis (⭐ 🌟)</li>
<li>Similar “grin” emojis (😀 😃 😄 😁 😆 😅)</li>
<li>Similar “heart” emojis (💔 💗 💕 💖 💘 💙 💜 💚 💛 🖤)</li>
<li>Similar “hand” emojis (🤞 👋 ✋ 👊 ✊ 🤝 🤲)</li>
</ol>
<p>It might be desirable to choose emojis not based on community membership or
common usage, but on their most dissimilar visual identity. to make it even
harder for scammers to deliberately pick lookalike emojis and rely on small
text sizes, platform font differences, or user inattention.</p>
<p>A couple of other emoji sets can be found, for example
<a href="https://github.com/Equim-chan/base256">@Equim-chan/base256</a> (which uses one
of my favorite emojis: 👾),
<a href="https://www.npmjs.com/package/base-emoji">npm/base-emoji</a>, or even the
<a href="https://crates.io/crates/kittenmoji">KittenMoji</a> crate.</p>
<p>Fun!</p>
<p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/base256emoji/base256emoji.factor">GitHub</a>.</p>
RDAP
https://re.factorcode.org/2025/03/rdap.html
Tue, 18 Mar 2025 08:00:00 -0700https://re.factorcode.org/2025/03/rdap.html<p><a href="https://www.icann.org">ICANN</a> recently posted an update on <a href="https://www.icann.org/en/announcements/details/icann-update-launching-rdap-sunsetting-whois-27-01-2025-en">Launching RDAP;
Sunsetting
WHOIS</a>
which got <a href="https://news.ycombinator.com/item?id=43384069">some discussion on Hacker
News</a>. Andy Newton, one of
the creators of RDAP, has published <a href="https://rdap.rcode3.com/">A Guide to the Registration Data Access
Protocol (RDAP)</a>, which is a pretty useful
resource for understanding how it works. More information comes from the
<a href="https://about.rdap.org/">main RDAP website</a> which describes it as:</p>
<blockquote>
<p>The <strong>Registration Data Access Protocol (RDAP)</strong> is the successor to
<a href="https://whois.icann.org/en/about-whois">WHOIS</a>. Like WHOIS, RDAP provides
access to information about Internet resources (<a href="https://en.wikipedia.org/wiki/Domain_name">domain
names</a>, <a href="https://en.wikipedia.org/wiki/Autonomous_system_(Internet)">autonomous system
numbers</a>, and
<a href="https://en.wikipedia.org/wiki/IP_address">IP addresses</a>). Unlike WHOIS,
RDAP provides:</p>
<ul>
<li>A machine-readable representation of registration data;</li>
<li>Differentiated access;</li>
<li>Structured request and response semantics;</li>
<li>Internationalisation;</li>
<li>Extensibility.</li>
</ul>
</blockquote>
<p>The <a href="https://en.wikipedia.org/wiki/WHOIS">WHOIS protocol</a> that it replaces
is super simple, being described by <a href="https://tools.ietf.org/html/rfc3912">RFC
3912</a> in a few paragraphs. And,
in fact, you can test it out on the command-line of most computers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ <span class="nb">echo</span> -e <span class="s2">"factorcode.org\r\n"</span> <span class="p">|</span> nc -i <span class="m">1</span> whois.cloudflare.com <span class="m">43</span>
</span></span><span class="line"><span class="cl">Domain Name: FACTORCODE.ORG
</span></span><span class="line"><span class="cl">Registry Domain ID: c49c93dee3304f39b081383262d320c6-LROR
</span></span><span class="line"><span class="cl">Registrar WHOIS Server: whois.cloudflare.com
</span></span><span class="line"><span class="cl">Registrar URL: https://www.cloudflare.com
</span></span><span class="line"><span class="cl">Updated Date: 2025-01-15T22:46:54Z
</span></span><span class="line"><span class="cl">Creation Date: 2005-12-01T04:54:37Z
</span></span><span class="line"><span class="cl">Registrar Registration Expiration Date: 2025-12-01T04:54:37Z
</span></span><span class="line"><span class="cl">Registrar: Cloudflare, Inc.
</span></span><span class="line"><span class="cl">Registrar IANA ID: <span class="m">1910</span>
</span></span><span class="line"><span class="cl">Domain Status: clienttransferprohibited https://icann.org/epp#clienttransferprohibited
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>The <a href="https://en.wikipedia.org/wiki/Registration_Data_Access_Protocol">RDAP
protocol</a>
must be equally simple, right? Well, not so fast. Instead of a few
paragraphs, and simple queries over sockets, you get many RFCs describing
it:</p>
<ul>
<li><a href="https://tools.ietf.org/html/rfc7480">RFC 7480: HTTP Usage in the Registration Data Access Protocol (RDAP)</a></li>
<li><a href="https://tools.ietf.org/html/rfc7481">RFC 7481: Security Services for the Registration Data Access Protocol (RDAP)</a></li>
<li><a href="https://tools.ietf.org/html/rfc9083">RFC 9083: JSON Responses for the Registration Data Access Protocol (RDAP)</a></li>
<li><a href="https://tools.ietf.org/html/rfc9224">RFC 9224: Finding the Authoritative Registration Data (RDAP) Service</a></li>
<li><a href="https://tools.ietf.org/html/rfc8056">RFC 8056: Registration Data Access Protocol (RDAP) Object Tagging</a></li>
<li><a href="https://tools.ietf.org/html/rfc8977">RFC 8977: Registration Data Access Protocol (RDAP) Query Parameters for Result Sorting and Paging</a></li>
<li><a href="https://tools.ietf.org/html/rfc8982">RFC 8982: Registration Data Access Protocol (RDAP) Partial Response</a></li>
<li><a href="https://tools.ietf.org/html/rfc9082">RFC 9082: Registration Data Access Protocol (RDAP) Query Format</a></li>
<li><a href="https://tools.ietf.org/html/rfc9536">RFC 9536: Registration Data Access Protocol (RDAP) Reverse Search</a></li>
<li><a href="https://tools.ietf.org/html/rfc9537">RFC 9537: Redacted Fields in the Registration Data Access Protocol (RDAP) Response</a></li>
</ul>
<p>These, along with things like the <a href="https://www.iana.org/assignments/rdap-extensions/rdap-extensions.xhtml">RDAP Extension
registry</a>,
and the protocol reliance on HTTP/HTTPS, JSON, and
<a href="https://datatracker.ietf.org/doc/html/rfc9535">JSONPath</a> considerably
increase the complexity of RDAP implementations.</p>
<p>Below we are going to start an implementation using <a href="https://factorcode.org">Factor</a>.</p>
<h2 id="bootstrapping">Bootstrapping</h2>
<p>The first concept we have to implement is <a href="https://rdap.rcode3.com/bootstrapping/iana.html">RDAP
Bootstrapping</a> which uses 5
IANA files to redirect searches to the correct upstream RDAP servers.</p>
<table>
<thead>
<tr>
<th>Type</th>
<th>Link</th>
</tr>
</thead>
<tbody>
<tr>
<td>Forward DNS</td>
<td><a href="https://data.iana.org/rdap/dns.json">https://data.iana.org/rdap/dns.json</a></td>
</tr>
<tr>
<td>IPv4 Addresses</td>
<td><a href="https://data.iana.org/rdap/ipv4.json">https://data.iana.org/rdap/ipv4.json</a></td>
</tr>
<tr>
<td>IPv6 Addresses</td>
<td><a href="https://data.iana.org/rdap/ipv6.json">https://data.iana.org/rdap/ipv6.json</a></td>
</tr>
<tr>
<td>Autonomous System Numbers</td>
<td><a href="https://data.iana.org/rdap/asn.json">https://data.iana.org/rdap/asn.json</a></td>
</tr>
<tr>
<td>Object Tags</td>
<td><a href="https://data.iana.org/rdap/object-tags.json">https://data.iana.org/rdap/object-tags.json</a></td>
</tr>
</tbody>
</table>
<p>We can abstract these by making a word to convert a type to a <a href="https://en.wikipedia.org/wiki/URL">URL</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bootstrap-url</span> <span class="nf">( </span><span class="nv">type</span> <span class="nf">-- </span><span class="nv">url</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://data.iana.org/rdap/"</span> <span class="s">".json"</span> <span class="nb">surround </span><span class="k">;
</span></span></span></code></pre></div><p>We don’t want to retrieve these files all the time, so let’s cache them for
30 days:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">INITIALIZED-SYMBOL: bootstrap-cache [ <span class="m">30 </span>days ]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bootstrap-get</span> <span class="nf">( </span><span class="nv">type</span> <span class="nf">-- </span><span class="nv">data</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> bootstrap-url cache-directory bootstrap-cache <span class="nb">get
</span></span></span><span class="line"><span class="cl"> download-outdated-into path>json <span class="k">;
</span></span></span></code></pre></div><p>And provide a way to force delete the cached bootstrap files:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">bootstrap-files</span> { <span class="s">"asn"</span> <span class="s">"dns"</span> <span class="s">"ipv4"</span> <span class="s">"ipv6"</span> <span class="s">"object-tags"</span> }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">reset-bootstrap</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> [ bootstrap-files [ <span class="s">".json"</span> <span class="nb">append </span>?delete-file ] <span class="nb">each </span>] with-cache-directory <span class="k">;
</span></span></span></code></pre></div><p>Each bootstrap file is described in <a href="https://datatracker.ietf.org/doc/html/rfc9224#name-structure-of-the-rdap-boots">RFC
9224</a>,
but basically we want to extract and manipulate the <code>"services"</code> block,
modifying the keys of the
<a href="https://docs.factorcode.org/content/article-assocs-protocol.html">assoc</a>
for convenient searching:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-services</span> <span class="nf">( </span><span class="nv">data</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">key</span> <span class="nf">-- </span><span class="nv">key'</span> <span class="nf">) -- </span><span class="nv">services</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"services"</span> <span class="nb">of </span>] <span class="nb">dip </span>'[ [ _ <span class="nb">map </span>] <span class="nb">dip </span>] <span class="nb">assoc-map </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">search-services</span> <span class="nf">( </span><span class="nv">services</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">key</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">) -- </span><span class="nv">urls</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ <span class="nb">drop </span>_ <span class="nb">any? </span>] <span class="nb">assoc-find drop nip </span><span class="k">; inline
</span></span></span></code></pre></div><p>We can then provide <em>bootstrap</em> data structures that are used for searching.
For example, we find the longest subdomain that has an entry in the <em>dns
bootstrap</em> list to handle both
<a href="https://en.wikipedia.org/wiki/Second-level_domain">SLD</a> and
<a href="https://en.wikipedia.org/wiki/Top-level_domain">TLD</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">dns-bootstrap</span> <span class="nf">( -- </span><span class="nv">services</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"dns"</span> bootstrap-get <span class="s">"services"</span> <span class="nb">of </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">split-domain</span> <span class="nf">( </span><span class="nv">domain</span> <span class="nf">-- </span><span class="nv">domains</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"."</span> split <span class="nb">dup length </span><iota> [ <span class="nb">tail </span><span class="s">"."</span> <span class="nb">join </span>] <span class="nb">with map </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">domain-endpoints</span> <span class="nf">( </span><span class="nv">domain</span> <span class="nf">-- </span><span class="nv">urls</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> split-domain dns-bootstrap [ <span class="nb">swap member? </span>] <span class="nb">with </span>search-services <span class="k">;
</span></span></span></code></pre></div><p>You can see that different domain names are directed to different RDAP
endpoints:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"factorcode.org"</span> domain-endpoints <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"https://rdap.publicinterestregistry.org/rdap/"</span> }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"google.com"</span> domain-endpoints <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"https://rdap.verisign.com/com/v1/"</span> }
</span></span></code></pre></div><p>Or, find the correct endpoint for a given <a href="https://en.wikipedia.org/wiki/IPv4">IPV4
address</a> from the <em>ipv4 bootstrap</em> list:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">ipv4-bootstrap</span> <span class="nf">( -- </span><span class="nv">services</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"ipv4"</span> bootstrap-get [ >ipv4-network ] parse-services <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">ipv4-endpoints</span> <span class="nf">( </span><span class="nv">ipv4</span> <span class="nf">-- </span><span class="nv">urls</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> ipv4-aton ipv4-bootstrap [ ipv4-contains? ] <span class="nb">with </span>search-services <span class="k">;
</span></span></span></code></pre></div><h2 id="lookup">Lookup</h2>
<p>The RDAP data is typically available from HTTP or HTTPS web servers, as JSON
files, but it uses a custom mime-type <code>application/rdap+json</code>. We can
write a simple word to make the request and convert the response:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">accept-rdap</span> <span class="nf">( </span><span class="nv">request</span> <span class="nf">-- </span><span class="nv">request</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"application/rdap+json"</span> <span class="s">"Accept"</span> set-header <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">rdap-get</span> <span class="nf">( </span><span class="nv">url</span> <span class="nf">-- </span><span class="nv">response</span> <span class="nv">rdap</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <get-request> accept-rdap http-request
</span></span><span class="line"><span class="cl"> <span class="nb">dup string? </span>[ utf8 decode ] <span class="nb">unless </span>json> <span class="k">;
</span></span></span></code></pre></div><p>And now we can build a word to lookup a domain:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">lookup-domain</span> <span class="nf">( </span><span class="nv">domain</span> <span class="nf">-- </span><span class="nv">results</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ domain-endpoints random ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"domain/%s"</span> sprintf derive-url rdap-get <span class="nb">nip </span>] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>Or to lookup an IPV4 address:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">lookup-ipv4</span> <span class="nf">( </span><span class="nv">ipv4</span> <span class="nf">-- </span><span class="nv">results</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ ipv4-endpoints random ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"ip/%s"</span> sprintf derive-url rdap-get <span class="nb">nip </span>] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>And we can try it out, getting an extensive response:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"factorcode.org"</span> lookup-domain
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- Data stack:
</span></span><span class="line"><span class="cl">LH{ { <span class="s">"rdapConformance"</span> ~array~ } { <span class="s">"notices"</span> ~array~ } { ...
</span></span></code></pre></div><h2 id="output">Output</h2>
<p>It would be nice to print the output in a more <a href="https://en.wikipedia.org/wiki/Human-readable_medium_and_data">human-readable
format</a>. For
now, we will just print these as a nested tree of keys and values:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">GENERIC:</span> <span class="nf">print-rdap-nested</span> <span class="nf">( </span><span class="nv">padding</span> <span class="nv">key</span> <span class="nv">value</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">linked-assoc</span> <span class="nf">print-rdap-nested</span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">over write write </span><span class="s">":"</span> <span class="nb">print </span><span class="s">" "</span> <span class="nb">append </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">swapd </span>print-rdap-nested ] <span class="nb">with assoc-each </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">array</span> <span class="nf">print-rdap-nested</span>
</span></span><span class="line"><span class="cl"> [ print-rdap-nested ] 2with <span class="nb">each </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">object</span> <span class="nf">print-rdap-nested</span>
</span></span><span class="line"><span class="cl"> present [ <span class="nb">2drop </span>] [ [ <span class="s">": "</span> [ <span class="nb">write </span>] <span class="nb">tri@ </span>] <span class="nb">dip print </span>] <span class="nb">if-empty </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">print-rdap</span> <span class="nf">( </span><span class="nv">results</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <span class="s">""</span> <span class="nb">-rot </span>print-rdap-nested ] <span class="nb">assoc-each </span><span class="k">;
</span></span></span></code></pre></div><p>This could probably be improved a fair bit – for example, the keys could be
made more readable, and it doesn’t handle
<a href="https://en.wikipedia.org/wiki/VCard">vCard</a> entries very well.</p>
<h2 id="try-it-out">Try it out!</h2>
<p>You can try this out, lookup a domain name:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"factorcode.org"</span> lookup-domain print-rdap
</span></span><span class="line"><span class="cl">rdapConformance: rdap_level_0
</span></span><span class="line"><span class="cl">rdapConformance: icann_rdap_response_profile_0
</span></span><span class="line"><span class="cl">rdapConformance: icann_rdap_technical_implementation_guide_0
</span></span><span class="line"><span class="cl">ldhName: factorcode.org
</span></span><span class="line"><span class="cl">unicodeName: factorcode.org
</span></span><span class="line"><span class="cl">nameservers:
</span></span><span class="line"><span class="cl"> ldhName: carl.ns.cloudflare.com
</span></span><span class="line"><span class="cl"> unicodeName: carl.ns.cloudflare.com
</span></span><span class="line"><span class="cl"> objectClassName: nameserver
</span></span><span class="line"><span class="cl"> handle: c34bedeccd8e4514b917e9e82a052077-LROR
</span></span><span class="line"><span class="cl"> status: associated
</span></span><span class="line"><span class="cl">nameservers:
</span></span><span class="line"><span class="cl"> ldhName: kay.ns.cloudflare.com
</span></span><span class="line"><span class="cl"> unicodeName: kay.ns.cloudflare.com
</span></span><span class="line"><span class="cl"> objectClassName: nameserver
</span></span><span class="line"><span class="cl"> handle: 7fc12bf413944de088f27f837349a8da-LROR
</span></span><span class="line"><span class="cl"> status: associated
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>Or, lookup an IP address:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"1.1.1.1"</span> lookup-ipv4 print-rdap
</span></span><span class="line"><span class="cl">rdapConformance: history_version_0
</span></span><span class="line"><span class="cl">rdapConformance: nro_rdap_profile_0
</span></span><span class="line"><span class="cl">rdapConformance: cidr0
</span></span><span class="line"><span class="cl">rdapConformance: rdap_level_0
</span></span><span class="line"><span class="cl">events:
</span></span><span class="line"><span class="cl"> eventAction: registration
</span></span><span class="line"><span class="cl"> eventDate: 2011-08-10T23:12:35Z
</span></span><span class="line"><span class="cl">events:
</span></span><span class="line"><span class="cl"> eventAction: <span class="nb">last </span>changed
</span></span><span class="line"><span class="cl"> eventDate: 2023-04-26T22:57:58Z
</span></span><span class="line"><span class="cl">name: APNIC-LABS
</span></span><span class="line"><span class="cl">status: active
</span></span><span class="line"><span class="cl">type: ASSIGNED PORTABLE
</span></span><span class="line"><span class="cl">endAddress: 1.1.1.255
</span></span><span class="line"><span class="cl">ipVersion: v4
</span></span><span class="line"><span class="cl">startAddress: 1.1.1.0
</span></span><span class="line"><span class="cl">objectClassName: ip network
</span></span><span class="line"><span class="cl">handle: 1.1.1.0 <span class="nb">- </span>1.1.1.255
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>Pretty cool!</p>
<p>This was <a href="https://github.com/factor/factor/commit/bddceb9fa347fa832bda86ab64a9b881799632ec">recently
committed</a>
to the <a href="https://github.com/factor/factor">development version of Factor</a>.</p>
Two Sum
https://re.factorcode.org/2025/02/two-sum.html
Sat, 22 Feb 2025 08:00:00 -0700https://re.factorcode.org/2025/02/two-sum.html<p>A few days ago, <a href="https://www.developing.dev">Ryan Petermen</a> wrote a <a href="https://x.com/ryanlpeterman/status/1891531125673357690">tweet
about switching to Python from
C++</a> in a solution for
the <a href="https://leetcode.com/problems/two-sum/description/">Two Sum problem</a> from
<a href="https://leetcode.com">LeetCode</a>:</p>
<blockquote>
<p>Given an array of integers <code>nums</code> and an integer <code>target</code>, return
<em>indices of the two numbers such that they add up to <code>target</code></em>.</p>
<p>You may assume that each input would have <em><strong>exactly</strong></em> <strong>one solution</strong>, and
you may not use the <em>same</em> element twice.</p>
<p>You can return the answer in any order.</p>
</blockquote>
<p>Spoilers below!</p>
<p>They offered this iterative <a href="https://en.wikipedia.org/wiki/C%2B%2B">C++</a> solution:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">int</span><span class="o">></span> <span class="n">twoSum</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">int</span><span class="o">>&</span> <span class="n">nums</span><span class="p">,</span> <span class="kt">int</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">unordered_map</span><span class="o"><</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="o">></span> <span class="n">hashMap</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">int</span><span class="o">></span> <span class="n">result</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">nums</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">complement</span> <span class="o">=</span> <span class="n">target</span> <span class="o">-</span> <span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">hashMap</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">complement</span><span class="p">)</span> <span class="o">!=</span> <span class="n">hashMap</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">result</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">hashMap</span><span class="p">[</span><span class="n">complement</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"> <span class="n">result</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="n">hashMap</span><span class="p">[</span><span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]]</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Followed by this improved <a href="https://python.org">Python</a> solution:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">two_sum</span><span class="p">(</span><span class="n">nums</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">hashmap</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">num</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">nums</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">complement</span> <span class="o">=</span> <span class="n">target</span> <span class="o">-</span> <span class="n">num</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">complement</span> <span class="ow">in</span> <span class="n">hashmap</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">[</span><span class="n">hashmap</span><span class="p">[</span><span class="n">complement</span><span class="p">],</span> <span class="n">i</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="n">hashmap</span><span class="p">[</span><span class="n">num</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span>
</span></span></code></pre></div><p>Of course, I was curious what a <a href="https://factorcode.org">Factor</a> solution would
look like.</p>
<h3 id="direct-translation">Direct translation</h3>
<p>We can start by making a <em>mostly</em> direct translation using <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">two-sum</span> <span class="nf">( </span><span class="nv">nums</span> <span class="nv">target</span> <span class="nf">-- </span><span class="nv">i</span> <span class="nv">j</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> H{ } <span class="nb">clone </span>:> hashmap
</span></span><span class="line"><span class="cl"> nums <enumerated> [
</span></span><span class="line"><span class="cl"> <span class="nb">first2 </span>:> <span class="nf">( </span><span class="nv">i</span> <span class="nv">num</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> target num <span class="nb">- </span>:> complement
</span></span><span class="line"><span class="cl"> complement hashmap <span class="nb">at </span>[
</span></span><span class="line"><span class="cl"> i num hashmap <span class="nb">set-at </span><span class="no">f
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">unless*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map-find ?first </span><span class="k">;
</span></span></span></code></pre></div><p>And a few test cases to show that it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="m">0 1 </span>} [ { <span class="m">2 7 11 15 </span>} <span class="m">9 </span>two-sum ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">1 2 </span>} [ { <span class="m">3 2 4 </span>} <span class="m">6 </span>two-sum ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">0 1 </span>} [ { <span class="m">3 3 </span>} <span class="m">6 </span>two-sum ] unit-test
</span></span></code></pre></div><h3 id="using-map-find-index">Using map-find-index</h3>
<p>Sometimes a higher-level word exists that can simplify logic, for example
<a href="https://docs.factorcode.org/content/word-map-find-index,sequences.extras.html">map-find-index</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">two-sum</span> <span class="nf">( </span><span class="nv">nums</span> <span class="nv">target</span> <span class="nf">-- </span><span class="nv">i</span> <span class="nv">j</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> H{ } <span class="nb">clone </span>:> hashmap
</span></span><span class="line"><span class="cl"> nums [| num i |
</span></span><span class="line"><span class="cl"> target num <span class="nb">- </span>:> complement
</span></span><span class="line"><span class="cl"> complement hashmap <span class="nb">at </span>[
</span></span><span class="line"><span class="cl"> i num hashmap <span class="nb">set-at </span><span class="no">f
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">unless*
</span></span></span><span class="line"><span class="cl"> ] map-find-index <span class="nb">drop </span><span class="k">;
</span></span></span></code></pre></div><h3 id="implicit-arguments">Implicit arguments</h3>
<p>In the spirit of <a href="https://re.factorcode.org/2011/07/concatenative-thinking.html">concatenative
thinking</a>, we could reduce the amount
of named variables we have slightly by not naming the <code>complement</code>
internal variable:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">two-sum</span> <span class="nf">( </span><span class="nv">nums</span> <span class="nv">target</span> <span class="nf">-- </span><span class="nv">i</span> <span class="nv">j</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> H{ } <span class="nb">clone </span>:> hashmap
</span></span><span class="line"><span class="cl"> nums [| num i |
</span></span><span class="line"><span class="cl"> target num <span class="nb">- </span>hashmap <span class="nb">at </span>[
</span></span><span class="line"><span class="cl"> i num hashmap <span class="nb">set-at </span><span class="no">f
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">unless*
</span></span></span><span class="line"><span class="cl"> ] map-find-index <span class="nb">drop </span><span class="k">;
</span></span></span></code></pre></div><h3 id="fried-quotations">Fried quotations</h3>
<p>And that works, but perhaps we could try some alternative syntax features like
<a href="https://docs.factorcode.org/content/article-fry.html">fried quotations</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">two-sum</span> <span class="nf">( </span><span class="nv">nums</span> <span class="nv">target</span> <span class="nf">-- </span><span class="nv">i</span> <span class="nv">j</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> H{ } <span class="nb">clone dup </span>'[
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span>_ <span class="nb">over - </span>_ <span class="nb">at </span>[ <span class="nb">2nip </span>] [ _ <span class="nb">set-at </span><span class="no">f </span>] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] map-find-index <span class="nb">drop </span><span class="k">;
</span></span></span></code></pre></div><p>Is that better? Perhaps.</p>
<p>How else might you solve this problem?</p>
<p>Can you do it in one-line?</p>
Random Derangement
https://re.factorcode.org/2024/12/random-derangement.html
Mon, 16 Dec 2024 08:00:00 -0700https://re.factorcode.org/2024/12/random-derangement.html<p>I had a lot of fun writing code to compute
<a href="https://re.factorcode.org/2024/12/derangements.html">derangements</a> recently. I thought I was done with
that topic until I bumped into a question on
<a href="https://stackoverflow.com">StackOverflow</a> asking how to <a href="https://stackoverflow.com/questions/25200220/generate-a-random-derangement-of-a-list">generate a random
derangement of a
list</a>.
Being <a href="https://xkcd.com/356/">nerd sniped</a> is a real thing, and so I started
looking at solutions.</p>
<p>There’s a paper called “<a href="https://labdisia.disia.unifi.it/merlini/papers/Derangements.pdf">An analysis of a simple algorithm for random
derangements</a>”
that has an, ahem, <em>simple</em> algorithm. The basic idea is to generate a random
permutation of indices, breaking early if the <em>random permutation</em> is obviously
not a derangement.</p>
<p>One way to take a random permutation would be to use our <a href="https://docs.factorcode.org/content/word-__lt__permutations__gt__,math.combinatorics.html">permutations virtual
sequence</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"ABCDEF"</span> <permutations> random <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"FCEBDA"</span> <span class="c">! is a derangement</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"ABCDEF"</span> <permutations> random <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"DFBCEA"</span> <span class="c">! is NOT a derangement</span>
</span></span></code></pre></div><p>And so you could loop until a <em>derangement</em> of indices is found:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-derangement-indices</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span><span class="nb">swap </span><iota> <permutations>
</span></span><span class="line"><span class="cl"> '[ <span class="nb">drop </span>_ random <span class="nb">dup </span>derangement? <span class="nb">not </span>] <span class="nb">loop </span><span class="k">;
</span></span></span></code></pre></div><p>But, since only 36% or so of permutations are derangements, perhaps it would be
faster and better to implement the algorithm from that paper – making our own
random permutation of indices and breaking early if obviously not a
<em>derangement</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">random-derangement-indices</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">indices</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> n <iota> <span class="nb">>array </span>:> seq
</span></span><span class="line"><span class="cl"> <span class="no">f </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>:> v
</span></span><span class="line"><span class="cl"> n <span class="m">1 </span>(a..b] [| j |
</span></span><span class="line"><span class="cl"> j <span class="m">1 </span><span class="nb">+ </span>random :> p
</span></span><span class="line"><span class="cl"> p v <span class="nb">nth </span>j <span class="nb">= </span>[ <span class="no">t </span>] [ j p v <span class="nb">exchange </span><span class="no">f </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">any? </span>v <span class="nb">first zero? or
</span></span></span><span class="line"><span class="cl"> ] [ <span class="nb">drop </span>seq <span class="nb">clone </span>] <span class="nb">do while </span><span class="k">;
</span></span></span></code></pre></div><p>We can use that to build a <code>random-derangement</code> word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-derangement</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>random-derangement-indices ] [ <span class="nb">nths </span>] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>And then, for example, get a <em>random derangement</em> of the alphabet – of which
there are one hundred and forty-eight septillion derangements, give or take –
in under a millisecond:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"ABCDEFGHIJKLMNOPQRSTUVWXYZ"</span> random-derangement <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"CZFABMSUXRQDEHGYJLTPVOIKWN"</span>
</span></span></code></pre></div><p>We could check to make sure that we generate all derangments with equal
possibility using a simple test case:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1,000,000 </span>[
</span></span><span class="line"><span class="cl"> <span class="s">"ABCD"</span> random-derangement
</span></span><span class="line"><span class="cl"> ] <span class="nb">replicate </span>histogram sort-keys <span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> { <span class="s">"BADC"</span> <span class="m">111639 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"BCDA"</span> <span class="m">110734 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"BDAC"</span> <span class="m">110682 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"CADB"</span> <span class="m">111123 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"CDAB"</span> <span class="m">111447 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"CDBA"</span> <span class="m">111147 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"DABC"</span> <span class="m">111215 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"DCAB"</span> <span class="m">111114 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"DCBA"</span> <span class="m">110899 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Looks good to me!</p>
Derangements
https://re.factorcode.org/2024/12/derangements.html
Sun, 08 Dec 2024 08:00:00 -0700https://re.factorcode.org/2024/12/derangements.html<p><a href="https://en.wikipedia.org/wiki/Derangement">Derangements</a>, also sometimes known
as <em>deranged permutations</em>, are described as:</p>
<blockquote>
<p>In <a href="https://en.wikipedia.org/wiki/Combinatorics">combinatorial</a>
<a href="https://en.wikipedia.org/wiki/Mathematics">mathematics</a>, a derangement is a
<a href="https://en.wikipedia.org/wiki/Permutation">permutation</a> of the elements of a
<a href="https://en.wikipedia.org/wiki/Set_(mathematics)">set</a> in which no element
appears in its original position. In other words, a derangement is a
permutation that has no <a href="https://en.wikipedia.org/wiki/Fixed_point_(mathematics)">fixed
points</a>.</p>
</blockquote>
<p>There is a fun online <a href="https://www.dcode.fr/derangements-generator">derangements generator
tool</a> that you can use to play
with computing the <em>derangements of a sequence</em> as well as calculating the
<em>number of derangements</em> for a given sequence size.</p>
<p>As an example, we can use the <a href="https://docs.factorcode.org/content/vocab-math.combinatorics.html">math.combinatorics
vocabulary</a>,
to generate all the permutations of the sequence <code>{ 0 1 2 }</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">0 1 2 </span>} all-permutations <span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> { <span class="m">0 1 2 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">0 2 1 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">1 0 2 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">1 2 0 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">2 0 1 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">2 1 0 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Since a <em>derangement</em> is a permutation that requires each element to be in a
different slot, we could write a word to check the permuted indices to see if
that is true:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">derangement?</span> <span class="nf">( </span><span class="nv">indices</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup length </span><iota> [ <span class="nb">= </span>] 2any? <span class="nb">not </span><span class="k">;
</span></span></span></code></pre></div><p>These would be the two <em>derangements</em> of the indices <code>{ 0 1 2 }</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">0 1 2 </span>} all-permutations [ derangement? ] <span class="nb">filter </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> { <span class="m">1 2 0 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">2 0 1 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>The <em>number of derangements</em> is the
<a href="https://mathworld.wolfram.com/Subfactorial.html">subfactorial</a> of the length
of the sequence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">subfactorial</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">1 </span>] [ factorial <span class="m">1 </span><span class="nb">+ </span>e <span class="nb">/i </span>] <span class="nb">if-zero </span><span class="k">;
</span></span></span></code></pre></div><p>We can build a <code><derangement-iota></code> that is a sequence as long as that
number:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><derangement-iota></span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv"><iota></span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">length </span>subfactorial <iota> <span class="k">; inline
</span></span></span></code></pre></div><p>And we can build a <code>next-derangement</code> word that calculates the <a href="https://re.factorcode.org/2012/03/next-permutation.html">next
permutation</a> that is a <em>derangement</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-derangement</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>derangement? ] [ next-permutation ] <span class="nb">do until </span><span class="k">;
</span></span></span></code></pre></div><p>We can then build upon some of the code for <a href="https://docs.factorcode.org/content/word-each-permutation,math.combinatorics.html">iterating
permutations</a>,
designing an internal <code>derangements-quot</code> word that is similar in form to the
existing <code>permutations-quot</code> word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">derangements-quot</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">quot</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nv">quot'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ <derangement-iota> ] [ <span class="nb">length </span><iota> <span class="nb">>array </span>] [ ] <span class="nb">tri </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> '[ <span class="nb">drop </span>_ next-derangement _ nths-unsafe @ ] <span class="k">; inline
</span></span></span></code></pre></div><p>And then use it to build a series of words that can provide iteration across
<em>derangements</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">each-derangement</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">seq</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">elt</span> <span class="nf">-- </span><span class="nv">...</span> <span class="nf">) -- </span><span class="nv">...</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> derangements-quot <span class="nb">each </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">map-derangements</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">seq</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">elt</span> <span class="nf">-- </span><span class="nv">...</span> <span class="nv">newelt</span> <span class="nf">) -- </span><span class="nv">...</span> <span class="nv">newseq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> derangements-quot <span class="nb">map </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">filter-derangements</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">seq</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">elt</span> <span class="nf">-- </span><span class="nv">...</span> <span class="nv">?</span> <span class="nf">) -- </span><span class="nv">...</span> <span class="nv">newseq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">selector </span>[ each-derangement ] <span class="nb">dip </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-derangements</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ ] map-derangements <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-derangements?</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">seq</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">elt</span> <span class="nf">-- </span><span class="nv">...</span> <span class="nv">?</span> <span class="nf">) -- </span><span class="nv">...</span> <span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> derangements-quot <span class="nb">all? </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-derangement</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">seq</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">elt</span> <span class="nf">-- </span><span class="nv">...</span> <span class="nv">?</span> <span class="nf">) -- </span><span class="nv">...</span> <span class="nv">elt/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ _ <span class="nb">keep and </span>] derangements-quot <span class="nb">map-find drop </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">reduce-derangements</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">seq</span> <span class="nv">identity</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">prev</span> <span class="nv">elt</span> <span class="nf">-- </span><span class="nv">...</span> <span class="nv">next</span> <span class="nf">) -- </span><span class="nv">...</span> <span class="nv">result</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">swapd </span>each-derangement <span class="k">; inline
</span></span></span></code></pre></div><p>And, now we can use this to find the nine <em>derangements</em> for <code>"ABCD"</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"ABCD"</span> all-derangements <span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> <span class="s">"BADC"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"BCDA"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"BDAC"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"CADB"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"CDAB"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"CDBA"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"DABC"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"DCAB"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"DCBA"</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/derangements/derangements.factor">GitHub</a>.</p>
Zen of Factor
https://re.factorcode.org/2024/12/zen-of-factor.html
Thu, 05 Dec 2024 08:00:00 -0700https://re.factorcode.org/2024/12/zen-of-factor.html<p>Years ago, I remember reading a blog post called “<a href="http://factor-language.blogspot.com/2006/06/why-is-groovy-is-big.html">Why is Groovy is
big?</a>”
by <a href="https://factorcode.org/slava/">Slava Pestov</a>, the original author of
<a href="https://factorcode.org">Factor</a>. In it, he talks about lines of code and
sets the stage for how <a href="https://re.factorcode.org/2011/07/concatenative-thinking.html">concatenative
thinking</a> can lead to properties like
<a href="https://re.factorcode.org/2011/04/verbosity.html">conciseness</a> and
<a href="https://re.factorcode.org/2012/02/readability.html">readability</a>, ending with this:</p>
<blockquote>
<p>I tend to think the majority of code people write is overly complicated,
full of redundancy, and designed for such flexibility that in practice is
not needed at all. I hope one day this trend reverses.</p>
</blockquote>
<p>I’ve been thinking a lot recently about <a href="https://re.factorcode.org/2024/08/reflecting-on-20-years.html">20 years of
Factor</a> and I thought it would be fun to
write a <em>Zen of Factor</em>. Perhaps inspired by the <a href="https://www.amazon.com/Zen-Programming-Geoffrey-James/dp/0931137098">Zen of
Programming</a>
book written in 1987 by Geoffrey James, there are a couple of examples I wanted
to point to first.</p>
<h3 id="python">Python</h3>
<p>The <a href="https://python.org">Python programming language</a> was one of the first
languages that I am aware of to get a <em>Zen</em>, contributed at least as far
back as <a href="https://github.com/python/cpython/commit/63cd9bf4887cd4603ead4db29c772fa370e68a25">February
2002</a>
in a kind of easter egg fashion encrypted by
<a href="https://en.wikipedia.org/wiki/ROT13">ROT13</a>:</p>
<pre tabindex="0"><code>$ python -c "import this"
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
</code></pre><h3 id="zig">Zig</h3>
<p>Quite a bit more recently, the <a href="https://ziglang.org">Zig programming language</a>
introduced a <em>Zen</em> through a series of commits starting in <a href="https://github.com/ziglang/zig/commit/221e5b188c4aaef9667c8e397d7fac21a8ac8fbd">August
2017</a>.
You can see the current version by running <code>zig zen</code>:</p>
<pre tabindex="0"><code>$ zig zen
* Communicate intent precisely.
* Edge cases matter.
* Favor reading code over writing code.
* Only one obvious way to do things.
* Runtime crashes are better than bugs.
* Compile errors are better than runtime crashes.
* Incremental improvements.
* Avoid local maximums.
* Reduce the amount one must remember.
* Focus on code rather than style.
* Resource allocation may fail; resource deallocation must succeed.
* Memory is a resource.
* Together we serve the users.
</code></pre><h3 id="factor">Factor</h3>
<p>In that spirit, I thought it would be fun to put together some ideas for what a
<em>Zen of Factor</em> might look like, incorporating aspects of the language that I
enjoy, some thematic elements that might make you more successful learning and
developing in it, as well as pointing out the strong community that we have and
hope to grow.</p>
<p>Here is the current draft:</p>
<pre tabindex="0"><code>The Zen of Factor
The REPL is your playground.
Working code beats perfect theory.
Words are better than paragraphs.
Stack effects tell the story.
Any syntax you need, you can create.
Simple primitives yield powerful combinations.
First make it work, then make it beautiful.
Make it beautiful, then make it fast.
Quick hacks grow into robust solutions.
When in doubt, factor it out.
Every word should do one thing well.
Let the stack guide your logic.
Write less, compose more.
If it works, ship it.
If it doesn't work, fix it.
If you don't like it, change it.
Today's beginner is tomorrow's core developer.
Questions encouraged, PRs welcome.
</code></pre><p>I really appreciate hearing about programs and problems that developers work
on, getting feedback that allows us to iteratively improve, and knowing that
every commit leads us towards a better future.</p>
<p>Thank you!</p>
Watching Code
https://re.factorcode.org/2024/12/watching-code.html
Tue, 03 Dec 2024 08:00:00 -0700https://re.factorcode.org/2024/12/watching-code.html<p><a href="https://factorcode.org">Factor</a> has a <a href="https://re.factorcode.org/2012/09/watching-words.html">watching
words</a> feature that allows you to see the inputs
and outputs of a word when it is called. I have wanted a way to define a
<em>watched code block</em> that we could use to see the stack before and after some
inner part of a word.</p>
<p>It turns out that it’s pretty simple to make this syntax using our existing
<a href="https://docs.factorcode.org/content/word-watch,tools.annotations.html">watch</a>
implementation from the <a href="https://docs.factorcode.org/content/vocab-tools.annotations.html">tools.annotations
vocabulary</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">DEFER:</span> <span class="nf">WATCH></span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">SYNTAX: </span><WATCH
</span></span><span class="line"><span class="cl"> <span class="no">\ WATCH></span> parse-until >quotation <span class="nb">dup </span>(watch) <span class="nb">append! </span><span class="k">;
</span></span></span></code></pre></div><p>Using this, we can now watch the inner part of a word, for example this word
that increments the input by 1:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">foo</span> <span class="nf">( </span><span class="nv">x</span> <span class="nf">-- </span><span class="nv">y</span> <span class="nf">) </span><span class="m">1 </span><WATCH <span class="nb">+ </span>WATCH> <span class="k">;
</span></span></span></code></pre></div><p>And see it used:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 </span>foo
</span></span><span class="line"><span class="cl">--- Entering [ <span class="nb">+ </span>]
</span></span><span class="line"><span class="cl">x <span class="m">10
</span></span></span><span class="line"><span class="cl">x <span class="m">1
</span></span></span><span class="line"><span class="cl">--- Leaving [ <span class="nb">+ </span>]
</span></span><span class="line"><span class="cl">x <span class="m">11
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- Data stack:
</span></span><span class="line"><span class="cl"><span class="m">11
</span></span></span></code></pre></div><p>One thing that I’d like to improve on this someday, is making it so this watch
syntax has a
<a href="https://docs.factorcode.org/content/article-prettyprint.html">prettyprint</a>
implementation that allows it to be rendered as it is typed.</p>
<p>I have added this to the latest <a href="https://github.com/factor/factor/">developer
version</a> if you’d like to update and give it
a try!</p>
Listener Font Sizes
https://re.factorcode.org/2024/11/listener-font-sizes.html
Wed, 13 Nov 2024 08:00:00 -0700https://re.factorcode.org/2024/11/listener-font-sizes.html<p><a href="https://factorcode.org">Factor</a> contains a
<a href="https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop">REPL</a> – called the
<em>Listener</em> – available on the command-line and graphically as part of the <a href="https://docs.factorcode.org/content/article-ui-tools.html">UI
developer tools</a>.
For many users, this is their main interface to programming in the <a href="https://en.wikipedia.org/wiki/Factor_(programming_language)">Factor
programming
language</a>.</p>
<p>We sometimes get requests to better support styling the <a href="https://docs.factorcode.org/content/article-ui.html">user
interface</a>. This has led
to improvements such as support for light and dark themes, adjustable font
sizes, and other customizations. There have been a few existing ways to <a href="https://docs.factorcode.org/content/article-ui-listener-style.html">style
the UI
listener</a>
including support for keyboard commands to increase or decrease font sizes.
But, until recently this only affected new output or new Listener sessions.</p>
<p>Today, I improved this to make adjusting the font size much more dynamic, using
traditional keyboard shortcuts of <code>Ctrl</code> – or <code>Cmd</code> on
<a href="https://www.apple.com/macos">macOS</a> – combined with <code>+</code> or <code>-</code>:</p>
<video preload="" controls>
<source src="https://re.factorcode.org/videos/listener-font-sizes.mp4" type="video/mp4">
There should have been a video here but your browser does not seem
to support it.
</video>
<p>Give it a try, and please let us know other ways we can make improvements!</p>
Finding Subsequences
https://re.factorcode.org/2024/11/finding-subsequences.html
Tue, 12 Nov 2024 08:00:00 -0700https://re.factorcode.org/2024/11/finding-subsequences.html<p>Recently, I’ve been inspired by conversations taking place on our <a href="https://discord.gg/QxJYZx3QDf">Factor
Discord server</a>. This sometimes reflects
areas of interest from new contributors, curiousity exploring similarities
and differences between <a href="https://factorcode.org">Factor</a> and other
programming languages, or even early learning moments when exploring
<a href="https://concatenative.org/wiki/view/Concatenative%20language">concatenative
languages</a> in
general.</p>
<p>Today, someone asked about how to think about “<em>accumulation of values in an
array… to find all occurences (their position) of a subseq in a seq</em>”. The
solution to this might have this word name and stack effect:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">subseq-indices</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">subseq</span> <span class="nf">-- </span><span class="nv">indices</span> <span class="nf">) </span>... <span class="k">;
</span></span></span></code></pre></div><p>Before answering, I wanted to make sure they wanted to find overlapping
indices vs. non-overlapping indices, and they clarified that they expect it
to find this result – allowing overlapping subsequences:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"abcabcabc"</span> <span class="s">"abcabc"</span> subseq-indices <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">0 3 </span>}
</span></span></code></pre></div><p>So, now that we have a reasonable specification, how do we think about
solving this problem when we are at the same time learning to solve problems
in stack languages and trying to see what features of <a href="https://docs.factorcode.org/content/article-vocab-index.html">Factor’s standard
library</a> would
help.</p>
<p>There are a lot of ways to think about this, and I often recommend one of
three approaches:</p>
<ol>
<li>starting inside-out (working on the inner part of the loop)</li>
<li>starting outside-in (modeling the outer loop and then figuring out what’s inside it)</li>
<li>using <a href="https://docs.factorcode.org/content/article-locals.html">local variables</a> (helpful when coming from an applicative language background)</li>
</ol>
<p>So let’s look at each approach in turn:</p>
<h2 id="inside-out">Inside Out</h2>
<p>The inner logic is going to require something like “<em>take an index to start
from and find the next matching subseq index</em>”, which looks an awful lot
like
<a href="https://docs.factorcode.org/content/word-subseq-index-from%2Csequences.html">subseq-index-from</a>
– except you also want to increment the found index afterwards to make sure
you are progressing through the sequence.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-subseq-index</span> <span class="nf">( </span><span class="nv">index</span> <span class="nv">seq</span> <span class="nv">subseq</span> <span class="nf">-- </span><span class="nv">next-index/f</span> <span class="nv">found-index/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> subseq-index-from [ [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">keep </span>] [ <span class="no">f f </span>] <span class="nb">if* </span><span class="k">;
</span></span></span></code></pre></div><p>Then you could use it like so in a
<a href="https://docs.factorcode.org/content/word-loop,kernel.html">loop</a> with an
accumulator:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">subseq-indices</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">subseq</span> <span class="nf">-- </span><span class="nv">indices</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ V{ } <span class="nb">clone </span><span class="m">0 </span>] <span class="nb">2dip </span>'[
</span></span><span class="line"><span class="cl"> _ _ next-subseq-index <span class="nb">dup </span>[ [ <span class="nb">pick push </span>] <span class="nb">keep </span>] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop drop </span><span class="k">;
</span></span></span></code></pre></div><p>But that feels like we had to work hard to do that, directly using an
accumulator, conditionals, and some stack shuffling. Luckily we have some
higher level words that might help, for example the <a href="https://docs.factorcode.org/content/article-namespaces-make.html">make
vocabulary</a>
which has an implicit accumulator that we can use <code>,</code> or <code>%</code> to push into:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">subseq-indices</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">subseq</span> <span class="nf">-- </span><span class="nv">indices</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">0 </span>] <span class="nb">2dip </span>'[
</span></span><span class="line"><span class="cl"> [ _ _ next-subseq-index <span class="nb">dup </span>[ , ] <span class="nb">when* </span>] <span class="nb">loop
</span></span></span><span class="line"><span class="cl"> ] { } make <span class="nb">nip </span><span class="k">;
</span></span></span></code></pre></div><p>Or even using a
<a href="https://docs.factorcode.org/content/word-while__star__,kernel.html">while*</a>
loop, which is less code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">subseq-indices</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">subseq</span> <span class="nf">-- </span><span class="nv">indices</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">0 </span>] <span class="nb">2dip </span>'[
</span></span><span class="line"><span class="cl"> [ _ _ next-subseq-index ] [ , ] while*
</span></span><span class="line"><span class="cl"> ] { } make <span class="nb">nip </span><span class="k">;
</span></span></span></code></pre></div><p>But that feels like a lot too, simpler might be
<a href="https://docs.factorcode.org/content/word-produce,sequences.html">produce</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">subseq-indices</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">subseq</span> <span class="nf">-- </span><span class="nv">indices</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">0 </span>] <span class="nb">2dip </span>'[ _ _ next-subseq-index <span class="nb">dup </span>] [ ] <span class="nb">produce 2nip </span><span class="k">;
</span></span></span></code></pre></div><p>Or using
<a href="https://docs.factorcode.org/content/word-follow,sequences.html">follow</a>,
adjusting our start index and increment:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">subseq-indices</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">subseq</span> <span class="nf">-- </span><span class="nv">indices</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">-1 </span>] <span class="nb">2dip </span>'[ <span class="m">1 </span><span class="nb">+ </span>_ _ subseq-index-from ] <span class="nb">follow rest </span><span class="k">;
</span></span></span></code></pre></div><h2 id="outside-in">Outside In</h2>
<p>The outer logic approach would be something like “<em>we need to loop from the
start of the sequence, finding the next match, and accumulating it, until we
hit some exit condition and then return a result</em>” which you could write in a
kind of non-functional stack
<a href="https://en.wikipedia.org/wiki/Pseudocode">pseudocode</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">subseq-indices</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">subseq</span> <span class="nf">-- </span><span class="nv">indices</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>[ find-next-match ] [ accumulate-match ] <span class="nb">while </span><span class="k">;
</span></span></span></code></pre></div><p>Then you have to kind of figure out what goes into those blocks:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-next-match</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">subseq</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">found-index/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">-rot </span>subseq-index-from <span class="k">;
</span></span></span></code></pre></div><p>And also something like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">accumulate-match</span> <span class="nf">( </span><span class="nv">accum</span> <span class="nv">found-index</span> <span class="nf">-- </span><span class="nv">accum</span> <span class="nv">next-index</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">suffix! </span>] <span class="nb">keep </span><span class="m">1 </span><span class="nb">+ </span><span class="k">;
</span></span></span></code></pre></div><p>Taking those, and maybe thinking about what items should be on the stack and
in what order to reduce stack shuffling, becomes something like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">subseq-indices</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">subseq</span> <span class="nf">-- </span><span class="nv">indices</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ V{ } <span class="nb">clone </span><span class="m">0 </span>] <span class="nb">2dip
</span></span></span><span class="line"><span class="cl"> '[ _ _ subseq-index-from ] [ [ <span class="nb">suffix! </span>] <span class="nb">keep </span><span class="m">1 </span><span class="nb">+ </span>] while* <span class="k">;
</span></span></span></code></pre></div><p>It is true that <code>[ suffix! ] keep 1 +</code> is also <code>[ suffix! ] [ 1 + ] bi</code>,
with varying aesthetics and ease of understanding, but sometimes when
learning a new language especially a stack language with
<a href="https://docs.factorcode.org/content/article-combinators.html">combinators</a>,
it is sometimes easy to start with stack shuffling and then learn about
these forms later to see if they can improve your code.</p>
<h2 id="locals">Locals</h2>
<p>Instead of those two stack approaches, we could instead use our <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a> and
write one big word in a manner similar to applicative languages, stepping
back and focusing on the result we want:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">subseq-indices</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">subseq</span> <span class="nf">-- </span><span class="nv">indices</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> V{ } <span class="nb">clone </span>:> accum
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>:> i!
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ i seq <span class="nb">subseq </span>subseq-index-from ]
</span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>accum <span class="nb">push </span><span class="m">1 </span><span class="nb">+ </span>i! ] while*
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> accum <span class="k">;
</span></span></span></code></pre></div><p>When working on this stuff, it’s nice to remember you can put a <code>B</code> to
<a href="https://docs.factorcode.org/content/article-breakpoints.html">set a
breakpoint</a> in
places to examine the stack at some inner point, or perhaps write a comment
showing the incoming stack and optionally the outgoing stack that a piece of
code is expected to have so that you understand what is happening in the
next few lines:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="c">! the next block of code finds the next index</span>
</span></span><span class="line"><span class="cl"><span class="c">! ( index seq subseq -- found-index )</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">! and pushes it into an accumulator</span>
</span></span><span class="line"><span class="cl"><span class="c">! ( accum found-index -- accum )</span>
</span></span></code></pre></div><p>This was <a href="https://github.com/factor/factor/commit/ae50ca8e1189b13a4a86dca72d07d19015d08961">added to the developer
branch</a>
in the <code>sequences.extras</code> vocabulary.</p>
<p>We love to hear questions and it’s even better when we can provide answers or
guidance for learning and solving problems. Feel free to join our conversations
and explore <a href="https://concatenative.org/wiki/view/Factor/Learning">learning
Factor</a>!</p>
Removing Subdomains
https://re.factorcode.org/2024/11/removing-subdomains.html
Tue, 05 Nov 2024 08:00:00 -0700https://re.factorcode.org/2024/11/removing-subdomains.html<p>There was an interesting question on the <a href="https://unix.stackexchange.com">Unix & Linux
StackExchange</a> asking how to <a href="https://unix.stackexchange.com/questions/774280/remove-subdomains-or-existing-domains">remove
subdomains or existing
domains</a>.
I thought it would be fun to show a few different approaches to solving this
using <a href="https://factorcode.org">Factor</a>.</p>
<p>Our first step should be to understand <a href="https://www.wix.com/blog/what-is-a-subdomain">what is a
subdomain</a>:</p>
<blockquote>
<p>A subdomain is a prefix added to a domain name to separate a section of
your website. Site owners primarily use subdomains to manage extensive
sections that require their own content hierarchy, such as online stores,
blogs, job boards or support platforms.</p>
</blockquote>
<h3 id="common-subdomains">Common Subdomains</h3>
<p>If we’re curious about what common subdomains are, we can turn to the
<a href="https://github.com/danielmiessler/SecLists">SecLists</a> project – described
as a “<em>security tester’s companion</em>” – which maintains a list of common
<a href="https://github.com/danielmiessler/SecLists/blob/master/Discovery/DNS/subdomains-top1million-5000.txt">5,000
subdomains</a>,
<a href="https://github.com/danielmiessler/SecLists/blob/master/Discovery/DNS/subdomains-top1million-20000.txt">20,000
subdomains</a>,
and <a href="https://github.com/danielmiessler/SecLists/blob/master/Discovery/DNS/subdomains-top1million-110000.txt">110,000
subdomains</a>
that were generated in 2015 as well as a <a href="https://github.com/danielmiessler/SecLists/blob/master/Discovery/DNS/combined_subdomains.txt">combined
subdomains</a>
list that has some additional ones added.</p>
<p>You can download the top 5,000 common subdomains using
<a href="https://docs.factorcode.org/content/article-memoize.html">memoization</a> to
cache the result:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MEMO:</span> <span class="nf">top-5000-subdomains</span> <span class="nf">( -- </span><span class="nv">subdomains</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://raw.githubusercontent.com/danielmiessler/SecLists/refs/heads/master/Discovery/DNS/subdomains-top1million-5000.txt"</span>
</span></span><span class="line"><span class="cl"> cache-directory download-once-into utf8 file-lines <span class="k">;
</span></span></span></code></pre></div><p>And then see what the “top 10” are:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> top-5000-subdomains <span class="m">10 </span><span class="nb">head </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> <span class="s">"www"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"mail"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"ftp"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"localhost"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"webmail"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"smtp"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"webdisk"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"pop"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"cpanel"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"whm"</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>You could remove “common subdomains” – adding a dot to make sure we only strip
a full subdomain – by recursively trying to clean the hostname until it stops
changing.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">remove-common-subdomains</span> <span class="nf">( </span><span class="nv">host</span> <span class="nf">-- </span><span class="nv">host'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> top-5000-subdomains [ <span class="s">"."</span> <span class="nb">append </span>] <span class="nb">map </span>'[ _ [ ?head ] <span class="nb">any? </span>] <span class="nb">loop </span><span class="k">;
</span></span></span></code></pre></div><p>And try it out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"www.mail.ftp.localhost.factorcode.org"</span>
</span></span><span class="line"><span class="cl"> remove-common-subdomains <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"factorcode.org"</span>
</span></span></code></pre></div><p>That works pretty well, but it’s reliant on a <a href="https://en.wikipedia.org/wiki/Web_scraping">scraped
list</a> of subdomains that might not
be exhaustive, and could become stale over time as the tools and techniques
that developers use change.</p>
<h3 id="observed-subdomains">Observed Subdomains</h3>
<p>Similarly, another technique we could use would be to use our own observations
about domains, and if we observe a domain being used and then subsequently see
a subdomain of it, we can ignore the subdomain.</p>
<p>First, we write a word to remove any item that is prefixed by another, sorting
to make sure we see the prefix before the item prefixed by it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">remove-prefixed</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> sort V{ } <span class="nb">clone </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>'[
</span></span><span class="line"><span class="cl"> [ _ [ <span class="nb">head? </span>] <span class="nb">with </span>none? ] _ push-when
</span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div><p>Second, we can remove the subdomains by using a kind of <a href="https://en.wikipedia.org/wiki/Schwartzian_transform">Schwartzian
transform</a>:</p>
<ol>
<li>reverse the domain names</li>
<li>remove the ones that are prefixed by another</li>
<li>un-reverse the domain names</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">remove-observed-subdomains</span> <span class="nf">( </span><span class="nv">hosts</span> <span class="nf">-- </span><span class="nv">hosts'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"."</span> <span class="nb">prepend reverse </span>] <span class="nb">map </span>remove-prefixed [ <span class="nb">reverse rest </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>And then see it work:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="s">"a.b.c"</span> <span class="s">"b.c"</span> <span class="s">"c.d.e"</span> <span class="s">"e.f"</span> }
</span></span><span class="line"><span class="cl"> remove-observed-subdomains <span class="m">.
</span></span></span><span class="line"><span class="cl">V{ <span class="s">"b.c"</span> <span class="s">"c.d.e"</span> <span class="s">"e.f"</span> }
</span></span></code></pre></div><h3 id="resolving-domains">Resolving Domains</h3>
<p>And, finally, another technique might be to use the <a href="https://en.wikipedia.org/wiki/Domain_Name_System">Domain Name
System</a> to find the
<em>rootiest</em> <a href="https://en.wikipedia.org/wiki/Domain_name">domain name</a>.</p>
<p>First, we use our <a href="https://docs.factorcode.org/content/vocab-dns.html">dns
vocabulary</a> to check that a
host resolves to an IP address:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">valid-domain?</span> <span class="nf">( </span><span class="nv">host</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ dns-A-query message>a-names <span class="nb">empty? not </span>]
</span></span><span class="line"><span class="cl"> [ dns-AAAA-query message>aaaa-names <span class="nb">empty? not </span>]
</span></span><span class="line"><span class="cl"> } 1|| <span class="k">;
</span></span></span></code></pre></div><p>And try it out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"re.factorcode.org"</span> valid-domain? <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">t
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"not-valid.factorcode.org"</span> valid-domain? <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">f
</span></span></span></code></pre></div><p>Second, we write a word to split a domain into chunks to be tested:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">split-domain</span> <span class="nf">( </span><span class="nv">host</span> <span class="nf">-- </span><span class="nv">hosts</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"."</span> split <span class="nb">dup length </span><span class="m">1 </span>[-] <iota> [ <span class="nb">tail </span><span class="s">"."</span> <span class="nb">join </span>] <span class="nb">with map </span><span class="k">;
</span></span></span></code></pre></div><p>And try it out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"a.b.c.com"</span> split-domain <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"a.b.c.com"</span> <span class="s">"b.c.com"</span> <span class="s">"c.com"</span> }
</span></span></code></pre></div><p>Third, we find the <em>rootiest</em> domain that is <em>valid</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">remove-subdomains</span> <span class="nf">( </span><span class="nv">host</span> <span class="nf">-- </span><span class="nv">host'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> split-domain [ valid-domain? ] <span class="nb">find-last nip </span><span class="k">;
</span></span></span></code></pre></div><p>And try it out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"a.b.c.d.factorcode.org"</span> remove-subdomains <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"factorcode.org"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"sorting.cr.yp.to"</span> remove-subdomains <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"cr.yp.to"</span>
</span></span></code></pre></div><p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/subdomains/subdomains.factor">GitHub</a>.</p>
<p>It’s fun to explore these kinds of problems!</p>
A Language A Day
https://re.factorcode.org/2024/11/a-language-a-day.html
Mon, 04 Nov 2024 08:00:00 -0700https://re.factorcode.org/2024/11/a-language-a-day.html<p><a href="https://andrewshitov.com">Andrew Shitov</a> recently <a href="https://andrewshitov.com/2024/11/02/a-language-a-day/">published a
book</a> called “<em>A
Language A Day</em>”, which is a collection of brief overviews to 21 programming
languages – including <a href="https://factorcode.org">Factor</a>!</p>
<p>
<img src="https://re.factorcode.org/images/2024-11-04-a-language-a-day.jpg" alt="" width="600" height="845" />
</p>
<blockquote>
<p>This book provides a concise overview of 21 different programming languages.
Each language is introduced using the same approach: solving several
programming problems to showcase its features and capabilities. Languages
covered in the book: C++, Clojure, Crystal, D, Dart, Elixir, Factor, Go,
Hack, Hy, Io, Julia, Kotlin, Lua, Mercury, Nim, OCaml, Raku, Rust, Scala, and
TypeScript.</p>
<p>Each chapter covers the essentials of a different programming language. To make
the content more consistent and comparable, I use the same structure for each
language, focusing on the following mini projects:</p>
<ol>
<li>Creating a ‘Hello, World!’ program.</li>
<li>Implementing a Factorial function using recursion or a functional-style approach.</li>
<li>Creating a polymorphic array of objects (a ‘zoo’ of cats and dogs) and calling methods on them.</li>
<li>Implementing the Sleep Sort algorithm—while impractical for real-word use, it’s a playful demonstration of language’s concurrency capabilities.</li>
</ol>
<p>Each language description follows—where applicable—this pattern:</p>
<ol>
<li>Installing a command-line compiler and running a program.</li>
<li>Creating and using variables.</li>
<li>Defining and using functions.</li>
<li>Exploring object-oriented features.</li>
<li>Handling exception.</li>
<li>Introducing basic concurrency and parallelism.</li>
</ol>
<p>You can find all the code examples in this book on GitHub:
<a href="https://github.com/ash/a-language-a-day">https://github.com/ash/a-language-a-day</a>.</p>
<p>You can buy it on Amazon or LeanPub as an electronic or Kindle edition, or as a
paper hardcover or paperback version. <a href="https://andrewshitov.com/a-language-a-day/">More information with the links to the
shops</a>.</p>
</blockquote>
<p>Check it out!</p>
Constants
https://re.factorcode.org/2024/10/constants.html
Tue, 29 Oct 2024 08:00:00 -0700https://re.factorcode.org/2024/10/constants.html<p><a href="https://factorcode.org">Factor</a> has <a href="https://en.wikipedia.org/wiki/Syntax_(programming_languages)">programmable
syntax</a>, a
feature that allows for <a href="https://concatenative.org/wiki/view/Concatenative%20language/Concision">concise source
code</a>,
reducing repetition and allowing the programmer to express forms and intent
with minimal tokens. As an example of this, today I want to discuss
<a href="https://docs.factorcode.org/content/article-words.constant.html">constants</a>.</p>
<p>You can define a word with a constant value, using syntax like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">three</span> <span class="m">3
</span></span></span></code></pre></div><p>Someone on our <a href="https://discord.gg/QxJYZx3QDf">Factor Discord server</a> asked if
it was possible to define multiple constants in one syntax expression, to avoid
the line noise of defining them one-by-one.</p>
<p>So, instead of these four definitions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">foo</span> <span class="m">1
</span></span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">bar</span> $[ <span class="m">2 </span>sqrt ]
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">baz</span> <span class="no">$ bar</span>
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">qux</span> <span class="no">\ foo</span>
</span></span></code></pre></div><p>We could instead make this syntax:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYNTAX: </span>CONSTANTS:
</span></span><span class="line"><span class="cl"> <span class="s">";"</span> [
</span></span><span class="line"><span class="cl"> create-word-in
</span></span><span class="line"><span class="cl"> [ reset-generic ]
</span></span><span class="line"><span class="cl"> [ scan-object define-constant ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] each-token <span class="k">;
</span></span></span></code></pre></div><p>Breaking that down into steps:</p>
<ol>
<li><code>SYNTAX:</code> indicates we’re defining new syntax</li>
<li><code>CONSTANTS:</code> is the name of our new syntax word</li>
<li><code>";"</code> defines the terminator that will end our constant definitions</li>
<li><code>each-token</code> will process each token until it hits the terminator</li>
</ol>
<p>For each constant definition, it performs these steps:</p>
<ul>
<li><code>create-word-in</code> creates a new word in the current vocabulary</li>
<li><code>reset-generic</code> clears any generic word properties</li>
<li><code>scan-object</code> reads and parses the next value</li>
<li><code>define-constant</code> makes it a constant with the parsed value</li>
</ul>
<p>And now this expression works, reducing the <a href="https://medium.com/free-code-camp/noise-is-all-around-us-d0c0fcb8d48">visual
noise</a> in
our source code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">CONSTANTS:
</span></span><span class="line"><span class="cl"> foo <span class="m">1
</span></span></span><span class="line"><span class="cl"> bar $[ <span class="m">2 </span>sqrt ]
</span></span><span class="line"><span class="cl"> baz <span class="no">$ bar</span>
</span></span><span class="line"><span class="cl"> qux <span class="no">\ foo</span>
</span></span><span class="line"><span class="cl"><span class="k">;
</span></span></span></code></pre></div><p>As an aside, the different syntaxes used above are:</p>
<ul>
<li><code>1</code> is just a token parsed as a number literal</li>
<li><code>$[ ... ]</code> evaluates the code inside at parse time</li>
<li><code>$</code> gets the value of another constant</li>
<li><code>\</code> gets the word object itself rather than its value</li>
</ul>
<p>Factor’s syntax <a href="https://docs.factorcode.org/content/article-parsing-words.html">parsing
words</a> allow a
great deal of flexibility in making custom
<a href="https://en.wikipedia.org/wiki/Domain-specific_language">DSL-style</a> syntax
forms work nicely to reduce repetition, and generate code with less effort.</p>
<p>I’m not sure if this is worth adding to the <a href="https://docs.factorcode.org/content/article-vocab-index.html">standard
library</a> or not,
but it’s neat!</p>
Base16 Themes
https://re.factorcode.org/2024/10/base16-themes.html
Thu, 17 Oct 2024 08:00:00 -0700https://re.factorcode.org/2024/10/base16-themes.html<p>Over a decade ago, <a href="https://github.com/chriskempson">Chris Kempson</a> created the
<a href="https://github.com/chriskempson/base16">Base16 theme framework</a> for creating
color palettes of 16 colors that can be used to provide theming of user
interfaces. These have been commonly supported by many text editors, with some
developers gravitating toward setting their favorite theme in every user
interface that supports it.</p>
<p>A few years ago, this framework and the many themes that became popular in it
were forked into the <a href="https://github.com/tinted-theming">Tinted Theming</a>
project described in a post called <a href="https://github.com/tinted-theming/home/issues/51">Base16 Project Lives
On</a>. You can view their
<a href="https://tinted-theming.github.io/base16-gallery/">gallery of Base16 themes</a>
which gives a good sense of the variety and utility of these color schemes
having commonly recognizable names such as <em>dracula</em>, <em>mocha</em>, <em>solarized</em>, and
more.</p>
<p>I was reminded of this recently in a discussion around a recent contribution to
change the scrollbar and button implementations to not use images, but to draw
the scrollbars using the colors configured in the user’s
<a href="https://docs.factorcode.org/content/vocab-ui.theme.html">theme</a>.</p>
<p>Since 2021, the <a href="https://docs.factorcode.org/content/vocab-ui.theme.base16.html">ui.theme.base16
vocabulary</a> has
allowed theming the <a href="https://docs.factorcode.org/content/article-ui.html">Factor user
interface</a> by choosing a
<a href="https://docs.factorcode.org/content/word-base16-theme-name,ui.theme.base16.html">base16-theme-name</a>
and setting
<a href="https://docs.factorcode.org/content/word-base16-mode,ui.theme.switching.html">base16-mode</a>.
We have just improved our support for Base16 theme support by <a href="https://github.com/factor/factor/commit/7bf6cf4f89fb4a3eda0cdc922b66532b21375c98">adding all the
current
styles</a>
from the <a href="https://github.com/tinted-theming/schemes">Tinted Theming schemes</a>
list.</p>
<p>So, now you can try <em>solarized-dark</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"solarized-dark"</span> base16-theme-name <span class="nb">set-global
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> base16-mode
</span></span></code></pre></div><p>
<img src="https://re.factorcode.org/images/2024-10-17-solarized-dark.png" alt="" width="689" height="444" />
</p>
<p>Or perhaps <em>greenscreen</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"greenscreen"</span> base16-theme-name <span class="nb">set-global
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> base16-mode
</span></span></code></pre></div><p>
<img src="https://re.factorcode.org/images/2024-10-17-greenscreen.png" alt="" width="689" height="444" />
</p>
<p>Or any of the other 270 named color schemes now available!</p>
<p>Enjoy!</p>
Emit
https://re.factorcode.org/2024/10/emit.html
Sun, 13 Oct 2024 08:00:00 -0700https://re.factorcode.org/2024/10/emit.html<p>One of the interesting aspects of a <a href="https://concatenative.org/wiki/view/Concatenative%20language">concatenative
language</a> like
<a href="https://factorcode.org">Factor</a> is that blocks of logic can be easily
extracted and easily reused since they apply logic to objects on the
<a href="https://docs.factorcode.org/content/article-stacks.html">stack</a>.</p>
<p>For example, if this was a word that operated on stack values:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">do-things</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">c</span> <span class="nv">d</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ sqrt <span class="nb">* </span>] [ <span class="nb">swap </span>sqrt <span class="nb">+ </span>] <span class="nb">2bi </span><span class="k">;
</span></span></span></code></pre></div><p>One change we could easily make is to extract and name the two pieces of logic:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">calc-c</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">c</span> <span class="nf">) </span>sqrt <span class="nb">* </span><span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">calc-d</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">d</span> <span class="nf">) </span><span class="nb">swap </span>sqrt <span class="nb">+ </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">do-things</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">c</span> <span class="nv">d</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ calc-c ] [ calc-d ] <span class="nb">2bi </span><span class="k">;
</span></span></span></code></pre></div><p>We could also convert it to operate on <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">do-things</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">c</span> <span class="nv">d</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> a b sqrt <span class="nb">* </span>a sqrt b <span class="nb">+ </span><span class="k">;
</span></span></span></code></pre></div><p>And extract those same two pieces of logic:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">calc-c</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">c</span> <span class="nf">) </span>a b sqrt <span class="nb">* </span><span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">calc-d</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">d</span> <span class="nf">) </span>a sqrt b <span class="nb">+ </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">do-things</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">c</span> <span class="nv">d</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> a b calc-c a b calc-d <span class="k">;
</span></span></span></code></pre></div><p>But, notice that we have to specify that the local variable <code>a</code> and <code>b</code>
have to be put back on the stack before we can call our <em>extracted</em> words that
make the computations.</p>
<h3 id="hypothetical-syntax">Hypothetical Syntax</h3>
<p>Today, someone on the <a href="https://discord.gg/QxJYZx3QDf">Factor Discord server</a>
asked about this very issue, wanting to have extractable pieces of logic that
would effectively be operating on nested local variables, wherever they are
used. Inspired by the goal of <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">don’t repeat
yourself</a> and the
convenience of extracting logic that operates on the data stack.</p>
<p>Specifically, they wanted to be able to take blocks of logic that operate on
named variables, and extract them in a similar manner to the logic blocks that
operate on the stack – offering this hypothetical syntax as the goal:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">EMIT: calc-c <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">c</span> <span class="nf">) </span>a b sqrt <span class="nb">* </span><span class="k">;
</span></span></span><span class="line"><span class="cl">EMIT: calc-d <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">d</span> <span class="nf">) </span>a sqrt b <span class="nb">+ </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">do-things</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">c</span> <span class="nv">d</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> calc-c calc-d <span class="k">;
</span></span></span></code></pre></div><p>Let’s try and build real syntax that allows this hypothetical syntax to work.</p>
<h3 id="building-the-syntax">Building the Syntax</h3>
<p>First, we make a tuple to hold a lazy variable binding:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">lazy</span> <span class="nv">token</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">C:</span> <span class="nf"><lazy></span> <span class="nc">lazy</span>
</span></span></code></pre></div><p>Then, we need a way to generate temporary syntax words in a similar manner to
<a href="https://docs.factorcode.org/content/word-define-temp,words.html">temporary
words</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">define-temp-syntax</span> <span class="nf">( </span><span class="nv">quot</span> <span class="nf">-- </span><span class="nv">word</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ gensym <span class="nb">dup </span>] <span class="nb">dip </span>define-syntax <span class="k">;
</span></span></span></code></pre></div><p>We create temporary syntax words to convert each named references to lazy
variables:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">make-lazy-vars</span> <span class="nf">( </span><span class="nv">names</span> <span class="nf">-- </span><span class="nv">words</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>'[ _ <lazy> <span class="nb">suffix! </span>] define-temp-syntax ] H{ } <span class="nb">map>assoc </span><span class="k">;
</span></span></span></code></pre></div><p>Given a
<a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a> that
we have parsed in an <em>emit</em> description, we can build a word to replace all
these lazy variables by looking them up in the current <a href="https://docs.factorcode.org/content/word-manifest,vocabs.parser.html">vocabulary
manifest</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">replace-lazy-vars</span> <span class="nf">( </span><span class="nv">quot</span> <span class="nf">-- </span><span class="nv">quot'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>lazy? [ token>> parse-word ] <span class="nb">when </span>] deep-map <span class="k">;
</span></span></span></code></pre></div><p>And, finally, create our <em>emit</em> syntax word that parses a definition, making
lazy variables that are then replaced when the <em>emit</em> word is called in the
nested scope:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYNTAX: </span>EMIT:
</span></span><span class="line"><span class="cl"> scan-new-word scan-effect in>>
</span></span><span class="line"><span class="cl"> [ make-lazy-vars ] with-compilation-unit
</span></span><span class="line"><span class="cl"> [ parse-definition ] with-words
</span></span><span class="line"><span class="cl"> '[ _ replace-lazy-vars <span class="nb">append! </span>] define-syntax <span class="k">;
</span></span></span></code></pre></div><h3 id="using-the-syntax">Using the Syntax</h3>
<p>Now, let’s go back to our original example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">EMIT: calc-c <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">c</span> <span class="nf">) </span>a b sqrt <span class="nb">* </span><span class="k">;
</span></span></span><span class="line"><span class="cl">EMIT: calc-d <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">d</span> <span class="nf">) </span>a sqrt b <span class="nb">+ </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">do-things</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">c</span> <span class="nv">d</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> calc-c calc-d <span class="k">;
</span></span></span></code></pre></div><p>Does it work?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1 2 </span>do-things
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- Data stack:
</span></span><span class="line"><span class="cl"><span class="m">1.4142135623730951
</span></span></span><span class="line"><span class="cl"><span class="m">3.0
</span></span></span></code></pre></div><p>Yep! That’s kind of a neat thing to build.</p>
<p>I have added this syntax in the <a href="https://github.com/factor/factor/blob/master/extra/locals/lazy/lazy.factor">locals.lazy
vocabulary</a>,
if you want to try it out.</p>
<p>I’m not sure how useful it will be in general, but it is always fun to build
something new with <a href="https://factorcode.org">Factor</a>!</p>
Battlesnake
https://re.factorcode.org/2024/09/battlesnake.html
Sun, 15 Sep 2024 08:00:00 -0700https://re.factorcode.org/2024/09/battlesnake.html<p><a href="https://play.battlesnake.com">Battlesnake</a> is <em>“a competitive game where
your code is the controller”</em>. In particular, in answering the question
“<a href="https://docs.battlesnake.com">What is Battlesnake?</a>”, the documentation
says:</p>
<blockquote>
<p>In this game, each Battlesnake is controlled in real-time by a live web
server, responding to the <a href="https://docs.battlesnake.com/api">Battlesnake API</a>.
It navigates the game board based on your algorithm, trying to find food,
avoid other Battlesnakes, and survive as long as possible. Battlesnakes
can be built using any tech stack you’d like, and we encourage you to step
outside of your comfort zone.</p>
</blockquote>
<p>It is also a very neat set of episodes of “Coding Badly” from almost two
years ago that talks about building <em>battlesnakes</em> using
<a href="https://factorcode.org">Factor</a>. In particular, they use a live-coding
style to explore the <a href="https://docs.factorcode.org/content/article-ui-tools.html">development
environment</a>,
build web servers using the <a href="https://docs.factorcode.org/content/article-furnace.html">furnace web
framework</a>, and
learn how to use and deploy their program!</p>
<p>I did not know about these videos until today, but I thought it makes a nice
series to share with the world. I love it when people build things using
<a href="https://factorcode.org">Factor</a> and am always glad to hear about it!</p>
<p>More information is also available on the
<a href="https://github.com/BattlesnakeOfficial">@BattlesnakeOfficial</a> GitHub
organization, as well as an <a href="https://github.com/joenash/factor-snake">archive of the Coding Badly
implementation</a> and a different
<a href="https://github.com/gifti258/factor-battlesnake">Factor battlesnake library</a>
by another contributor.</p>
<h3 id="episode-1">Episode 1</h3>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/sqHY3YDGj80?autoplay=0&controls=1&end=0&loop=0&mute=0&start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
</div>
<h3 id="episode-2">Episode 2</h3>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/tPXAmEsS_6I?autoplay=0&controls=1&end=0&loop=0&mute=0&start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
</div>
<h3 id="episode-3">Episode 3</h3>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/9cti_7OLrsw?autoplay=0&controls=1&end=0&loop=0&mute=0&start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
</div>
Factor 0.100 now available
https://re.factorcode.org/2024/09/factor-0-100-now-available.html
Wed, 11 Sep 2024 08:00:00 -0700https://re.factorcode.org/2024/09/factor-0-100-now-available.html<p><em>“Life can only be understood backwards; but it must be lived forwards.” —
Kierkegaard</em></p>
<p>I’m very pleased to announce the release of <a href="https://factorcode.org">Factor</a>
0.100!</p>
<table class="downloads" cellspacing="0">
<colgroup>
<col style="width: 25%" />
<col style="width: 25%" />
<col style="width: 25%" />
<col style="width: 25%" />
</colgroup>
<thead>
<tr class="header">
<th class="nobg" style="text-align: center;">OS/CPU</th>
<th class="bg" style="text-align: center;" scope="col">Windows</th>
<th class="bg" style="text-align: center;" scope="col">Mac OS</th>
<th class="bg" style="text-align: center;" scope="col">Linux</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<th class="bg" style="text-align: center;" scope="row">x86</th>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.100/factor-windows-x86-32-0.100.zip">0.100</a>
</td>
<td class="doesnotexist">
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.100/factor-linux-x86-32-0.100.tar.gz">0.100</a>
</td>
</tr>
<tr class="even">
<th class="bg" style="text-align: center;" scope="row">x86-64</th>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.100/factor-windows-x86-64-0.100.zip">0.100</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.100/factor-macos-x86-64-0.100.dmg">0.100</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.100/factor-linux-x86-64-0.100.tar.gz">0.100</a>
</td>
</tr>
</tbody>
</table>
<p><strong>Source code</strong>: <a
href="https://downloads.factorcode.org/releases/0.100/factor-src-0.100.zip"
class="release">0.100</a></p>
<p>This release is brought to you with over 1400 commits by the following individuals:</p>
<blockquote>
<p>Aditya Aryaman Das, Alex <code>null</code> Maestas, Alexander Ilin, Andy Kluger,
Bhargav Shirin Nalamati, Charlie Weismann, Dave Carlton, David Enders, Doug
Coleman, Evgenii Petrov, Giftpflanze, Ikko Eltociear Ashimine, J. Ryan
Stinnett, Jean-Marc Lugrin, John Benediktsson, Keldan Chapman, Limnanthes
Serafini, Marc Michael, Michael Raitzam, Michael Thies, Pragya Pant, Raghu
Ranganathan, Rebecca Kelly, Rudi Grinberg, Sandesh Pyakurel, Sebastian
Strobl, Shruti Sen, Surav Shrestha, Val Packett,
<a href="https://github.com/Capital-Ex">@Capital-EX</a>,
<a href="https://github.com/Smoothieewastaken">@Smoothieewastaken</a>,
<a href="https://github.com/TheWitheredStriker">@TheWitheredStriker</a>,
<a href="https://github.com/TriedAngle">@TryAngle</a>,
<a href="https://github.com/chunes3">@chunes3</a>,
<a href="https://github.com/inivekin">@inivekin</a>,
<a href="https://github.com/nomennescio">@nomennescio</a>,
<a href="https://github.com/olus2000">@olus2000</a>.</p>
</blockquote>
<p>Besides some bug fixes and library improvements, I want to highlight the following changes:</p>
<ul>
<li>Upgraded to Unicode 15.1</li>
<li>Fix some <code>xmlns</code> that were accidentally changed to <code>https</code></li>
<li>Improved the printing of shortest decimal representation of floating-point numbers</li>
<li>Some early support for ARM64 in the non-optimizing compiler, more to do for full support</li>
<li>Automatic light/dark theme detection works on Microsoft Windows</li>
<li>Support for compressed images, useful when reducing file size is important</li>
</ul>
<p>Some possible backwards compatibility issues:</p>
<ul>
<li>ui: <code>focusable-child*</code> now returns <code>f</code> to indicate parent should be focused</li>
<li>peg: change to compile-time <code>PEG:</code> and <code>PARTIAL-PEG:</code> forms, not delay to first invocation</li>
<li>system: renamed <code>macosx</code> to <code>macos</code></li>
<li>math.trig: moved <code>deg>rad</code> and <code>rad>deg</code> to math.functions vocabulary</li>
<li>math.functions: fix <code>divisor?</code> to support mixed numbers (floats and integers)</li>
<li>math.functions.integer-logs: moved <code>integer-log10</code> and <code>integer-log2</code> to math.functions vocabulary</li>
<li>ranges: fixed exclusive range to be more correct for non-integer types</li>
<li>http.client: moved some download words to <code>http.download</code> vocabulary</li>
<li>rosetta-code: moved solutions to the <a href="https://github.com/factor/factor-rosetta-code/">factor-rosetta-code</a> git repository</li>
<li>json: <code>read-json</code> returns a single object, use <code>read-jsons</code> to read multiple</li>
<li>base32: now contains all of the words from the <code>base32-crockford</code> and <code>base32hex</code> vocabularies</li>
</ul>
<p>I would also like to bring particular recognition to Raghu Ranganathan, also
known as <a href="https://github.com/razetime">@razetime</a>, who was an incredible
developer with an incredibly good attitude and contributing member to many
technical communities including code golfing and various programming languages
including Factor. We are very sad that <a href="https://codegolf.meta.stackexchange.com/questions/26416/in-memory-of-razetime">he passed
away</a>
a couple of months ago and would like to have this moment dedicated in his
memory.</p>
<h3 id="what-is-factor">What is Factor</h3>
<p>Factor is a <a href="https://www.concatenative.org/">concatenative</a>, stack-based
programming language with <a href="https://concatenative.org/wiki/view/Factor/Features/The%20language">high-level
features</a>
including dynamic types, extensible syntax, macros, and garbage
collection. On a practical side, Factor has a <a href="https://docs.factorcode.org/content/article-vocab-index.html">full-featured
library</a>,
supports many different platforms, and has been extensively documented.</p>
<p>The implementation is <a href="https://concatenative.org/wiki/view/Factor/Optimizing%20compiler">fully
compiled</a>
for performance, while still supporting <a href="https://concatenative.org/wiki/view/Factor/Interactive%20development">interactive
development</a>.
Factor applications are portable between all common platforms. Factor
can <a href="https://concatenative.org/wiki/view/Factor/Deployment">deploy stand-alone
applications</a> on
all platforms. Full source code for the Factor project is available
under a BSD license.</p>
<h3 id="new-libraries">New libraries:</h3>
<ul>
<li><a href="https://docs.factorcode.org/content/vocab-base45.html">base45</a>: adding support for <a href="https://datatracker.ietf.org/doc/html/rfc9285.html">Base45 Data Encoding (RFC 9285)</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-bend.html">bend</a>: features from the <a href="https://github.com/HigherOrderCO/Bend">Bend programming language</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-checksums.khash.html">checksums.khash</a>: implementation of <a href="https://github.com/Keith-Cancel/k-hash">k-hash</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-command-line.parser.html">command-line.parser</a>: experimental command-line <a href="https://re.factorcode.org/2024/05/argument-parser.html">argument parser</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-containers.html">containers</a>: experimental high-level container words across sequences, sets, dlists, and assocs.</li>
<li><a href="https://docs.factorcode.org/content/vocab-drunken-bishop.html">drunken-bishop</a>: implements <a href="https://re.factorcode.org/2023/08/drunken-bishop.html">OpenSSH Drunken Bishop algorithm</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.chime.html">editors.chime</a>: support <a href="https://www.chimehq.com">Chime</a> editor</li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.focus.html">editors.focus</a>: support <a href="https://focus-editor.dev">Focus Editor</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.notepadnext.html">editors.notepadnext</a>: support <a href="https://github.com/dail8859/NotepadNext">NotepadNext</a> editor</li>
<li><a href="https://docs.factorcode.org/content/vocab-gilded-rose.html">gilded-rose</a>: adds the <a href="https://github.com/emilybache/GildedRose-Refactoring-Kata">Gilded Rose Refactoring Kata</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-golden-section.html">golden-section</a>: recovers an old UI demo that uses processing for rendering</li>
<li><a href="https://docs.factorcode.org/content/vocab-hex-strings.html">hex-strings</a>: words for changing bytes to hex strings and back</li>
<li><a href="https://docs.factorcode.org/content/vocab-http.download.html">http.download</a>: collection of various <code>download</code> words</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.tee.html">io.streams.tee</a>: adding a “tee’ing” stream utility</li>
<li><a href="https://docs.factorcode.org/content/vocab-images.viewer.scaling.html">images.viewer.scaling</a>: adding a “scaling image” gadget</li>
<li><a href="https://docs.factorcode.org/content/vocab-kaggle.html">kaggle</a>: wrapper for the <a href="https://www.kaggle.com/docs/api">Kaggle API</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-lazy.html">lazy</a>: renamed from <code>promises</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-leb128.html">leb128</a>: implements <a href="https://en.wikipedia.org/wiki/LEB128">LEB128 or Little Endian Base 128</a> encoding</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.statistics.running.html">math.statistics.running</a>: implement “running statistics” and “running regression” tuples</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.vectors.ranges.html">math.vectors.ranges</a>: more efficient vector operations on range objects</li>
<li><a href="https://docs.factorcode.org/content/vocab-models.combinators.html">models.combinators</a>: some extensions to <a href="https://docs.factorcode.org/content/article-models.html">models</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-models.combinators.templates.html">models.combinators.templates</a>: a functor for generating model objects</li>
<li><a href="https://docs.factorcode.org/content/vocab-persistency.html">persistency</a>: simple wrapper for <a href="https://docs.factorcode.org/content/article-db-tuples.html">db.tuples</a>, possibly a little redundant</li>
<li><a href="https://docs.factorcode.org/content/vocab-project-euler.061.html">project-euler.061</a>: solution for <a href="https://projecteuler.net/problem=61">Cyclical Figurate Numbers</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-project-euler.098.html">project-euler.098</a>: solution for <a href="https://projecteuler.net/problem=98">Anagramic Squares</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-quoted-printable.rfc2047.html">quoted-printable.rfc2047</a>: implementation of <a href="https://datatracker.ietf.org/doc/html/rfc2047">RFC2047</a> strings</li>
<li><a href="https://docs.factorcode.org/content/vocab-random.xorshift.html">random.xorshift</a>: simple <a href="https://en.wikipedia.org/wiki/Xorshift">Xorshift</a> random number generator</li>
<li><a href="https://docs.factorcode.org/content/vocab-recipes.html">recipes</a>: recovers an old UI demo of a recipe browser</li>
<li><a href="https://docs.factorcode.org/content/vocab-scryfall.html">scryfall</a>: extensive library for working with MtG cards on <a href="https://scryfall.com">Scryfall</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.prefixed.html">sequences.prefixed</a>: virtual “prefixed” sequence</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.suffixed.html">sequences.suffixed</a>: virtual “suffixed” sequence</li>
<li><a href="https://docs.factorcode.org/content/vocab-stomp.html">stomp</a>: client library for the <a href="https://stomp.github.io">STOMP protocol</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-stomp.cli.html">stomp.cli</a>: command-line interface for <a href="https://re.factorcode.org/2024/05/stomp.html">STOMP</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-sudokus.html">sudokus</a>: a graphical Sudoku game and solver</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.image.html">tools.image</a>: common code for working with Factor image files</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.image.analyzer.html">tools.image.analyzer</a>: renamed <code>tools.image-analyzer</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.image.compressor.html">tools.image.compressor</a>: working with compressed Factor image files</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.image.uncompressor.html">tools.image.uncompressor</a>: working with compressed Factor image files</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.gadgets.alert.html">ui.gadgets.alert</a>: UI elements to prompt user and popup alerts</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.gadgets.cartesian.html">ui.gadgets.cartesian</a>: three-axis coordinate system</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.gadgets.comboboxes.html">ui.gadgets.comboboxes</a>: UI elements for a combo box</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.gadgets.controls.html">ui.gadgets.controls</a>: recovers some old UI support code</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.gadgets.layout.html">ui.gadgets.layout</a>: recovers some old UI support code</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.gadgets.slate.html">ui.gadgets.slate</a>: recovers some old UI support code</li>
<li><a href="https://docs.factorcode.org/content/vocab-msgpack.rpc.html">msgpack.rpc</a>: implementation of <a href="https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md">MsgPack-RPC</a></li>
</ul>
<h3 id="improved-libraries">Improved libraries:</h3>
<ul>
<li><a href="https://docs.factorcode.org/content/vocab-alien.c-types.html">alien.c-types</a>: added <code>ssize_t</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-alien.data.html">alien.data</a>: adding <code>stream-read-c-ptr</code> and <code>read-c-ptr</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-assocs.html">assocs</a>: merged some useful words from <code>assocs.extras</code>, removed <code>with-assoc</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-assocs.extras.html">assocs.extras</a>: demoted <code>set-of</code> from <code>assocs</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-base85.html">base85</a>: added Ascii85, Adobe85, and Z85 variants</li>
<li><a href="https://docs.factorcode.org/content/vocab-calendar.html">calendar</a>: added <code>sunrise</code>, <code>sunset</code>, and <code>solar-noon</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-classes.algebra.html">classes.algebra</a>: fix <code>class<=</code> for <code>anonymous-predicate</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-cocoa.statusbar.html">cocoa.statusbar</a>: some cleanup, additional words</li>
<li><a href="https://docs.factorcode.org/content/vocab-codebase-analyzer.html">codebase-analyzer</a>: handle owner and version files, print clickable links</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.contrast.html">colors.contrast</a>: adding <code>contrast-text-color</code> to select white/black text on dark/light backgrounds</li>
<li><a href="https://docs.factorcode.org/content/vocab-combinators.extras.html">combinators.extras</a>: adding <code>sequence-case</code>, fix <code>3tri*</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-command-line.html">command-line</a>: adding <code>command-line-options</code> for easy options parsing</li>
<li><a href="https://docs.factorcode.org/content/vocab-contributors.html">contributors</a>: added <code>contributors.</code> and make the changelog respect <code>.mailmap</code> file</li>
<li><a href="https://docs.factorcode.org/content/vocab-crontab.html">crontab</a>: adding support for “~” randomization</li>
<li><a href="https://docs.factorcode.org/content/vocab-curses.html">curses</a>: added <code>nodelay</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-db.tuples.html">db.tuples</a>: adding <code>LIKE" column"</code> syntax</li>
<li><a href="https://docs.factorcode.org/content/vocab-debugger.html">debugger</a>: fixed error description for <code>bad-escape</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-discord.html">discord</a>: some improvements to the <a href="https://discord.com/developers/docs/intro">Discord bot</a> library</li>
<li><a href="https://docs.factorcode.org/content/vocab-dns.html">dns</a>: adding some more DNS types</li>
<li><a href="https://docs.factorcode.org/content/vocab-formatting.html">formatting</a>: split out a <code>format-directive</code> EBNF word</li>
<li><a href="https://docs.factorcode.org/content/vocab-game-of-life.html">game-of-life</a>: small performance improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-geo-ip.html">geo-ip</a>: fix database download link</li>
<li><a href="https://docs.factorcode.org/content/vocab-github.html">github</a>: implement more of the <a href="https://docs.github.com/en/rest">GitHub REST API</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-globs.html">globs</a>: added <code>rglob</code> for recursive glob</li>
<li><a href="https://docs.factorcode.org/content/vocab-gpu.shaders.html">gpu.shaders</a>: change two tuples used in errors to be <a href="https://docs.factorcode.org/content/word-ERROR__colon__,syntax.html">error classes</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-html.templates.chloe.html">html.templates.chloe</a>: improve <code><t:meta></code> tag to be able to specific any <a href="https://www.w3schools.com/tags/tag_meta.asp">meta attributes</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-http.server.responses.html">http.server.responses</a>: adding all the real and <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418">joke response codes</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-http2.hpack.huffman.html">http2.hpack.huffman</a>: simplify implementation using <code>nths</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-interpolate.html">interpolate</a>: adding <code>I"</code> interpolated string syntax, <a href="2024/06/interpolate-formatting.html">allow format directives</a> to be used</li>
<li><a href="https://docs.factorcode.org/content/vocab-inverse.html">inverse</a>: adding <code>under</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-io.directories.html">io.directories</a>: added <code>?move-file</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-io.directories.windows.html">io.directories.windows</a>: fixed <code>move-file</code> to properly replace existing files</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.files.html">io.files</a>: added <code>if-file-exists</code> combinators and <code>(file-writer-secure)</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-io.files.temp.macosx.html">io.files.temp.macosx</a>: allow <code>default-cache-directory</code> to work in <a href="https://www.macports.org">MacPorts</a> environment</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.files.unique.html">io.files.unique</a>: added <code>safe-replace-file</code> and <code>safe-modify-file</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-io.pipes.html">io.pipes</a>: added <code><connected-pair></code></li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.256color.html">io.streams.256color</a>: formatting ANSI string tables properly</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.ansi.html">io.streams.ansi</a>: formatting ANSI string tables properly</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.escape-codes.html">io.streams.escape-codes</a>: added <code>strip-ansi-escapes</code> and <code>format-ansi-tables</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-json.html">json</a>: renamed <code>read-json</code> to <code>read-jsons</code>, added <code>read-json</code> that reads a single object</li>
<li><a href="https://docs.factorcode.org/content/vocab-ldcache.html">ldcache</a>: fix to work on platforms that don’t have <code>/etc/ld.so.cache</code> (such as NixOS)</li>
<li><a href="https://docs.factorcode.org/content/vocab-libc.html">libc</a>: added some <code>setlocale</code> support</li>
<li><a href="https://docs.factorcode.org/content/vocab-libclang.html">libclang</a>: working on header parsing to factor source</li>
<li><a href="https://docs.factorcode.org/content/vocab-machine-learning.functions.html">machine-learning.functions</a>: added <code>gelu</code>, <code>stable-softmax</code>, and <code>stable-log-softmax</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-mason.release.archive.html">mason.release.archive</a>: build tar file without xattrs</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.bits.html">math.bits</a>: adding a <code>binary-bits</code> tuple</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.combinatorics.html">math.combinatorics</a>: fix <code><k-permutations></code> for k=0</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.distances.html">math.distances</a>: added <code>squared-euclidian-distance</code> and <code>normalized-squared-euclidian-distance</code>, aliases for <code>taxicab-distance</code> and <code>chessboard-distance</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.extras.html">math.extras</a>: added <code>weighted-randoms-as</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.functions.html">math.functions</a>: moved math.trig words in (<code>deg>rad</code> and <code>rad>deg</code>), math.functions.integer-logs, and <code>integer-sqrt</code>, added <code>fma</code> (fused-multiply-add)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.matrices.html">math.matrices</a>: fixed stack effect for <code><matrix-by></code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.parser.html">math.parser</a>: implement <a href="https://re.factorcode.org/2024/02/dragonbox.html">Dragonbox algorithm</a>, added <code>>digits</code> and <code>digits></code></li>
<li><a href="https://docs.factorcode.org/content/vocab-mirrors.html">mirrors</a>: changed to display all assocs like hashtables, all sets like hash-sets.</li>
<li><a href="https://docs.factorcode.org/content/vocab-modern.html.html">modern.html</a>: added some XML traversal works</li>
<li><a href="https://docs.factorcode.org/content/vocab-msgpack.html">msgpack</a>: added <code>?read-msgpack</code> and <code>read-msgpacks</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-oauth2.html">oauth2</a>: simplify using the <code>json.http</code> vocabulary</li>
<li><a href="https://docs.factorcode.org/content/vocab-openai.html">openai</a>: adding <code><cheapest-chat-completion></code> for ease-of-use with “gpt-4o-mini”, add timestamps to the list-models api</li>
<li><a href="https://docs.factorcode.org/content/vocab-opengl.html">opengl</a>: adding OpenGL 4+ extensions and demos, better support for <code>multi-texture</code> scaling</li>
<li><a href="https://docs.factorcode.org/content/vocab-peg.ebnf.html">peg.ebnf</a>: reset ebnf words properly</li>
<li><a href="https://docs.factorcode.org/content/vocab-pong.html">pong</a>: fix issue when pressing <code>N</code> for new game a lot</li>
<li><a href="https://docs.factorcode.org/content/vocab-prettyprint.config.html">prettyprint.config</a>: adding <code>qualified-names?</code> to allow word names to be prettyprinted as fully-qualified</li>
<li><a href="https://docs.factorcode.org/content/vocab-quotations.html">quotations</a>: added <code>compose-all</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-random.html">random</a>: rename <code>random-bits*</code> to <code>random-bits-exact</code>, rename the <code>*-random-float</code> distributions to <code>*-random</code>, add <code>*-distribution</code> types, added more of them, defined a <code>base-random</code> that allows a better <code>not-a-random-generator</code> error to be produced in some cases</li>
<li><a href="https://docs.factorcode.org/content/vocab-random.c.html">random.c</a>: fixed to guarantee <code>rand()</code> is used to generate full range of 32-bit numbers</li>
<li><a href="https://docs.factorcode.org/content/vocab-ranges.html">ranges</a>: support some set operations more efficiently</li>
<li><a href="https://docs.factorcode.org/content/vocab-raylib.html">raylib</a>: added extensive documentation</li>
<li><a href="https://docs.factorcode.org/content/vocab-reddit.html">reddit</a>: fix <code>domain-stats</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.extras.html">sequences.extras</a>: adding <code>count=</code>, faster <code>longest-subseq</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.generalizations.html">sequences.generalizations</a>: adding <code>lastn</code>, <code>?lastn</code>, <code>set-lastn</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.parser.html">sequences.parser</a>: rename <code>get+increment</code> to <code>consume</code>, and change <code>next</code> to return the next element</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.product.html">sequences.product</a>: making <code>product-each</code>, <code>product-map</code>, and <code>product-find</code> significantly faster</li>
<li><a href="https://docs.factorcode.org/content/vocab-serialize.html">serialize</a>: added <code>deep-clone</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-shuffle.html">shuffle</a>: added <code>dupdd</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-splitting.html">splitting</a>: break on more line separators <code>\r\n\v\f\x1c\x1d\x1e\x85\u002028\u002029</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-system.html">system</a>: renamed <code>macosx</code> to <code>macos</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-syntax.html">syntax</a>: adding <code>VOCAB:</code> syntax</li>
<li><a href="https://docs.factorcode.org/content/vocab-toml.html">toml</a>: fix issue with sub-tables being defined first</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.completion.html">tools.completion</a>: added completion for <code>VOCAB:</code> syntax</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.disassembler.capstone.html">tools.disassembler.capstone</a>: support Capstone 4 and Capstone 5</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.html">ui</a>: adding <code>close-all-windows</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.backend.gtk2.html">ui.backend.gtk2</a>: check for non-empty <code>DISPLAY</code> os-var for graphical capability</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.backend.windows.html">ui.backend.windows</a>: support horizontal mouse wheel, detect light/dark theme on startup</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.backend.x11.html">ui.backend.x11</a>: check for non-empty <code>DISPLAY</code> os-var for graphical capability</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.commands.html">ui.commands</a>: adding <code>update-command-map</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.gadgets.html">ui.gadgets</a>: fix <code>f focusable-child</code> busy loop</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.listener.history.html">ui.tools.listener.history</a>: saving and restoring UI listener history to a <code>~/.factor-history</code> file</li>
<li><a href="https://docs.factorcode.org/content/vocab-units.imperial.html">units.imperial</a>: add area units, change “fingerbreadth” unit</li>
<li><a href="https://docs.factorcode.org/content/vocab-units.si.html">units.si</a>: adding <code>km^2</code> and more aliases</li>
<li><a href="https://docs.factorcode.org/content/vocab-vocabs.loader.html">vocabs.loader</a>: make <code>vocab-exists?</code> no longer throw <code>bad-vocab-name</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-vocabs.refresh.monitor.html">vocabs.refresh.monitor</a>: fix warnings for non-vocab-like files</li>
<li><a href="https://docs.factorcode.org/content/vocab-xml.html">xml</a>: fix xml namespace</li>
<li><a href="https://docs.factorcode.org/content/vocab-webapps.mason.html">webapps.mason</a>: improve <a href="https://builds.factorcode.org/dashboard">build farm dashboard</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-websites.concatenative.html">websites.concatenative</a>: allow use of <code><t:meta></code> in child templates</li>
<li><a href="https://docs.factorcode.org/content/vocab-words.html">words</a>: adding <code>uninterned-word</code> predicate, <code>undefined-word</code> error class</li>
<li><a href="https://docs.factorcode.org/content/vocab-xmode.catalog.html">xmode.catalog</a>: adding <code>qdoc</code> and <code>sparql</code> modes</li>
<li><a href="https://docs.factorcode.org/content/vocab-zeromq.html">zeromq</a>: change <code>zmq-error</code> to be an error class</li>
<li><a href="https://docs.factorcode.org/content/vocab-zoneinfo.html">zoneinfo</a>: updated to tzdata version <code>2024b</code></li>
</ul>
<h3 id="vm-improvements">VM Improvements:</h3>
<ul>
<li>Improved ARM64 bootstrap assembly to allow small forms to execute
successfully and natively in the non-optimizing compiler. This continues to
be a work-in-progress to fully support ARM64.</li>
<li>Tentative <a href="https://re.factorcode.org/2024/05/compressed-images.html">support for compressed images</a>,
allowing Factor images to be as much as <strong>8x</strong> smaller in size with run-time
uncompression overhead.</li>
<li>Improved console I/O on windows to work in environments such as cygwin and
GitHub Actions.</li>
</ul>
Cash Register
https://re.factorcode.org/2024/08/cash-register.html
Thu, 08 Aug 2024 05:00:00 -0700https://re.factorcode.org/2024/08/cash-register.html<p>Building a “cash register” is an often used example project, from places like
the <a href="https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/javascript-algorithms-and-data-structures-projects/cash-register">freeCodeCamp’s Javascript Algorithms and Data Structures Project “Cash
Register”</a>
or <a href="https://www.codecademy.com/forum_questions/5407f855282ae35fc700033e">codecademy’s “Building a Cash
Register”</a>
along with other examples like the <a href="https://github.com/kurushdubash/simple-python-cash-register/blob/master/Cash%20Register.py">Simple Cash Register in
Python</a>.</p>
<p>I thought it would be fun to write about building something similar, but not
the same, in <a href="https://factorcode.org">Factor</a>.</p>
<p>We are going to make a few assumptions:</p>
<ol>
<li>We handle one currency – the <a href="https://en.wikipedia.org/wiki/United_States_dollar">“buck”</a>.</li>
<li>We can make change in various units – from the
<a href="https://en.wikipedia.org/wiki/Penny_(United_States_coin)">penny</a> to the
<a href="https://en.wikipedia.org/wiki/United_States_one-hundred-dollar_bill#:~:text=The%20bills%20are%20also%20commonly,on%20the%20blue%20tint%20of">Benjamin</a>.</li>
<li>Despite still being <a href="https://en.wikipedia.org/wiki/Legal_tender">legal
tender</a>, we do not support <a href="https://www.usa.gov/currency#:~:text=American%20paper%20currency%20comes%20in,may%20still%20be%20in%20circulation.">$500,
$1,000, $5,000, or $10,000
bills</a>.</li>
<li>Despite being rare, we include the <a href="https://en.wikipedia.org/wiki/United_States_two-dollar_bill">two-dollar bill</a>.</li>
</ol>
<p>Here are our units of change along with their descriptions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">COINS</span> {
</span></span><span class="line"><span class="cl"> { <span class="m">10000 </span><span class="s">"$100"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">5000 </span><span class="s">"$50"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">2000 </span><span class="s">"$20"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">1000 </span><span class="s">"$10"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">500 </span><span class="s">"$5"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">200 </span><span class="s">"$2"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">100 </span><span class="s">"$1"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">25 </span><span class="s">"quarters"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">10 </span><span class="s">"dimes"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">5 </span><span class="s">"nickels"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">1 </span><span class="s">"pennies"</span> }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>If we want to <a href="https://www.merriam-webster.com/dictionary/make%20change">make
change</a>, we can
generate it using something like the <a href="https://www.geeksforgeeks.org/greedy-algorithm-to-find-minimum-number-of-coins/">greedy algorithm to find minimum number
of
coins</a>,
starting with the largest denomination possible and iterating to smaller ones:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">make-change</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> COINS [ [ <span class="nb">/mod swap </span>] <span class="nb">dip </span>] <span class="nb">assoc-map swap </span><span class="m">0 </span><span class="nb">assert= </span><span class="k">;
</span></span></span></code></pre></div><p>For convenience, we can make a formatting word to format our coins into dollars:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">$.</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="m">100 </span><span class="nb">/f </span><span class="s">"$%.2f\n"</span> printf <span class="k">;
</span></span></span></code></pre></div><p>And now a word to print out the change we made:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">change.</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"CHANGE: "</span> <span class="nb">write dup </span>$. make-change [
</span></span><span class="line"><span class="cl"> '[ _ <span class="s">"%d of %s\n"</span> printf ] <span class="nb">unless-zero
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">assoc-each </span><span class="k">;
</span></span></span></code></pre></div><p>We can store the amount <em>owed</em> and the amount <em>paid</em> in <a href="https://docs.factorcode.org/content/article-namespaces.html">dynamic variables</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">INITIALIZED-SYMBOL: owed [ <span class="m">0 </span>]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">INITIALIZED-SYMBOL: paid [ <span class="m">0 </span>]
</span></span></code></pre></div><p>Using that, we can make a word to display the balance due:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">balance.</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"OWED: "</span> <span class="nb">write </span>owed <span class="nb">get-global </span>$.
</span></span><span class="line"><span class="cl"> <span class="s">"PAID: "</span> <span class="nb">write </span>paid <span class="nb">get-global </span>$. <span class="k">;
</span></span></span></code></pre></div><p>A word to add a charge, increasing the amount <em>owed</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">charge</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"CHARGE: "</span> <span class="nb">write dup </span>$.
</span></span><span class="line"><span class="cl"> owed [ <span class="nb">+ </span>] <span class="nb">change-global </span>balance. <span class="k">;
</span></span></span></code></pre></div><p>A word to make a payment, providing change if the amount <em>paid</em> is greater than
the amount <em>owed</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pay</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"PAY: "</span> <span class="nb">write dup </span>$.
</span></span><span class="line"><span class="cl"> paid [ <span class="nb">+ </span>] <span class="nb">change-global </span>balance.
</span></span><span class="line"><span class="cl"> paid <span class="nb">get-global </span>owed <span class="nb">get-global - dup </span><span class="m">0 </span><span class="nb">>=
</span></span></span><span class="line"><span class="cl"> [ change. <span class="m">0 </span>owed <span class="nb">set-global </span><span class="m">0 </span>paid <span class="nb">set-global </span>] [ <span class="nb">drop </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>And a word to cancel a transaction, refunding any paid amounts:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">cancel</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"CANCEL"</span> <span class="nb">print
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>owed <span class="nb">set-global
</span></span></span><span class="line"><span class="cl"> paid [ change. <span class="m">0 </span>] <span class="nb">change-global </span><span class="k">;
</span></span></span></code></pre></div><p>Using a word that parses input into a number of pennies:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-$</span> <span class="nf">( </span><span class="nv">args</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"$"</span> ?head <span class="nb">drop </span>string>number <span class="m">100 </span><span class="nb">* </span>round <span class="nb">>integer </span><span class="k">;
</span></span></span></code></pre></div><p>We can then define a set of commands using the <a href="https://docs.factorcode.org/content/vocab-command-loop.html">command-loop
vocabulary</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">COMMANDS</span> {
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"balance"</span> }
</span></span><span class="line"><span class="cl"> { quot [ <span class="nb">drop </span>balance. ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Display current balance."</span> }
</span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">"b"</span> } } }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"charge"</span> }
</span></span><span class="line"><span class="cl"> { quot [ parse-$ charge ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Charge an item."</span> }
</span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">"c"</span> } } }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"pay"</span> }
</span></span><span class="line"><span class="cl"> { quot [ parse-$ pay ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Pay with money."</span> }
</span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">"p"</span> } } }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"cancel"</span> }
</span></span><span class="line"><span class="cl"> { quot [ <span class="nb">drop </span>cancel ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Cancel transaction."</span> }
</span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">"x"</span> } } }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>And then define the loop that we run as
<a href="https://docs.factorcode.org/content/word-MAIN__colon__,syntax.html">MAIN</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">cash-register-main</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"Welcome to the Cash Register!"</span> <span class="s">"$>"</span>
</span></span><span class="line"><span class="cl"> command-loop new-command-loop
</span></span><span class="line"><span class="cl"> COMMANDS [ <span class="nb">over </span>add-command ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> run-command-loop <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">cash-register-main</span>
</span></span></code></pre></div><p>And you can see an example from running it:</p>
<pre tabindex="0"><code>Welcome to the Cash Register!
$> c 10.23
CHARGE: $10.23
OWED: $10.23
PAID: $0.00
$> c 15.37
CHARGE: $15.37
OWED: $25.60
PAID: $0.00
$> p 100.00
PAY: $100.00
OWED: $25.60
PAID: $100.00
CHANGE: $74.40
1 of $50
1 of $20
2 of $2
1 of quarters
1 of dimes
1 of nickels
</code></pre><p>It could be fun to extend this example to have an inventory of purchasable
items, allow users to ring up these items instead of a series of charges,
maybe implement taxable items and discounts, display and print receipts,
handle refunds, handle available bills and coins when making change, support
other currencies, and other features that you might find in a more
“complete” or “real-world” cash register.</p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/cash-register/cash-register.factor">GitHub</a>.</p>
Reflecting on 20 Years
https://re.factorcode.org/2024/08/reflecting-on-20-years.html
Fri, 02 Aug 2024 14:00:00 -0700https://re.factorcode.org/2024/08/reflecting-on-20-years.html<p>As close as I can tell, <a href="https://factorcode.org">Factor</a> is the result of
contributions from around 180 contributors over the past 20 years.</p>
<p>Recently, I was reminded of a tool that can produce graphs showing some
aspects of contributions to <a href="https://git-scm.com">git repositories</a>. The
tool is called <a href="https://github.com/erikbern/git-of-theseus">Git of Theseus</a>
written in the <a href="https://python.org">Python programming language</a>, which can
be used to generate a series of interesting plots showing statistics over
time about a project. A similar tool is
<a href="https://github.com/src-d/hercules">Hercules</a>, which claims to be a bit
faster and is written in the <a href="https://go.dev">Go programming language</a>.</p>
<p>We can look at code written in each year which, aside from the first few
years, mostly continues to exist in the latest version. In addition, we see
a healthy and increasing chart over time:</p>
<p>
<img src="https://re.factorcode.org/images/2024-08-02-stack_plot_cohorts.png" alt="" width="960" height="720" />
</p>
<p>When wondering about the <a href="https://erikbern.com/2016/12/05/the-half-life-of-code.html">half-life of
code</a>, we might
want to see how long a particular line of code continues to exist in the
project. We can see that 50% of them are still existing after 5 years:</p>
<p>
<img src="https://re.factorcode.org/images/2024-08-02-survival_plot.png" alt="" width="650" height="400" />
</p>
<p>Many of those early contributions came from <a href="https://factorcode.org/slava/">Slava
Pestov</a>, <a href="https://github.com/erg">Doug
Coleman</a>, <a href="https://bluishcoder.co.nz">Chris Double</a>,
and others, and we continue to benefit from the impressive early work that
they did for the <a href="https://factorcode.org">Factor programming language</a>. We
can <a href="https://erikbern.com/2018/01/03/plotting-author-statistics-for-git-repos-using-git-of-theseus.html">plot author
statistics</a>,
and see the large blocks of contributions over time by <a href="https://docs.factorcode.org/content/article-vocab-authors.html">various
authors</a>:</p>
<p>
<img src="https://re.factorcode.org/images/2024-08-02-stack_plot_authors.png" alt="" width="960" height="720" />
</p>
<p>And, as a percentage of lines of code, see that beginning with almost 100%
of the source code contributed by Slava Pestov, more recently we have around
20% each from Slava Pestov, Doug Coleman, and myself as well as several
other significant authors:</p>
<p>
<img src="https://re.factorcode.org/images/2024-08-02-line_plot.png" alt="" width="960" height="720" />
</p>
<p>We can generate a more detailed breakdown of these lines of code by language
using <a href="https://github.com/XAMPPRocky/tokei">tokei</a>, but in particular see
that we have over 388,000 lines of Factor source code in our latest
<a href="https://github.com/factor/factor">development version</a>:</p>
<pre tabindex="0"><code>$ tokei .
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Language Files Lines Code Comments Blanks
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Factor 4952 503452 388845 25717 88890
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
</code></pre><p>It was pretty great to generate these graphs, and to reminisce about all the
<a href="https://docs.factorcode.org/content/article-vocab-index.html">vocabularies</a>
available so far, and ponder all those still yet to be written.</p>
<p>Happy coding!</p>
Deploy Issues on MacOS
https://re.factorcode.org/2024/07/deploy-issues-on-macos.html
Thu, 18 Jul 2024 03:00:00 -0700https://re.factorcode.org/2024/07/deploy-issues-on-macos.html<p>While trying to help get the <a href="https://re.factorcode.org/2024/07/bitguessr.html">BitGuessr</a> game
deployed on <a href="https://www.apple.com/macos/">macOS</a>, I ran into a few issues
that were interesting, and I wanted to discuss the process of
troubleshooting them.</p>
<p>Sometimes, <a href="https://docs.factorcode.org/content/article-tools.deploy.usage.html">using the deploy
tool</a>
is easy, and sometimes it is not-so-easy. There are some challenges around
choosing the right level of reflection for the features used in the
application you are trying to deploy – we suggest starting with <code>Full environment</code> and then reducing until the program breaks – but besides that
it is typically one command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"bitguessr"</span> deploy
</span></span></code></pre></div><p>That command results in a per-platform executable, which on
<a href="https://www.apple.com/macos">macOS</a> is an <code>.app</code> bundle that includes the
Factor executable, a deployed image, any resources the deployed image uses, and
any libraries that the deployed image depends on:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ find bitguessr.app
</span></span><span class="line"><span class="cl">bitguessr.app
</span></span><span class="line"><span class="cl">bitguessr.app/Contents
</span></span><span class="line"><span class="cl">bitguessr.app/Contents/Frameworks
</span></span><span class="line"><span class="cl">bitguessr.app/Contents/Frameworks/libraylib.dylib
</span></span><span class="line"><span class="cl">bitguessr.app/Contents/Info.plist
</span></span><span class="line"><span class="cl">bitguessr.app/Contents/MacOS
</span></span><span class="line"><span class="cl">bitguessr.app/Contents/MacOS/bitguessr
</span></span><span class="line"><span class="cl">bitguessr.app/Contents/Resources
</span></span><span class="line"><span class="cl">bitguessr.app/Contents/Resources/bitguessr
</span></span><span class="line"><span class="cl">bitguessr.app/Contents/Resources/bitguessr/_resources
</span></span><span class="line"><span class="cl">bitguessr.app/Contents/Resources/bitguessr/_resources/bitguessr_icon.png
</span></span><span class="line"><span class="cl">bitguessr.app/Contents/Resources/bitguessr/_resources/bitguessr_soundtrack.wav
</span></span><span class="line"><span class="cl">bitguessr.app/Contents/Resources/bitguessr/_resources/button-0.png
</span></span><span class="line"><span class="cl">bitguessr.app/Contents/Resources/bitguessr/_resources/button-1.png
</span></span><span class="line"><span class="cl">bitguessr.app/Contents/Resources/bitguessr/_resources/correct.wav
</span></span><span class="line"><span class="cl">bitguessr.app/Contents/Resources/bitguessr/_resources/wrong.wav
</span></span><span class="line"><span class="cl">bitguessr.app/Contents/Resources/bitguessr.image
</span></span><span class="line"><span class="cl">bitguessr.app/Contents/Resources/Icon.icns
</span></span></code></pre></div><p>After building this application, checking that it <em>works for me</em>, and
uploading it to the server, of course we got a bug report when someone else
tried to run it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ ./bitguessr.app/Contents/MacOS/bitguessr
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">Cannot resolve C library <span class="k">function</span>
</span></span><span class="line"><span class="cl">Library: DLL<span class="s2">" libraylib.dylib"</span>
</span></span><span class="line"><span class="cl">Symbol: InitWindow
</span></span><span class="line"><span class="cl">DlError: none
</span></span><span class="line"><span class="cl">See https://concatenative.org/wiki/view/Factor/Requirements
</span></span></code></pre></div><p>Is the <code>InitWindow</code> symbol in the library?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ nm -gU ./bitguessr.app/Contents/Frameworks/libraylib.dylib <span class="p">|</span> grep InitWindow
</span></span><span class="line"><span class="cl"><span class="m">0000000000017920</span> T _InitWindow
</span></span></code></pre></div><p>Yes, it is.</p>
<p>Is it loading the correct <code>libraylib.dylib</code> file?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ <span class="nv">DYLD_PRINT_LIBRARIES</span><span class="o">=</span><span class="m">1</span> ./bitguessr.app/Contents/MacOS/bitguessr
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">dyld<span class="o">[</span>69951<span class="o">]</span>: <B5534AF8-58E9-3F59-A5DE-F33164570F6B> ./bitguessr.app/Contents/Frameworks/libraylib.dylib
</span></span></code></pre></div><p>Yes, it seems to be.</p>
<p>Let’s learn more about how dynamic libraries work. There is a nice thread on
<a href="https://forums.developer.apple.com/forums/thread/736719">dynamic library
identification</a>
that goes into some details about how these are identified and then loaded.</p>
<p>Let’s start with the library – we get our <a href="https://raylib.com">Raylib</a> from
<a href="https://brew.sh">Homebrew</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ <span class="nb">cd</span> <span class="k">$(</span>brew --prefix raylib<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ otool -l libraylib.dylib <span class="p">|</span> grep -A <span class="m">2</span> LC_ID_DYLIB
</span></span><span class="line"><span class="cl"> cmd LC_ID_DYLIB
</span></span><span class="line"><span class="cl"> cmdsize <span class="m">72</span>
</span></span><span class="line"><span class="cl"> name /usr/local/opt/raylib/lib/libraylib.450.dylib <span class="o">(</span>offset 24<span class="o">)</span>
</span></span></code></pre></div><p>Okay, so this probably needs to be relative to a “runtime path” or <code>rpath</code>,
which you can either set:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ install_name_tool -id <span class="s2">"@rpath/libraylib.dylib"</span> libraylib.dylib
</span></span></code></pre></div><p>Or, fix by downloading a <a href="https://github.com/raysan5/raylib/releases">Raylib
release</a> that is already set
properly for embedding.</p>
<p>Did it change?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ otool -l libraylib.dylib <span class="p">|</span> grep -A <span class="m">2</span> LC_ID_DYLIB
</span></span><span class="line"><span class="cl"> cmd LC_ID_DYLIB
</span></span><span class="line"><span class="cl"> cmdsize <span class="m">48</span>
</span></span><span class="line"><span class="cl"> name @rpath/libraylib.dylib <span class="o">(</span>offset 24<span class="o">)</span>
</span></span></code></pre></div><p>Yes, it did!</p>
<p>Now that we have that, we can re-deploy and see if it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ ./bitguessr.app/Contents/MacOS/bitguessr
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">Cannot resolve C library <span class="k">function</span>
</span></span><span class="line"><span class="cl">Library: DLL<span class="s2">" libraylib.dylib"</span>
</span></span><span class="line"><span class="cl">Symbol: InitWindow
</span></span><span class="line"><span class="cl">DlError: none
</span></span><span class="line"><span class="cl">See https://concatenative.org/wiki/view/Factor/Requirements
</span></span></code></pre></div><p>Nope.</p>
<p>Okay, maybe the <code>rpath</code> that is used to lookup dynamic libraries isn’t set
properly:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ otool -l ./bitguessr.app/Contents/MacOS/bitguessr<span class="p">|</span> grep -A <span class="m">2</span> LC_RPATH
</span></span></code></pre></div><p>Hmm, it is not set at all. The dynamic linker maintains a list of these
“runtime path” directories. Maybe we can make sure it looks in the right
place by adding one:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ <span class="nb">cd</span> ./bitguessr.app/Contents/MacOS
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ install_name_tool -add_rpath <span class="s2">"@executable_path/../Frameworks"</span> bitguessr
</span></span></code></pre></div><p>Okay, now it looks right:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ otool -l ./bitguessr.app/Contents/MacOS/bitguessr <span class="p">|</span> grep -A <span class="m">2</span> LC_RPATH
</span></span><span class="line"><span class="cl"> cmd LC_RPATH
</span></span><span class="line"><span class="cl"> cmdsize <span class="m">48</span>
</span></span><span class="line"><span class="cl"> path @executable_path/../Frameworks <span class="o">(</span>offset 12<span class="o">)</span>
</span></span></code></pre></div><p>Let’s try again… and, it works!</p>
<p>I pushed a change to <a href="https://github.com/factor/factor/commit/b33764294bba34b5a27a2581e290f57e85a2dd58">set the rpath
directory</a>
properly for future deploys, changed to distributing it as an <a href="https://en.wikipedia.org/wiki/Apple_Disk_Image">Apple Disk
image</a>, and also made sure to
<a href="https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html">codesign the
application</a>
so that it launches easily after being downloaded and gave <a href="https://github.com/JosephOziel">Joseph
Oziel</a> an updated macOS build of
<a href="https://josephoziel.itch.io/bitguessr">BitGuessr</a> which included an
<code>Icons.icns</code> file in the <a href="https://en.wikipedia.org/wiki/Apple_Icon_Image_format">Apple Icon Image
format</a> for the
application icon.</p>
<p>Neat!</p>
Rosetta Code Downloaded
https://re.factorcode.org/2024/07/rosetta-code-downloaded.html
Wed, 17 Jul 2024 07:00:00 -0700https://re.factorcode.org/2024/07/rosetta-code-downloaded.html<p>I have been quite distracted by <a href="https://rosettacode.org">Rosetta Code</a>
shenanigans in the past few days. For anyone that is curious about the
backstory, I <a href="https://re.factorcode.org/2024/07/rosetta-code.html">removed the rosetta-code
vocabulary</a> from the main
<a href="https://factorcode.org">Factor</a> git repository, and then decided to
<a href="https://re.factorcode.org/2024/07/rosetta-code-revisited.html">archive all the Factor solutions</a> to
a separate
<a href="https://github.com/factor/factor-rosetta-code/">factor-rosetta-code</a>
repository for posterity, utility, and analysis.</p>
<p>I thought it would be fun to talk about different ways to do <a href="https://en.wikipedia.org/wiki/Web_scraping">web
scraping</a> for the <a href="https://rosettacode.org">Rosetta Code
website</a>, ultimately choosing to write a
<a href="https://factorcode.org">Factor</a> vocabulary to do it, which I’ll go into below.</p>
<h3 id="public-datasets">Public Datasets</h3>
<p>There are a couple public datasets and scraper tools that you can use:</p>
<ol>
<li><a href="https://huggingface.co">Hugging Face</a> has a
<a href="https://huggingface.co/datasets/christopher/rosetta-code">@christopher/rosetta-code</a>
dataset, but it looks like it was updated “<em>about 2 years ago</em>”, so perhaps
doesn’t contain recently contributed solutions.</li>
<li>The <a href="https://github.com/acmeism/RosettaCodeData">@acmeism/RosettaCodeData</a>
repository seems to be quite up-to-date and uses a <a href="https://metacpan.org/pod/RosettaCode">RosettaCode CPAN
module</a> and the <a href="https://www.mediawiki.org/wiki/API">MediaWiki
API</a> to synchronize their data files
periodically. At the moment, this is over 600 MB to clone.</li>
<li>The
<a href="https://github.com/brollb/rosetta-code-scraper">@brollb/rosetta-code-scraper</a>
repository is a Rust scraper that uses the
<a href="https://docs.rs/reqwest/latest/reqwest/">reqwest</a> and
<a href="https://docs.rs/scraper/latest/scraper/">scraper</a> crates to parse the web
pages and extract task descriptions and solutions. This required some minor
tweaks to get working with a recent <a href="https://www.rust-lang.org">Rust</a>
version, and had some issues with the newer HTML being generated.</li>
</ol>
<h3 id="using-factor">Using Factor</h3>
<p>I started with the previous approaches, but then I realized that I kinda wanted
to build my own program that only grabbed the <a href="https://factorcode.org">Factor</a>
solutions and weaved them into solution files with the task description for the
<a href="https://github.com/factor/factor-rosetta-code/">factor-rosetta-code</a>
repository.</p>
<p>After considering using the rendered <a href="https://en.wikipedia.org/wiki/HTML">HTML</a>
from the <a href="https://rosettacode.org">Rosetta Code</a> website, I decided it would be
a lot simpler to use the <a href="https://www.mediawiki.org/wiki/API">MediaWiki API</a>
and our <a href="https://docs.factorcode.org/content/vocab-mediawiki.api.html">mediawiki.api
vocabulary</a> to
extract the tasks. That vocabulary requires an <code>endpoint</code> to be specified, so
we define a simple combinator that sets it before running a
<a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">with-rosetta-code</span> <span class="nf">( </span><span class="nv">quot</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"https://rosettacode.org/w/api.php"</span> endpoint ] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> <span class="nb">with-variable </span><span class="k">; inline
</span></span></span></code></pre></div><p>The <a href="https://rosettacode.org/wiki/Category:Solutions_by_Programming_Task">Rosetta Code
solutions</a>
consists of a list of pages as well as sub-categories with their own lists of
pages. We need a <code>list-category</code> word that will get the members of a given
category, <a href="https://docs.factorcode.org/content/article-memoize.html">memoized</a>
in case pages reference each other or to speed up subsequent calls through
caching:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MEMO:</span> <span class="nf">list-category</span> <span class="nf">( </span><span class="nv">title</span> <span class="nf">-- </span><span class="nv">members</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> 'H{ { <span class="s">"list"</span> <span class="s">"categorymembers"</span> } { <span class="s">"cmtitle"</span> _ } }
</span></span><span class="line"><span class="cl"> query [ <span class="s">"title"</span> <span class="nb">of </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>And a <code>list-categories</code> word that will recursively resolve categories
containing other categories:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">list-categories</span> <span class="nf">( </span><span class="nv">title</span> <span class="nf">-- </span><span class="nv">tasks</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> list-category [ <span class="s">"Category:"</span> <span class="nb">head? </span>] <span class="nb">partition swap
</span></span></span><span class="line"><span class="cl"> [ list-categories ] <span class="nb">map concat append harvest </span>members sort <span class="k">;
</span></span></span></code></pre></div><p>Using these, we can retrieve all
<a href="https://rosettacode.org/wiki/Category:Solutions_by_Programming_Task">tasks</a>
and <a href="https://rosettacode.org/wiki/Category:Draft_Programming_Tasks">draft
tasks</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-tasks</span> <span class="nf">( -- </span><span class="nv">tasks</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"Category:Solutions_by_Programming_Task"</span> list-categories <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">draft-tasks</span> <span class="nf">( -- </span><span class="nv">tasks</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"Category:Draft_Programming_Tasks"</span> list-categories <span class="k">;
</span></span></span></code></pre></div><p>Each <em>task page</em> is a series of sections, beginning with the task description,
and then a series of solutions in different programming languages. Using
<a href="https://docs.factorcode.org/content/word-page-content,mediawiki.api.html">page-content</a>,
we can see what one of these pages looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"Sieve_of_Eratosthenes"</span> page-content ] with-rosetta-code
</span></span></code></pre></div><p>We can build a word that extracts a section that is specified by a <code>begin</code>
text and an <code>end</code> text, searching for them using
<a href="https://docs.factorcode.org/content/word-subseq-index,sequences.html">subseq-index</a>
to find where they occur in the page:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">extract-section</span> <span class="nf">( </span><span class="nv">page</span> <span class="nv">begin</span> <span class="nv">end</span> <span class="nf">-- </span><span class="nv">section/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> page begin subseq-index [
</span></span><span class="line"><span class="cl"> begin <span class="nb">length +
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>page end subseq-index-from
</span></span><span class="line"><span class="cl"> [ page <span class="nb">length </span>] <span class="nb">unless*
</span></span></span><span class="line"><span class="cl"> page <span class="nb">subseq
</span></span></span><span class="line"><span class="cl"> ] [ <span class="no">f </span>] <span class="nb">if* </span><span class="k">;
</span></span></span></code></pre></div><p>The description is everything before the first header section:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">get-description</span> <span class="nf">( </span><span class="nv">page</span> <span class="nf">-- </span><span class="nv">description/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"=={{header"</span> <span class="nb">over subseq? </span>[
</span></span><span class="line"><span class="cl"> <span class="s">""</span> <span class="s">"=={{header"</span> extract-section
</span></span><span class="line"><span class="cl"> ] [ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>The solution code is the first <code><syntaxhighlight></code> block for our desired
language:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">get-code</span> <span class="nf">( </span><span class="nv">page</span> <span class="nv">lang</span> <span class="nf">-- </span><span class="nv">code/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"<syntaxhighlight lang=\""</span> <span class="s">"\">"</span> <span class="nb">surround
</span></span></span><span class="line"><span class="cl"> <span class="s">"</syntaxhighlight>"</span> extract-section <span class="k">;
</span></span></span></code></pre></div><p>We can use those words to weave the commented-out description with the
<a href="https://factorcode.org">Factor</a> source code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">get-solution</span> <span class="nf">( </span><span class="nv">task</span> <span class="nf">-- </span><span class="nv">solution/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> page-content [ get-description ] <span class="nb">keep over empty?
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">2drop </span><span class="no">f </span>] [
</span></span><span class="line"><span class="cl"> [ string-lines [ <span class="s">"! "</span> <span class="nb">prepend </span>] <span class="nb">map </span><span class="s">"\n"</span> <span class="nb">join </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"factor"</span> get-code <span class="s">"\n\n"</span> <span class="nb">glue </span><span class="s">"\n"</span> <span class="nb">append </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>That works great, you can try it by printing out one of the draft tasks:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"10001th_prime"</span> get-solution <span class="nb">print </span>] with-rosetta-code
</span></span><span class="line"><span class="cl"><span class="c">! Task:</span>
</span></span><span class="line"><span class="cl"><span class="c">!
</span></span></span><span class="line"><span class="cl"><span class="c">! Find and show on this page the 10001st prime number.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">math</span> <span class="nn">math.primes</span> <span class="nn">prettyprint</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="m">2 10,000 </span>[ next-prime ] <span class="nb">times </span><span class="m">.
</span></span></span></code></pre></div><p>Now we want a way to save a task, and since the tasks have names that aren’t
all valid in filenames or vocabulary names, we do a little cleanup to turn a
task name into a path:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">task-path</span> <span class="nf">( </span><span class="nv">task</span> <span class="nf">-- </span><span class="nv">path</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>{ [ Letter? ] [ digit? ] } 1|| [ <span class="nb">drop </span><span class="sc">CHAR: - </span>] <span class="nb">unless </span>] <span class="nb">map
</span></span></span><span class="line"><span class="cl"> >lower R/ --+/ <span class="s">"-"</span> re-replace [ <span class="sc">CHAR: - </span><span class="nb">= </span>] <span class="nb">trim </span><span class="s">".factor"</span> <span class="nb">append </span><span class="k">;
</span></span></span></code></pre></div><p>Saving a task is getting the solution and then saving to a file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">save-task</span> <span class="nf">( </span><span class="nv">task</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"vocab:rosetta-code/solutions"</span> [
</span></span><span class="line"><span class="cl"> [ get-solution ]
</span></span><span class="line"><span class="cl"> [ task-path '[ _ utf8 set-file-contents ] <span class="nb">when* </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] with-directory <span class="k">;
</span></span></span></code></pre></div><p>With that, we can finally save all the tasks, or all the draft tasks:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">save-all-tasks</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> all-tasks [ save-task ] <span class="nb">each </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">save-draft-tasks</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> draft-tasks [ save-task ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>I used this, with some minor changes to ignore certain categories that do not
contain solutions, as well as using <a href="https://pandoc.org">Pandoc</a> to convert the
<a href="https://www.mediawiki.org/wiki/Help:Formatting">MediaWiki markup</a> before
embedding in the solution files.</p>
<p>Anyway, pretty cool!</p>
Rosetta Code Revisited
https://re.factorcode.org/2024/07/rosetta-code-revisited.html
Tue, 16 Jul 2024 07:00:00 -0700https://re.factorcode.org/2024/07/rosetta-code-revisited.html<p>I recently wrote about <a href="https://re.factorcode.org/2024/07/rosetta-code.html">removing the Rosetta Code
solutions</a> from our <a href="https://github.com/factor/factor">main Factor
repository</a>. We only had 62 solutions out
of 1,276
<a href="https://planet.factorcode.org/*https://rosettacode.org/wiki/Category:Solutions_by_Programming_Task">tasks</a>,
and I didn’t really want to maintain a subset of the solutions, nor mirror
them ineffectively into the main git repository.</p>
<p>As it turns out – and pretty much immediately afterwards – I got curious
enough to try and download all of the Factor solutions so we could maybe do
some analysis of all the various solutions that have been contributed to the
<a href="https://rosettacode.org/">Rosetta Code</a> project. And, it’s not a small
amount of code – it’s 12,461 lines of Factor code!</p>
<pre tabindex="0"><code>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Language Files Lines Code Comments Blanks
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Factor 1663 74221 12461 55885 5875
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
</code></pre><p>Not all of those are fully solved, but almost 1,000 of them seem to be!</p>
<p>This is available in the newly created
<a href="https://github.com/factor/factor-rosetta-code/">factor-rosetta-code</a> git
repository, if anyone else is as curious as I was. I don’t think we are
going to be able to consistently keep this in sync with the <a href="https://rosettacode.org/">Rosetta Code
website</a>, but at least it represents a checkpoint
today for quite a lot of nice <a href="https://factorcode.org">Factor</a> source code.</p>
<p>Some of these were written for older versions of Factor, but most of it is
usable as is, or with minor edits.</p>
<p>Check it out!</p>
Rosetta Code
https://re.factorcode.org/2024/07/rosetta-code.html
Sun, 14 Jul 2024 15:00:00 -0700https://re.factorcode.org/2024/07/rosetta-code.html<p>The <a href="https://rosettacode.org">Rosetta Code</a> project is quite interesting:</p>
<blockquote>
<p>The idea is to present solutions to the same task in as many different
languages as possible, to demonstrate how languages are similar and
different, and to aid a person with a grounding in one approach to a problem
in learning another. Rosetta Code currently has 1,276
<a href="*https://rosettacode.org/wiki/Category:Solutions_by_Programming_Task">tasks</a>,
401 <a href="https://rosettacode.org/wiki/Category:Draft_Programming_Tasks">draft
tasks</a>, and
is aware of 948
<a href="https://rosettacode.org/wiki/Category:Programming_Languages">languages</a>,
though we do not (and cannot) have solutions to every task in every
language.</p>
</blockquote>
<p>There is a <a href="https://rosettacode.org/wiki/Category:Factor">page for Factor</a>
with some programming language history, notes, and a list of <a href="https://rosettacode.org/wiki/Tasks_not_implemented_in_Factor">tasks not
implemented in
Factor</a>. If
you are new to learning Factor, this might be a fun way to learn and
contribute.</p>
<p>We had been maintaining a <code>rosetta-code</code> vocabulary in the <a href="https://github.com/factor/factor">main
development repository</a> with various
solutions. However, I noticed recently that it had only 62 solutions, and
was not being kept in sync with the ones on their website where many more
were available. We <a href="https://github.com/factor/factor/issues/2842">debated moving
these</a> and the general feeling
was if we aren’t able to maintain the “authoritative” solutions, it would be
best to move this code to the <a href="https://github.com/factor/factor-unmaintained">factor-unmaintained
repository</a> where we keep old
source code that we aren’t actively maintaining.</p>
<p>That move is now complete, but don’t let that stop you from <a href="https://rosettacode.org/wiki/Tasks_not_implemented_in_Factor">solving the
unsolved
ones</a>!</p>
BitGuessr
https://re.factorcode.org/2024/07/bitguessr.html
Sat, 13 Jul 2024 15:00:00 -0700https://re.factorcode.org/2024/07/bitguessr.html<p>The <a href="https://www.raylib.com">Raylib</a> library is a “<em>simple and easy-to-use
library to enjoy videogames programming</em>” that has seen an increasing
popularity in making quick visual demos and games.</p>
<p>We released support for it in <a href="https://re.factorcode.org/2023/08/factor-0-99-now-available.html">Factor
0.99</a> and have been keeping it
up-to-date. Currently, we support <a href="https://github.com/raysan5/raylib/releases/tag/4.5.0">version
4.5</a> – although I just
noticed that <a href="https://github.com/raysan5/raylib/releases/tag/5.0">version 5.0</a>
came out somewhat recently and seems to represent an exciting future that we
should probably update to soon.</p>
<p><a href="https://github.com/JosephOziel">Joseph Oziel</a>, one of our recent contributors,
was learning <a href="https://factorcode.org">Factor</a> and
<a href="https://www.raylib.com">Raylib</a> by making a fun little game called
<a href="https://josephoziel.itch.io/bitguessr">BitGuessr</a>. The game presents two
buttons, a <code>0</code> and a <code>1</code> and you have to guess a series of bits that are
generated randomly and try to achieve a high score! It supports keyboard and
mouse inputs and has sounds and music as well.</p>
<p>
<img src="https://re.factorcode.org/images/2024-07-13-bitguessr.png" alt="" width="868" height="546" />
</p>
<p>Pretty fun – I love this use of Factor! – and particularly neat that they have
used our <a href="https://docs.factorcode.org/content/article-tour-deploy.html">deployment
system</a> to create
standalone executables as a way of distributing it.</p>
<p>You can <a href="https://josephoziel.itch.io/bitguessr">read more about it</a> and <a href="https://github.com/JosephOziel/bitguessr">see
the source code</a> as well.</p>
<p>Check it out!</p>
Stack Complexity
https://re.factorcode.org/2024/07/stack-complexity.html
Thu, 11 Jul 2024 07:00:00 -0700https://re.factorcode.org/2024/07/stack-complexity.html<p>We had a recent discussion on the <a href="https://discord.gg/QxJYZx3QDf">Factor Discord
server</a> about how to deal with <a href="https://docs.factorcode.org/content/article-tour-stack-shuffling.html">stack
shuffling</a>,
and when and how you might use the various <a href="https://docs.factorcode.org/content/article-dataflow-combinators.html">dataflow
combinators</a>
that are available in <a href="https://factorcode.org">Factor</a>. Some of the impetus
for this discussion was my article about <a href="https://re.factorcode.org/2024/05/intersecting-ranges.html">intersecting
ranges</a> and the contrasting
implementations that it presented.</p>
<p>The
<a href="https://discord.com/channels/780615045771821076/786055699271909428/1243459371611062356">question</a>
that started it all:</p>
<blockquote>
<p>Overall very complex stack states.</p>
<p>How do you write code like that?</p>
<p>Are you in the
<a href="https://docs.factorcode.org/content/article-listener.html">listener</a> or
<a href="https://docs.factorcode.org/content/article-debugger.html">debugger</a> making
these changes one by one?</p>
<p>When I write even simple code I get lost when there’s 3+ things on the stack
I have to work with and spend more time figuring out stack order and correct
combinators than with the actual problem. I wonder if there’s a good workflow
where I can write the code iteratively.</p>
</blockquote>
<p>Let’s go over some of the advice that came up in that conversation:</p>
<h3 id="mockups">Mockups</h3>
<blockquote>
<p>I often write Factor code iteratively lately. Usually I come up with a tiny
increment that preserves the stack effect, then reload the code to compile
it.</p>
<p>For example, if I need to create a word with effect <code>( a b -- )</code>, I will
start with the minimal implementation like</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">word</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">2drop </span><span class="k">;
</span></span></span></code></pre></div><p>Mocking stuff every step of the way, compiling after each step.</p>
<p>For stuff inside a word that seems to be complex from the outset, I would
create a mock <code>(word)</code> with defined stack effect. I will inline it later if
it turns out to be simpler than expected (because I find something in the
<a href="https://docs.factorcode.org/content/article-vocab-index.html">standard
library</a> that
would do most of the work).</p>
</blockquote>
<h3 id="combinators">Combinators</h3>
<blockquote>
<p>Some things come with practice, that’s true. But some tips:</p>
<ol>
<li>Keep the stack shallow, 1-3 things and it often works best.</li>
<li>More than that and don’t worry about using named <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a>.</li>
<li>Try and use
<a href="https://docs.factorcode.org/content/article-spread-combinators.html">spread</a>
/ <a href="https://docs.factorcode.org/content/article-apply-combinators.html">apply</a>
/
<a href="https://docs.factorcode.org/content/article-cleave-combinators.html">cleave</a>
combinators to represent your logic as dataflow.</li>
</ol>
</blockquote>
<h3 id="monoliths">Monoliths</h3>
<blockquote>
<p>I like writing monoliths, so sometimes i write a <code>::</code> version first and
refactor to <code>:</code> later, especially if I don’t know how the data will flow
exactly.</p>
<p>When I’m porting code from other languages, I start with
<a href="https://docs.factorcode.org/content/article-locals.html">locals</a> to keep the
code blocks similar. Structure starts to become obvious once you have a big
block working.</p>
</blockquote>
<h3 id="practice">Practice</h3>
<blockquote>
<p>Sometimes writing inline comments showing what the stack effect is after each
block of code, is useful to visualize the stack effects of the intermediate
parts.</p>
<p>Sometimes there are words like <a href="https://docs.factorcode.org/content/article-math-vectors.html">vector
operations</a>
that clean up an iterative piece of code.</p>
<p>But mostly, keep practicing. Ask questions. Maybe get feedback on your code
from us here or other devs you like to work with.</p>
<p>And sometimes despite trying; it stays a big block of code that works, you
shrug and move on to the next thing.</p>
</blockquote>
<h3 id="variables">Variables</h3>
<blockquote>
<p>One way to reduce stack depth is, for example, to put some state on the
<a href="https://docs.factorcode.org/content/article-namespaces.html">namestack</a> as a
variable. That is how our <a href="https://docs.factorcode.org/content/article-stream-protocol.html">stream
words</a> work
(e.g., <code>read</code>, <code>write</code>, <code>print</code>). It’s how some words that accumulate
output work – either using the <a href="https://docs.factorcode.org/content/article-namespaces-make.html">make
vocabulary</a>
or an equivalent <code>vector</code> or <code>hash-set</code> variable to accumulate into.</p>
</blockquote>
<h3 id="reordering">Reordering</h3>
<blockquote>
<p>Oh, and one advanced tip: sometimes reordering the arguments on the stack to
avoid shuffles or allow using <code>with</code> / <code>map</code> / <code>curry</code> / <code>fry</code> things
helps a lot.</p>
<p>It takes a long time to “get” it, but there are some rules that help; the
order of your arguments to your words matter a lot. If you find yourself
juggling a lot, consider a different argument order of your word. Stuff new
context “under” your arguments with <code>dip</code>, e.g. a vector to collect stuff
in. Look at
<a href="https://docs.factorcode.org/content/article-accessors.html">accessors</a> and
see why they’re designed with a specific word order; typically things that
are “parameters” go on the top of the stack (e.g.
<a href="https://docs.factorcode.org/content/article-quotations.html">quotations</a>).
Also <code>assocs</code> have a certain logical order to them. If you understand that,
it becomes more natural, with the occasional <a href="https://docs.factorcode.org/content/article-tour-stack-shuffling.html">stack
shuffle</a>
still needed.</p>
<p>And don’t forget about
<a href="https://docs.factorcode.org/content/article-tuples.html">tuples</a>; that
quickly reduces the amount of things on the stack</p>
</blockquote>
<p>Good luck and happy coding!</p>
Random Distributions
https://re.factorcode.org/2024/07/random-distributions.html
Tue, 09 Jul 2024 10:00:00 -0700https://re.factorcode.org/2024/07/random-distributions.html<p>As was the case when I got distracted by <a href="https://re.factorcode.org/2013/10/color-support.html">color
support</a>, I recently was distracted by random
<a href="https://en.wikipedia.org/wiki/Probability_distribution">probability
distributions</a>.
Other programming languages have these – for example the
<a href="https://www.python.org">Python</a> module
<a href="https://numpy.org/doc/stable/reference/random/legacy.html">numpy.random</a> as
well as the <a href="https://julialang.org">Julia</a> module
<a href="https://juliastats.org/Distributions.jl/stable/">Distributions.jl</a>.</p>
<p>In particular, I wanted to make sure we supported a bunch of the commonly used
distributions, both continuous and discrete. I have added a few recently, and
we now support quite a few in the <a href="https://docs.factorcode.org/content/vocab-random.html">random
vocabulary</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">bernoulli-distribution</span> <span class="nv">p</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">beta-distribution</span> <span class="nv">alpha</span> <span class="nv">beta</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">binomial-distribution</span> <span class="nv">n</span> <span class="nv">p</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">cauchy-distribution</span> <span class="nv">median</span> <span class="nv">scale</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">chi-square-distribution</span> <span class="nv">dof</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">exponential-distribution</span> <span class="nv">lambda</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">f-distribution</span> <span class="nv">dof-num</span> <span class="nv">dof-den</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">gamma-distribution</span> <span class="nv">alpha</span> <span class="nv">beta</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">geometric-distribution</span> <span class="nv">p</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">gumbel-distribution</span> <span class="nv">loc</span> <span class="nv">scale</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">inv-gamma-distribution</span> <span class="nv">shape</span> <span class="nv">scale</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">laplace-distribution</span> <span class="nv">mean</span> <span class="nv">scale</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">logistic-distribution</span> <span class="nv">loc</span> <span class="nv">scale</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">lognormal-distribution</span> < <span class="nc">normal-distribution</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">logseries-distribution</span> <span class="nv">p</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">normal-distribution</span> <span class="nv">mean</span> <span class="nv">sigma</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">pareto-distribution</span> <span class="nv">k</span> <span class="nv">alpha</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">poisson-distribution</span> <span class="nv">mean</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">power-distribution</span> <span class="nv">alpha</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">rayleigh-distribution</span> <span class="nv">mode</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">student-t-distribution</span> <span class="nv">dof</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">triangular-distribution</span> <span class="nv">low</span> <span class="nv">high</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">uniform-distribution</span> <span class="nv">min</span> <span class="nv">max</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">von-mises-distribution</span> <span class="nv">mu</span> <span class="nv">kappa</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">wald-distribution</span> <span class="nv">mean</span> <span class="nv">scale</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">weibull-distribution</span> <span class="nv">alpha</span> <span class="nv">beta</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">zipf-distribution</span> <span class="nv">a</span> <span class="k">;
</span></span></span></code></pre></div><p>For each of these, we define a convenient <code>foo-random</code> word, an
implementation <code>foo-random*</code> that takes a
<a href="https://docs.factorcode.org/content/word-random-generator,random.html">random-generator</a>,
and a <code>foo-distribution</code>
<a href="https://docs.factorcode.org/content/article-tuples.html">tuple</a> that can be
used as an object to take a faster number of samples using
<a href="https://docs.factorcode.org/content/word-randoms,random.html">randoms</a> from.
For example, using the <a href="https://en.wikipedia.org/wiki/Binomial_distribution">binomial
distribution</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">100,000 </span>[ <span class="m">10 0.6 </span>binomial-random ] <span class="nb">replicate </span>histogram <span class="m">.
</span></span></span><span class="line"><span class="cl">H{
</span></span><span class="line"><span class="cl"> { <span class="m">0 21 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">1 157 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">2 1058 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">3 4228 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">4 11202 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">5 20002 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">6 25151 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">7 21537 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">8 11981 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">9 4045 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">10 618 </span>}
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">100,000
</span></span></span><span class="line"><span class="cl"> T{ binomial-distribution { n <span class="m">10 </span>} { p <span class="m">0.6 </span>} }
</span></span><span class="line"><span class="cl"> randoms histogram <span class="m">.
</span></span></span><span class="line"><span class="cl">H{
</span></span><span class="line"><span class="cl"> { <span class="m">0 6 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">1 164 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">2 1044 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">3 4255 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">4 11169 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">5 19928 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">6 25103 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">7 21414 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">8 12335 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">9 3973 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">10 609 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>I would love to get some additional per-distribution <a href="https://docs.factorcode.org/content/article-generic.html">generic
methods</a> to support
calculating things like mean, variance, skewness, kurtosis, entropy,
probability density/mass functions, etc. And, of course, would love more
distributions to be available in <a href="https://factorcode.org">Factor</a>.</p>
<p>There’s always things to add!</p>
Magic Forest
https://re.factorcode.org/2024/06/magic-forest.html
Sat, 29 Jun 2024 06:00:00 -0700https://re.factorcode.org/2024/06/magic-forest.html<p>About ten years ago, there was a blog post about a <a href="http://unriskinsight.blogspot.com/2014/04/goats-wolves-and-lions.html">Goats, Wolves, and
Lions</a>
puzzle that was <a href="http://www.kaenguru.at/uploads/media/2014_Student_EN.pdf">problem #30 from the 2014 Austrian “Math Kangaroo”
contest</a>. The puzzle
was kind of fun and the implementation prompted some decent follow-up at the
time.</p>
<blockquote>
<p>There are three species of animals in a magic forest: lions, wolves and
goats. Wolves can devour goats, and lions can devour wolves and goats. ("<em>The
stronger animal eats the weaker one</em>".) As this is a magic forest, a wolf,
after having devoured a goat, is transmuted into a lion; a lion, after having
devoured a goat, is transmuted into a wolf; and a lion having devoured a wolf
becomes a goat.</p>
<p>At the very beginning, there are 17 goats, 55 wolves and 6 lions in the
forest. After every meal, there is one animal fewer than before; therefore
after some time, there is no devouring possible any more.</p>
<p>What is the maximum number of animals who can live in the forest then?</p>
</blockquote>
<p>There are two versions of this puzzle: one that doesn’t give any possible
answers and the kangaroo version that gives these multiple choice options:</p>
<pre tabindex="0"><code>(A) 1
(B) 6
(C) 17
(D) 23
(E) 35
</code></pre><p>The original post followed up with a description of <a href="http://unriskinsight.blogspot.com/2014/04/three-ways-to-solve-goats-wolves-and.html">three ways to solve the
Goats, Wolves, and Lions
problem</a>
and then a <a href="http://unriskinsight.blogspot.com/2014/05/the-f-word-of-programming.html">brute-force solution in
Fortran</a>
followed by a <a href="http://unriskinsight.blogspot.com/2014/06/fast-functional-goats-lions-and-wolves.html">comparison of solutions in different programming
languages</a>
of which the fastest was a C++ version and then someone else described some
<a href="https://togototo.wordpress.com/2014/06/20/the-magic-forest-problem-revisited-rehabilitating-java-with-the-aid-of-d/">experiments and improvements in some other
languages</a>.
The best answer, maybe, was the one that described using linear programming
with <a href="https://sourceforge.net/projects/lpsolve/">lpsolve</a> for an algorithmic
solution that <a href="https://algassert.com/algorithm/2014/06/07/Eating-Lions-Wolves-and-Goats-Faster.html">beats all the previous
versions</a>
and even reduces to a simple formula that you can <em>calculate by hand</em>!</p>
<p>I had created a solution in <a href="https://factorcode.org">Factor</a> at the time, but
had never written about it. Below, I want to go over that approach it and
compare it with the performance of the <em>“fastest”</em> C++ version.</p>
<p>We are going to store our <code>forest</code> state as a tuple representing the number
of goats, wolves, and lions.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">forest</span> <span class="nv">goats</span> <span class="nv">wolves</span> <span class="nv">lions</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">C:</span> <span class="nf"><forest></span> <span class="nc">forest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>forest<</span> <span class="nf">( </span><span class="nv">forest</span> <span class="nf">-- </span><span class="nv">goats</span> <span class="nv">wolves</span> <span class="nv">lions</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ goats>> ] [ wolves>> ] [ lions>> ] <span class="nb">tri </span><span class="k">;
</span></span></span></code></pre></div><p>We can build words to represent each possible next state, or <code>f</code> if that next
state is not possible:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wolf-devours-goat</span> <span class="nf">( </span><span class="nv">forest</span> <span class="nf">-- </span><span class="nv">forest/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> >forest< { [ <span class="nb">pick </span><span class="m">0 </span><span class="nb">> </span>] [ <span class="nb">over </span><span class="m">0 </span><span class="nb">> </span>] } 0&&
</span></span><span class="line"><span class="cl"> [ [ <span class="m">1 </span><span class="nb">- </span>] [ <span class="m">1 </span><span class="nb">- </span>] [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">tri* </span><forest> ] [ <span class="nb">3drop </span><span class="no">f </span>] <span class="nb">if </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">lion-devours-goat</span> <span class="nf">( </span><span class="nv">forest</span> <span class="nf">-- </span><span class="nv">forest/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> >forest< { [ <span class="nb">pick </span><span class="m">0 </span><span class="nb">> </span>] [ <span class="nb">dup </span><span class="m">0 </span><span class="nb">> </span>] } 0&&
</span></span><span class="line"><span class="cl"> [ [ <span class="m">1 </span><span class="nb">- </span>] [ <span class="m">1 </span><span class="nb">+ </span>] [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">tri* </span><forest> ] [ <span class="nb">3drop </span><span class="no">f </span>] <span class="nb">if </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">lion-devours-wolf</span> <span class="nf">( </span><span class="nv">forest</span> <span class="nf">-- </span><span class="nv">forest/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> >forest< { [ <span class="nb">dup </span><span class="m">0 </span><span class="nb">> </span>] [ <span class="nb">over </span><span class="m">0 </span><span class="nb">> </span>] } 0&&
</span></span><span class="line"><span class="cl"> [ [ <span class="m">1 </span><span class="nb">+ </span>] [ <span class="m">1 </span><span class="nb">- </span>] [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">tri* </span><forest> ] [ <span class="nb">3drop </span><span class="no">f </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>We can track the set of next states by adding them to a
<a href="https://docs.factorcode.org/content/article-sets.html">set</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-forests</span> <span class="nf">( </span><span class="nv">set</span> <span class="nv">forest</span> <span class="nf">-- </span><span class="nv">set'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ wolf-devours-goat [ <span class="nb">over </span>adjoin ] <span class="nb">when* </span>]
</span></span><span class="line"><span class="cl"> [ lion-devours-goat [ <span class="nb">over </span>adjoin ] <span class="nb">when* </span>]
</span></span><span class="line"><span class="cl"> [ lion-devours-wolf [ <span class="nb">over </span>adjoin ] <span class="nb">when* </span>] <span class="nb">tri </span><span class="k">;
</span></span></span></code></pre></div><p>Given a sequence of <code>forest</code> states, we can produce the next states after a
<em>meal</em> has occurred:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">meal</span> <span class="nf">( </span><span class="nv">forests</span> <span class="nf">-- </span><span class="nv">forests'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span><span class="m">3 </span><span class="nb">* </span><hash-set> ] <span class="nb">keep </span>[ next-forests ] <span class="nb">each </span>members <span class="k">;
</span></span></span></code></pre></div><p>A forest is stable if there are no goats and either no wolves or no lions, or
goats and no wolves and no lions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">stable?</span> <span class="nf">( </span><span class="nv">forest</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> >forest< <span class="nb">rot zero? </span>[ [ <span class="nb">zero? </span>] <span class="nb">either? </span>] [ [ <span class="nb">zero? </span>] <span class="nb">both? </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>We can say that <em>devouring is possible</em> if there are no <em>stable</em> forests:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">devouring-possible?</span> <span class="nf">( </span><span class="nv">forests</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ stable? ] none? <span class="k">;
</span></span></span></code></pre></div><p>And maybe we want to be able to get all the <em>stable</em> forests:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">stable-forests</span> <span class="nf">( </span><span class="nv">forests</span> <span class="nf">-- </span><span class="nv">stable-forests</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ stable? ] <span class="nb">filter </span><span class="k">;
</span></span></span></code></pre></div><p>So, to find our answer, we can just iterate, until we find our first <em>stable</em>
forest state:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-stable-forests</span> <span class="nf">( </span><span class="nv">forest</span> <span class="nf">-- </span><span class="nv">forests</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">1array </span>[ <span class="nb">dup </span>devouring-possible? ] [ meal ] <span class="nb">while </span>stable-forests <span class="k">;
</span></span></span></code></pre></div><p>And, now we can answer the original question – which is <code>(D)</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> T{ forest <span class="no">f </span><span class="m">17 55 6 </span>} find-stable-forests <span class="m">.
</span></span></span><span class="line"><span class="cl">{ T{ forest { goats <span class="m">0 </span>} { wolves <span class="m">0 </span>} { lions <span class="m">23 </span>} } }
</span></span></code></pre></div><p>We can compare the performance of <a href="https://factorcode.org">Factor</a> with the
<a href="http://unriskinsight.blogspot.com/2014/06/fast-functional-goats-lions-and-wolves.html">C++
version</a>
from that earlier blog post and see that ours is around <strong>5.5x</strong> slower:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ T{ forest <span class="no">f </span><span class="m">317 355 306 </span>} find-stable-forests ] time <span class="m">.
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">4.625607999 </span>seconds
</span></span><span class="line"><span class="cl">{ T{ forest { goats <span class="m">0 </span>} { wolves <span class="m">0 </span>} { lions <span class="m">623 </span>} } }
</span></span></code></pre></div><p>versus</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ <span class="nb">time</span> ./magic_forest <span class="m">317</span> <span class="m">355</span> <span class="m">306</span>
</span></span><span class="line"><span class="cl">0, 0, <span class="m">623</span>
</span></span><span class="line"><span class="cl">./magic_forest <span class="m">317</span> <span class="m">355</span> <span class="m">306</span> 0.80s user 0.04s system 99% cpu 0.848 total
</span></span></code></pre></div><p>One of the reasons for that is we’re doing a lot of generic dispatch, not
specifying types anywhere, and passing tuples around that we are accessing
and allocating frequently. We could fix some of these issues and make ours
faster…</p>
<p>But, approaching this as a linear programming exercise beats all the iterative
approaches anyway:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> T{ forest <span class="no">f </span><span class="m">17 55 6 </span>} >forest< min <span class="nb">+ </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">23
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> T{ forest <span class="no">f </span><span class="m">317 355 306 </span>} >forest< min <span class="nb">+ </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">623
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> T{ forest <span class="no">f </span><span class="m">900006 900055 900017 </span>} >forest< min <span class="nb">+ </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">1800023
</span></span></span></code></pre></div><p>Still, there are probably performance lessons to be learned here…</p>
Man or Boy
https://re.factorcode.org/2024/06/man-or-boy.html
Wed, 26 Jun 2024 08:00:00 -0700https://re.factorcode.org/2024/06/man-or-boy.html<p>The <a href="https://en.wikipedia.org/wiki/Man_or_boy_test">man or boy test</a> was
proposed by <a href="https://en.wikipedia.org/wiki/Donald_Knuth">Donald Knuth</a>:</p>
<blockquote>
<p>“There are quite a few ALGOL60 translators in existence which have been
designed to handle recursion and non-local references properly, and I thought
perhaps a little test-program may be of value. Hence I have written the
following simple routine, which may separate the man-compilers from the
boy-compilers.”</p>
<p>— Donald Knuth</p>
</blockquote>
<p>The <a href="https://rosettacode.org/wiki/Rosetta_Code">Rosetta Code</a> project has a
<a href="https://rosettacode.org/wiki/Man_or_boy_test">list of implementations</a> to
compare with, and today we are going to contribute one in
<a href="https://factorcode.org">Factor</a>. While the original was framed in <a href="https://en.wikipedia.org/wiki/ALGOL_60">ALGOL
60</a>, let’s look at one contributed in
<a href="https://python.org">Python</a>, which is probably more readable for most of the
audience:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="ch">#!/usr/bin/env python</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl"><span class="n">sys</span><span class="o">.</span><span class="n">setrecursionlimit</span><span class="p">(</span><span class="mi">1025</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">a</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">x1</span><span class="p">,</span> <span class="n">x2</span><span class="p">,</span> <span class="n">x3</span><span class="p">,</span> <span class="n">x4</span><span class="p">,</span> <span class="n">x5</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">b</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="n">b</span><span class="o">.</span><span class="n">k</span> <span class="o">-=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">a</span><span class="p">(</span><span class="n">b</span><span class="o">.</span><span class="n">k</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">x1</span><span class="p">,</span> <span class="n">x2</span><span class="p">,</span> <span class="n">x3</span><span class="p">,</span> <span class="n">x4</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">b</span><span class="o">.</span><span class="n">k</span> <span class="o">=</span> <span class="n">k</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">x4</span><span class="p">()</span> <span class="o">+</span> <span class="n">x5</span><span class="p">()</span> <span class="k">if</span> <span class="n">b</span><span class="o">.</span><span class="n">k</span> <span class="o"><=</span> <span class="mi">0</span> <span class="k">else</span> <span class="n">b</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">x</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">i</span><span class="p">:</span> <span class="k">lambda</span><span class="p">:</span> <span class="n">i</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">a</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="n">x</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="n">x</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">),</span> <span class="n">x</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">),</span> <span class="n">x</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="n">x</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span>
</span></span></code></pre></div><p>In particular, this is challenging because it involves creating a <em>“tree of
<code>B</code> call frames that refer to each other and to the containing <code>A</code> call
frames, each of which has its own copy of <code>k</code> that changes every time the
associated <code>B</code> is called.”</em> And, as a result, many languages have
difficulting calculating for large values of <code>k</code> due to recursion or depth
limits.</p>
<p>In <a href="https://factorcode.org">Factor</a>, we have the ability to create inner
computations by making
<a href="https://docs.factorcode.org/content/article-quotations.html">quotations</a>,
which is essentially an anonymous version of the inner <code>B</code> function. These
quotations are allowed to access the variables defined in their outer scope.
Thus, we can implement the word reasonably simply by making a <code>B</code> quotation,
binding it to a variable for reference, and then calling it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">a</span> <span class="nf">( </span><span class="nv">k!</span> <span class="nv">x1</span> <span class="nv">x2</span> <span class="nv">x3</span> <span class="nv">x4</span> <span class="nv">x5</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> k <span class="m">0 </span><span class="nb"><= </span>[
</span></span><span class="line"><span class="cl"> x4 call( -- n ) x5 call( -- n ) <span class="nb">+
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="no">f </span>:> b!
</span></span><span class="line"><span class="cl"> [ k <span class="m">1 </span><span class="nb">- dup </span>k! b x1 x2 x3 x4 a ] b!
</span></span><span class="line"><span class="cl"> b call( -- n )
</span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>The <code>k</code> argument is an integer, the others are quotations with stack effect
of <code>( -- n )</code>, meaning they take no arguments, and produce a number when
called. We can call it and demonstrate that it works and produces the first few
values of the <a href="https://oeis.org/A132343">output of Knuth’s “man or boy” test for varying
k</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">13 </span>[0..b] [
</span></span><span class="line"><span class="cl"> [ <span class="m">1 </span>] [ <span class="m">-1 </span>] [ <span class="m">-1 </span>] [ <span class="m">1 </span>] [ <span class="m">0 </span>] a <span class="m">.
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"><span class="m">1
</span></span></span><span class="line"><span class="cl"><span class="m">0
</span></span></span><span class="line"><span class="cl"><span class="m">-2
</span></span></span><span class="line"><span class="cl"><span class="m">0
</span></span></span><span class="line"><span class="cl"><span class="m">1
</span></span></span><span class="line"><span class="cl"><span class="m">0
</span></span></span><span class="line"><span class="cl"><span class="m">1
</span></span></span><span class="line"><span class="cl"><span class="m">-1
</span></span></span><span class="line"><span class="cl"><span class="m">-10
</span></span></span><span class="line"><span class="cl"><span class="m">-30
</span></span></span><span class="line"><span class="cl"><span class="m">-67
</span></span></span><span class="line"><span class="cl"><span class="m">-138
</span></span></span><span class="line"><span class="cl"><span class="m">-291
</span></span></span><span class="line"><span class="cl"><span class="m">-642
</span></span></span></code></pre></div><p>Presently, larger values of <code>k</code> produce a <em>“retain stack overflow”</em> with the
default Factor settings. I looked briefly into using our <a href="https://re.factorcode.org/2024/06/bend.html">bend
vocabulary</a>, but it currently doesn’t support accessing the
outer variable scope, and requires passing arguments on the stack. That would
be a nice feature, and <strong>theoretically</strong> would then look like this simple
example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">a</span> <span class="nf">( </span><span class="nv">k!</span> <span class="nv">x1</span> <span class="nv">x2</span> <span class="nv">x3</span> <span class="nv">x4</span> <span class="nv">x5</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> k <span class="m">0 </span><span class="nb"><= </span>[
</span></span><span class="line"><span class="cl"> x4 call( -- n ) x5 call( -- n ) <span class="nb">+
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> BEND[ k <span class="m">1 </span><span class="nb">- dup </span>k! [ fork ] x1 x2 x3 x4 a ]
</span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>However, that doesn’t work at the moment. Maybe sometime in the future!</p>
Quit
https://re.factorcode.org/2024/06/quit.html
Wed, 12 Jun 2024 08:00:00 -0700https://re.factorcode.org/2024/06/quit.html<p>There is a funny recurring meme that we are all living <a href="https://www.scientificamerican.com/article/confirmed-we-live-in-a-simulation/">inside a
simulation</a>
or maybe even a
<a href="https://matrix.fandom.com/wiki/Matrix_in_a_Matrix_theory">matrix-in-a-matrix</a>.
Some people have even taken this as far as selling <a href="https://www.amazon.com/Sometimes-Like-Computer-Program-t-shirt/dp/B07T52LQNY">funny computer simulation
t-shirts</a>:</p>
<p>
<img src="https://re.factorcode.org/images/2024-06-12-computer-end-program.png" alt="" width="339" height="345" />
</p>
<p>Well, I have resisted for a long time adding a special <em>end program</em> –
<code>quit</code> – command to <a href="https://factorcode.org">Factor</a>. We have always
supported <code>Ctrl-D</code> to end the listener session (which works in both the <a href="https://docs.factorcode.org/content/article-ui-listener.html">UI
listener</a> and in
the command-line on most <a href="https://en.wikipedia.org/wiki/POSIX">POSIX</a> systems).
You have also been able to <a href="https://docs.factorcode.org/content/word-exit,system.html">exit with an error
code</a>, or even to
<a href="https://docs.factorcode.org/content/word-ui-stop-after-last-window__que__,ui.backend.html">stop after the last
window</a>
has been closed. And if you really needed something, you could define your own
<em>end program</em> word in your <a href="https://docs.factorcode.org/content/article-.factor-rc.html">startup initialization
file</a>.</p>
<p>This came up again recently in a discussion with
<a href="https://github.com/nomennescio">@nomennescio</a>, one of our contributors, who
has been pushing for a <code>quit</code> command that would be kind of like the
<a href="https://python.org">Python</a> version of <a href="https://www.freecodecamp.org/news/python-end-program-how-to-exit-a-python-program-in-terminal/">How to Exit a Python Program in the
Terminal</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">Python</span> <span class="mf">3.11.9</span> <span class="p">(</span><span class="n">main</span><span class="p">,</span> <span class="n">Apr</span> <span class="mi">2</span> <span class="mi">2024</span><span class="p">,</span> <span class="mi">08</span><span class="p">:</span><span class="mi">25</span><span class="p">:</span><span class="mi">04</span><span class="p">)</span> <span class="p">[</span><span class="n">Clang</span> <span class="mf">15.0.0</span> <span class="p">(</span><span class="n">clang</span><span class="o">-</span><span class="mf">1500.3.9.4</span><span class="p">)]</span> <span class="n">on</span> <span class="n">darwin</span>
</span></span><span class="line"><span class="cl"><span class="n">Type</span> <span class="s2">"help"</span><span class="p">,</span> <span class="s2">"copyright"</span><span class="p">,</span> <span class="s2">"credits"</span> <span class="ow">or</span> <span class="s2">"license"</span> <span class="k">for</span> <span class="n">more</span> <span class="n">information</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="o">>>></span> <span class="n">quit</span>
</span></span><span class="line"><span class="cl"><span class="n">Use</span> <span class="n">quit</span><span class="p">()</span> <span class="ow">or</span> <span class="n">Ctrl</span><span class="o">-</span><span class="n">D</span> <span class="p">(</span><span class="n">i</span><span class="o">.</span><span class="n">e</span><span class="o">.</span> <span class="n">EOF</span><span class="p">)</span> <span class="n">to</span> <span class="n">exit</span>
</span></span><span class="line"><span class="cl"><span class="o">>>></span> <span class="n">quit</span><span class="p">()</span>
</span></span></code></pre></div><p>This has become more important since they added <a href="https://github.com/factor/factor/blob/938d99be04682e7cb53023de5b9bf6fe0f30afc0/basis/ui/tools/listener/listener.factor#L310C9-L310C10">saving listener
history</a>
to the <a href="https://docs.factorcode.org/content/article-ui-tools.html">UI tools</a>
(currently saved to a history file when the listener window closes). This was
paired with a couple of changes to make Factor attempt to
<a href="https://docs.factorcode.org/content/word-close-all-windows,ui.html">close-all-windows</a>
and cleanup nicely using a <a href="https://docs.factorcode.org/content/word-SHUTDOWN-HOOK__colon__,syntax.html">shutdown
hook</a>
and include the <a href="https://docs.factorcode.org/content/vocab-system.html">system
vocabulary</a> in the
default list of <a href="https://docs.factorcode.org/content/word-with-interactive-vocabs,listener.html">interactive
vocabularies</a>.</p>
<p>This means that this works now, in both the <a href="https://docs.factorcode.org/content/article-listener.html">command-line
listener</a> as well as
the <a href="https://docs.factorcode.org/content/article-ui-listener.html">UI
listener</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">Factor <span class="m">0.100 </span>x86.64 (2271, heads/master-250db4215b, Jun <span class="m">4 2024 </span>18:08:03)
</span></span><span class="line"><span class="cl">[Clang (GCC Homebrew Clang 18.1.6)] <span class="nb">on </span>macosx
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> quit
</span></span></code></pre></div><p>If you’re curious how it works, <code>quit</code> is an alias for calling <code>exit</code> with
an error code of <code>0</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">system</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">quit</span> <span class="nf">( -- </span><span class="nv">*</span> <span class="nf">) </span><span class="m">0 </span>exit <span class="k">;
</span></span></span></code></pre></div><p>This is available in a <a href="https://github.com/factor/factor">development version of
Factor</a> and will be in the next release.</p>
Bend
https://re.factorcode.org/2024/06/bend.html
Wed, 05 Jun 2024 08:00:00 -0700https://re.factorcode.org/2024/06/bend.html<p>The <a href="https://github.com/HigherOrderCO/Bend">Bend programming language</a> is a
<em>massively parallel, high-level programming language</em>. It has some really neat
dataflow concepts that make it <em>inherently parallel</em> because it infers the flow
of variables and then naturally performs <a href="https://en.wikipedia.org/wiki/Divide-and-conquer_algorithm">divide and
conquer</a> to utilize
all available computing resources, both CPU and GPU.</p>
<p>This results in some <a href="https://github.com/HigherOrderCO/Bend?tab=readme-ov-file#parallel-programming-in-bend">impressive
speedups</a>:</p>
<blockquote>
<p>Yet, since it uses a divide-and-conquer approach, which is inherently
parallel, Bend will run it multi-threaded. Some benchmarks:</p>
<p>CPU, Apple M3 Max, 1 thread: <strong>12.15 seconds</strong></p>
<p>CPU, Apple M3 Max, 16 threads: <strong>0.96 seconds</strong></p>
<p>GPU, NVIDIA RTX 4090, 16k threads: <strong>0.21 seconds</strong></p>
<p>That’s a <strong>57x speedup</strong> by doing nothing.</p>
</blockquote>
<p>One of our current challenges in <a href="https://factorcode.org">Factor</a> is that we
use <a href="https://en.wikipedia.org/wiki/Green_thread">green threads</a> and are
effectively single-threaded for computations. We have a <a href="https://docs.factorcode.org/content/article-io.ports.html">non-blocking I/O
implementation</a> as
well as integration with <a href="https://docs.factorcode.org/content/article-ui-backend-init.html">UI event
loops</a>, so it
ends up feeling more concurrent than it actually is.</p>
<p>We hope to solve that in the future, both by supporting native threads and also
by a <em>multi-vm</em> approach that is somewhat equivalent to the
<a href="https://python.org">Python</a>
<a href="https://docs.python.org/3/library/multiprocessing.html">multiprocessing</a>
module. Some <a href="https://github.com/factor/factor/blob/master/vm/mvm.cpp">early
ideas</a> have been
contributed towards this goal, but so far it is not complete.</p>
<h3 id="bend-example">Bend Example</h3>
<p>One of the Bend examples that we are going to try and implement in
<a href="https://factorcode.org">Factor</a> uses a <code>bend</code> and <code>fork</code> operations which
creates concurrency points in a kind of <a href="https://en.wikipedia.org/wiki/Fork%E2%80%93join_model">fork-join
model</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># given a shader, returns a square image</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">render</span><span class="p">(</span><span class="n">depth</span><span class="p">,</span> <span class="n">shader</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">bend</span> <span class="n">d</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">when</span> <span class="n">d</span> <span class="o"><</span> <span class="n">depth</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">color</span> <span class="o">=</span> <span class="p">(</span><span class="n">fork</span><span class="p">(</span><span class="n">d</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">i</span><span class="o">*</span><span class="mi">2</span><span class="o">+</span><span class="mi">0</span><span class="p">),</span> <span class="n">fork</span><span class="p">(</span><span class="n">d</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">i</span><span class="o">*</span><span class="mi">2</span><span class="o">+</span><span class="mi">1</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">width</span> <span class="o">=</span> <span class="n">depth</span> <span class="o">/</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"> <span class="n">color</span> <span class="o">=</span> <span class="n">shader</span><span class="p">(</span><span class="n">i</span> <span class="o">%</span> <span class="n">width</span><span class="p">,</span> <span class="n">i</span> <span class="o">/</span> <span class="n">width</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">color</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># given a position, returns a color</span>
</span></span><span class="line"><span class="cl"><span class="c1"># for this demo, it just busy loops</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">demo_shader</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">bend</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">when</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">5000</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">color</span> <span class="o">=</span> <span class="n">fork</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">color</span> <span class="o">=</span> <span class="mh">0x000001</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">color</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># renders a 256x256 image using demo_shader</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="mi">16</span><span class="p">,</span> <span class="n">demo_shader</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="factor-syntax">Factor Syntax</h3>
<p>A few days ago, <a href="https://github.com/Kacarott">Keldan Chapman</a> contributed an
early version of a <a href="https://github.com/factor/factor/commit/25104f98e71e54eb4cad5a862b30ca51db49ec9e">bend
vocabulary</a>.
And then, we spent some time working together on the <a href="https://discord.gg/QxJYZx3QDf">Factor Discord
server</a> on some alternative implementations, one
of which I want to go over below.</p>
<p>We are going to create a <code>BEND[</code> quotation that provides support for a
<code>fork</code> word that implicitly recurses (but not currently through CPU or GPU
parallelism) and then joins the results together. We create an <a href="https://docs.factorcode.org/content/word-gensym,words.html">uninterned
word</a> to hold our
computation and use
<a href="https://docs.factorcode.org/content/word-with-words,vocabs.parser.html">with-words</a>
to make the <code>fork</code> word only valid in the parsing scope to recurse.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYNTAX: </span>BEND[
</span></span><span class="line"><span class="cl"> gensym <span class="nb">dup
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"fork"</span> associate [ parse-quotation ] with-words
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>infer define-declared <span class="nb">suffix! </span><span class="k">;
</span></span></span></code></pre></div><p>A simple use-case of this might be to compute the
<a href="https://en.wikipedia.org/wiki/Factorial">factorial</a> through recursion:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">factorial</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">n!</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> BEND[ <span class="nb">dup </span><span class="m">1 </span><span class="nb">> </span>[ <span class="nb">dup </span><span class="m">1 </span><span class="nb">- </span>fork <span class="nb">* </span>] <span class="nb">when </span>] <span class="k">;
</span></span></span></code></pre></div><p>Or, perhaps computing <a href="https://en.wikipedia.org/wiki/Fibonacci_sequence">Fibonacci
numbers</a> through recursion:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fibonacci</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">fib</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> BEND[ <span class="nb">dup </span><span class="m">1 </span><span class="nb">> </span>[ [ <span class="m">1 </span><span class="nb">- </span>fork ] [ <span class="m">2 </span><span class="nb">- </span>fork ] <span class="nb">bi + </span>] <span class="nb">when </span>] <span class="k">;
</span></span></span></code></pre></div><h3 id="factor-example">Factor Example</h3>
<p>With that syntax defined, we can now translate the above example to the
following form:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">render</span> <span class="nf">( </span><span class="nv">depth</span> <span class="nv">shader</span> <span class="nf">-- </span><span class="nv">color</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 0 </span>BEND[
</span></span><span class="line"><span class="cl"> [let :> <span class="nf">( </span><span class="nv">depth</span> <span class="nv">shader</span> <span class="nv">d</span> <span class="nv">i</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> d depth <span class="nb">< </span>[
</span></span><span class="line"><span class="cl"> depth shader d <span class="m">1 </span><span class="nb">+ </span>i <span class="m">2 </span><span class="nb">* </span>fork
</span></span><span class="line"><span class="cl"> depth shader d <span class="m">1 </span><span class="nb">+ </span>i <span class="m">2 </span><span class="nb">* </span><span class="m">1 </span><span class="nb">+ </span>fork
</span></span><span class="line"><span class="cl"> <span class="nb">2array
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> i depth <span class="nb">2/ /mod swap </span>shader call( x y -- color )
</span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ]
</span></span><span class="line"><span class="cl"> ] <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">demo-shader</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nf">-- </span><span class="nv">color</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>BEND[ <span class="nb">dup </span><span class="m">5000 </span><span class="nb">< </span>[ <span class="m">1 </span><span class="nb">+ </span>fork ] [ <span class="nb">drop </span><span class="m">0x000001 </span>] <span class="nb">if </span>] <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">main</span> <span class="nf">( -- </span><span class="nv">color</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">16 </span>[ demo-shader ] render <span class="k">;
</span></span></span></code></pre></div><p>Obviously, this <code>fork</code> doesn’t (currently) increase performance, and it might
shadow the
<a href="https://docs.factorcode.org/content/word-fork,unix.process.html">fork</a> from
the <a href="https://docs.factorcode.org/content/vocab-unix.process.html">unix.process
vocabulary</a>, but
it could represent the start of a new computational approach in Factor.</p>
<p>I can’t wait to see more from the <a href="https://github.com/HigherOrderCO/Bend">Bend programming
language</a> and I also hope to see more of
these ideas appearing and improving in <a href="https://factorcode.org">Factor</a>!</p>
Interpolate Formatting
https://re.factorcode.org/2024/06/interpolate-formatting.html
Tue, 04 Jun 2024 06:00:00 -0700https://re.factorcode.org/2024/06/interpolate-formatting.html<p>I wrote almost a decade ago about some <a href="https://re.factorcode.org/2015/04/interpolate.html">minor
improvements</a> to the <a href="https://docs.factorcode.org/content/vocab-interpolate.html">interpolate
vocabulary</a> in
<a href="https://factorcode.org">Factor</a>. This vocabulary provides support for
“interpolating variable values into strings” – using words such as
<code>interpolate</code> as well as the more recently added <em>interpolate string</em> syntax
<code>I" "</code>.</p>
<p>Recently, I added support for format directives such as those used in our
<a href="https://docs.factorcode.org/content/word-printf%2Cformatting.html">formatting
vocabulary</a>.
This required a change to <a href="https://github.com/factor/factor/commit/35fd8f8a8de24224ec38d056eac9bc5db01ae4eb">split out a format-directive
EBNF</a>
and then I could <a href="https://github.com/factor/factor/commit/9a8df5438e57b8046d8ec7302eeef7a2d9836d60">allow format directives to be
used</a>
in interpolate forms.</p>
<p>The result is something that works a lot more like <a href="https://docs.python.org/3/tutorial/inputoutput.html#formatted-string-literals">formatted string
literals</a>
in Python:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">interpolate</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1.2345 </span><span class="s">"${:011.5f}"</span> interpolate
</span></span><span class="line"><span class="cl"><span class="m">00001.23450
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1 </span>..= <span class="m">11 </span>[| x |
</span></span><span class="line"><span class="cl"> x <span class="nb">dup </span>x <span class="nb">* dup </span>x <span class="nb">*
</span></span></span><span class="line"><span class="cl"> <span class="s">"${:2d} ${:3d} ${:4d}\n"</span> interpolate
</span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span> <span class="m">1 </span> <span class="m">1
</span></span></span><span class="line"><span class="cl"> <span class="m">2 </span> <span class="m">4 </span> <span class="m">8
</span></span></span><span class="line"><span class="cl"> <span class="m">3 </span> <span class="m">9 </span> <span class="m">27
</span></span></span><span class="line"><span class="cl"> <span class="m">4 </span> <span class="m">16 </span> <span class="m">64
</span></span></span><span class="line"><span class="cl"> <span class="m">5 </span> <span class="m">25 </span> <span class="m">125
</span></span></span><span class="line"><span class="cl"> <span class="m">6 </span> <span class="m">36 </span> <span class="m">216
</span></span></span><span class="line"><span class="cl"> <span class="m">7 </span> <span class="m">49 </span> <span class="m">343
</span></span></span><span class="line"><span class="cl"> <span class="m">8 </span> <span class="m">64 </span> <span class="m">512
</span></span></span><span class="line"><span class="cl"> <span class="m">9 </span> <span class="m">81 </span> <span class="m">729
</span></span></span><span class="line"><span class="cl"><span class="m">10 100 1000
</span></span></span><span class="line"><span class="cl"><span class="m">11 121 1331
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> H{ { <span class="s">"n"</span> <span class="m">42 </span>} } [
</span></span><span class="line"><span class="cl"> <span class="s">"Hello, Worker #${n:06x}!"</span> interpolate
</span></span><span class="line"><span class="cl"> ] <span class="nb">with-variables
</span></span></span><span class="line"><span class="cl">Hello, Worker #00002a!
</span></span></code></pre></div><p>Perhaps we can add some support for the <code>name=</code> directive, inline factor code
instead of always stack and namespace variables, and other features that might be useful from
f-string literals and other format libraries.</p>
<p>This is available in a <a href="https://github.com/factor/factor">recent nightly build</a>!</p>
Transducers
https://re.factorcode.org/2024/06/transducers.html
Mon, 03 Jun 2024 08:00:00 -0700https://re.factorcode.org/2024/06/transducers.html<p>One of the distinct elements of <a href="https://clojure.org">Clojure</a> that gets
discussed on occasion are <a href="https://clojure.org/reference/transducers">Clojure
Transducers</a> (not to be confused
with <a href="https://clojure.org/reference/reducers">Clojure Reducers</a>.
Specifically, transducers are a type of transformation that allows for
composable algorithms using two primary concepts:</p>
<blockquote>
<p>A <strong>reducing function</strong> is the kind of function you’d pass to <strong>reduce</strong> - it
is a function that takes an accumulated result and a new input and returns a
new accumulated result, with a reducing function signature of:</p>
<p><code>whatever, input -> whatever</code></p>
<p>A <strong>transducer</strong> (sometimes referred to as xform or xf) is a transformation
from one reducing function to another, with a transducer signature of:</p>
<p><code>(whatever, input -> whatever) -> (whatever, input -> whatever)</code></p>
</blockquote>
<p>In Clojure, the <em>transducer</em> is defined to have 3 different arity functions:</p>
<blockquote>
<p><strong>Init</strong> (arity 0) - should call the init arity on the nested transform
<code>rf</code>, which will eventually call out to the transducing process.</p>
<p><strong>Step</strong> (arity 2) - this is a standard reduction function but it is expected
to call the <code>rf</code> step arity 0 or more times as appropriate in the
transducer. For example, filter will choose (based on the predicate) whether
to call <code>rf</code> or not. map will always call it exactly once. cat may call it
many times depending on the inputs.</p>
<p><strong>Completion</strong> (arity 1) - some processes will not end, but for those that do
(like transduce), the completion arity is used to produce a final value
and/or flush state. This arity must call the <code>rf</code> completion arity exactly
once.</p>
</blockquote>
<p>Similarly to <a href="https://hackage.haskell.org/package/stream-fusion">Haskell’s stream
fusion</a>, this allows for a
benefit of eliding intermediate sequences when multiple operations are applied.
However, in some ways, they are also quite a bit more powerful.</p>
<p>I wanted to discuss one potential way to build a <em>transducer-like</em> thing in
<a href="https://factorcode.org">Factor</a>.</p>
<h3 id="building-a-transducing-function">Building a transducing function</h3>
<p>Our <em>reducing functions</em> are any defined as <code>( prev elt -- next )</code> – used
with <a href="https://docs.factorcode.org/content/word-reduce,sequences.html">reduce</a>.</p>
<p>Our <em>transducing functions</em> have a similar stack effect, but with several rules:</p>
<ul>
<li>if <code>elt</code> is <code>null</code>, skip applying the reducing function</li>
<li>if <code>next</code> is <code>reduced</code>, we can early exit and return it</li>
<li>if <code>next</code> is <code>null</code>, we keep the previous result</li>
</ul>
<blockquote>
<p>Note: we have a simple <code>reduced</code> wrapper type to indicate an early exit
returns <code>obj</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">reduced</span> <span class="nv">obj</span> <span class="k">;
</span></span></span></code></pre></div></blockquote>
<p>We can build a word that converts a <em>reducing function</em> to a <em>transducing
function</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">xf</span> <span class="nf">( </span><span class="nv">rf:</span> <span class="nf">( </span><span class="nv">prev</span> <span class="nv">elt</span> <span class="nf">-- </span><span class="nv">next</span> <span class="nf">) -- </span><span class="nv">xf</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ { [ <span class="nb">over </span>reduced? ] [ <span class="nb">dup null eq? </span>] } 0|| [ <span class="nb">drop </span>] _ <span class="nb">if </span>] <span class="k">;
</span></span></span></code></pre></div><p>A common <em>transducing function</em> is a mapping operation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">xmap</span> <span class="nf">( </span><span class="nv">xf</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">elt</span> <span class="nf">-- </span><span class="nv">newelt</span> <span class="nf">) -- </span><span class="nv">xf'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ @ <span class="nb">dup </span>{ [ reduced? ] [ <span class="nb">null eq? </span>] } 1|| _ <span class="nb">unless </span>] xf <span class="k">;
</span></span></span></code></pre></div><h3 id="building-transduce">Building transduce</h3>
<p>That’s enough definition for us to build our <code>(transduce)</code> word, operating on
an <code>identity</code> and then applying the <em>transducing function</em> until a result is
achieved.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(transduce)</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">identity</span> <span class="nv">xf:</span> <span class="nf">( </span><span class="nv">prev</span> <span class="nv">elt</span> <span class="nf">-- </span><span class="nv">next</span> <span class="nf">) -- </span><span class="nv">result</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">swapd </span>'[
</span></span><span class="line"><span class="cl"> _ keepd <span class="nb">over null eq? </span>[ <span class="nb">nip </span><span class="no">f </span>] [ <span class="nb">drop dup </span>reduced? ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">find 2drop dup </span>reduced? [ obj>> ] <span class="nb">when </span><span class="k">; inline
</span></span></span></code></pre></div><p>And, for convenience we build ourselves a <code>transduce</code> word that takes a
<a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a>, which
is called to compose a <em>transducing function</em> (using
<a href="https://docs.factorcode.org/content/word-make,make.html">make</a> to build up an
<em>initialize</em> quotation) and returning a <em>step</em> quotation, and then returning a
block of code that calls <code>(transduce)</code> with the <code>null</code> identity.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MACRO:</span> <span class="nf">transduce</span> <span class="nf">( </span><span class="nv">quot:</span> <span class="nf">( </span><span class="nv">xf</span> <span class="nf">-- </span><span class="nv">xf'</span> <span class="nf">) -- </span><span class="nv">result</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ <span class="nb">nip </span>] <span class="nb">swap call </span>] [ ] make <span class="nb">swap </span>'[ @ <span class="nb">null </span>_ (transduce) ] <span class="k">;
</span></span></span></code></pre></div><blockquote>
<p>Note: currently this does not implement a <em>complete</em> quotation, but perhaps
some future <em>transducing function</em> will require that and we can add it.</p>
</blockquote>
<p>This word only operates on
<a href="https://docs.factorcode.org/content/word-sequence,sequences.html">sequences</a>,
but one could imagine adding support to apply <em>transducers</em> to other kinds of
streams of objects including infinite <a href="https://docs.factorcode.org/content/article-lists.lazy.html">lazy
lists</a>.</p>
<h3 id="building-transducers">Building transducers</h3>
<p>Using this logic, we can build a bunch of useful transducers with a small
amount of code.</p>
<ol>
<li>A <a href="https://docs.factorcode.org/content/word-filter,sequences.html">filter</a>
transducer skips certain values:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">xfilter</span> <span class="nf">( </span><span class="nv">xf</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">elt</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">) -- </span><span class="nv">xf'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ <span class="nb">dup </span>@ [ <span class="nb">drop null </span>] <span class="nb">unless </span>] xmap <span class="k">;
</span></span></span></code></pre></div><ol start="2">
<li>A <a href="https://docs.factorcode.org/content/word-sum,sequences.html">sum</a>
transducer adds up a series of numbers:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">xsum</span> <span class="nf">( </span><span class="nv">xf</span> <span class="nf">-- </span><span class="nv">xf'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [let <span class="no">f </span>:> n! [ <span class="m">0 </span>n! ] % [ n <span class="nb">+ </span>n! n ] xmap ] <span class="k">;
</span></span></span></code></pre></div><ol start="3">
<li>A <a href="https://docs.factorcode.org/content/article-prettyprint.html">prettyprint</a> transducer prints out intermediate values for debugging:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">xpprint</span> <span class="nf">( </span><span class="nv">xf</span> <span class="nf">-- </span><span class="nv">xf'</span> <span class="nf">) </span>[ <span class="nb">dup </span><span class="m">. </span>] xmap <span class="k">;
</span></span></span></code></pre></div><ol start="4">
<li>A
<a href="https://docs.factorcode.org/content/word-collector,sequences.html">collector</a>
transducer <em>collects</em> all results into a vector:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">xcollect</span> <span class="nf">( </span><span class="nv">xf</span> <span class="nf">-- </span><span class="nv">xf'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [let <span class="no">f </span>:> v! [ V{ } <span class="nb">clone </span>v! ] % [ v [ <span class="nb">push </span>] <span class="nb">keep </span>] xmap ] <span class="k">;
</span></span></span></code></pre></div><ol start="5">
<li>A
<a href="https://docs.factorcode.org/content/word-histogram,math.statistics.html">histogram</a>
transducer counts values:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">xhistogram</span> <span class="nf">( </span><span class="nv">xf</span> <span class="nf">-- </span><span class="nv">xf'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [let <span class="no">f </span>:> h! [ H{ } <span class="nb">clone </span>h! ] % [ h [ <span class="nb">inc-at </span>] <span class="nb">keep </span>] xmap ] <span class="k">;
</span></span></span></code></pre></div><ol start="6">
<li>A unique transducer passes through only unique values:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">xunique</span> <span class="nf">( </span><span class="nv">xf</span> <span class="nf">-- </span><span class="nv">xf'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [let <span class="no">f </span>:> s! [ HS{ } <span class="nb">clone </span>s! ] %
</span></span><span class="line"><span class="cl"> '[ [ s ?adjoin ] <span class="nb">keep null ? </span>] xmap
</span></span><span class="line"><span class="cl"> ] <span class="k">;
</span></span></span></code></pre></div><p>And <a href="https://github.com/mrjbq7/re-factor/blob/master/transducers/transducers.factor">a lot more</a>…</p>
<h3 id="performance">Performance</h3>
<p>Some quick performance tests will show the benefit of <em>fusing</em> multiple
operations together. The artificial test case is going to be a word that
operates on a sequence, returning the sum of the
<a href="https://docs.factorcode.org/content/word-sq,math.html">square</a> of <a href="https://docs.factorcode.org/content/word-prime__que__,math.primes.html">prime
numbers</a>,
if it is bigger than <code>5,000</code>.</p>
<ol>
<li>Using normal <a href="https://docs.factorcode.org/content/article-sequences.html">sequence
operations</a>:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">foo</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ prime? ] <span class="nb">filter
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">sq </span>] <span class="nb">map
</span></span></span><span class="line"><span class="cl"> [ <span class="m">5,000 </span><span class="nb">> </span>] <span class="nb">filter
</span></span></span><span class="line"><span class="cl"> <span class="nb">sum </span><span class="k">;
</span></span></span></code></pre></div><ol start="2">
<li>Using our new transducers:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bar</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ prime? ] xfilter
</span></span><span class="line"><span class="cl"> [ <span class="nb">sq </span>] xmap
</span></span><span class="line"><span class="cl"> [ <span class="m">5,000 </span><span class="nb">> </span>] xfilter
</span></span><span class="line"><span class="cl"> xsum
</span></span><span class="line"><span class="cl"> ] transduce <span class="k">;
</span></span></span></code></pre></div><p>And then see how it performs!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1,000,000 100 </span>randoms
</span></span><span class="line"><span class="cl"> [ [ foo ] time ]
</span></span><span class="line"><span class="cl"> [ [ bar ] time ] <span class="nb">bi assert=
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">0.037218208 </span>seconds
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.028653417 </span>seconds
</span></span></code></pre></div><p>In this case, it’s about 25% faster. Depending on your use-case and how many
intermediate sequence operations the transducer is able to eliminate, it could
represent a nice speedup as well as potentially a more elegant method.</p>
<p>Some things we can improve upon:</p>
<ul>
<li>provide length hints for cases where we are doing map operations</li>
<li>change the <code>reduced</code> and <code>null</code> logic to simplify transducer implementations</li>
<li>investigate using
<a href="https://docs.factorcode.org/content/word-with-return,continuations.html">with-return</a>
to early exit instead of <code>reduced</code> objects</li>
<li>consider doing initialization differently than using local variables</li>
<li>adding completion logic to do things like free up memory at the end of computation</li>
</ul>
<p>One of our contributors that uses <a href="https://re.factorcode.org/2024/01/codewars.html">Codewars</a>
experimented with a different approach to <a href="https://www.codewars.com/kumite/63b7fffeb1dcf200319b975a?sel=63b7fffeb1dcf200319b975a">transducer words in
Factor</a>
that is worth exploring as well.</p>
<p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/transducers/transducers.factor">GitHub</a>.</p>
Deep Clone
https://re.factorcode.org/2024/05/deep-clone.html
Tue, 28 May 2024 08:00:00 -0700https://re.factorcode.org/2024/05/deep-clone.html<p>Someone on the <a href="https://discord.gg/QxJYZx3QDf">Factor Discord server</a> asked if
we have an existing word in <a href="https://factorcode.org">Factor</a> to <em>deep clone</em> an
object. Specifically, a word that will recursively descend an object’s tree,
cloning every visited object to make sure that no reference remains in common
between the <em>deep clone</em> and the original.</p>
<p>The answer is: <em>not exactly</em>.</p>
<p>In particular, we have had a
<a href="https://docs.factorcode.org/content/word-clone,kernel.html">clone</a> word that
creates a <em>practical</em> shallow clone of an object using the <code>(clone)</code>
primitive word – which generates a “byte-by-byte copy of the given object” –
and sometimes makes sure to additionally clone certain slots for separate
mutation of the cloned object.</p>
<p>But, we also have a <a href="https://docs.factorcode.org/content/article-serialize.html">serialize
vocabulary</a> that
could be used (or abused?) to
<a href="https://docs.factorcode.org/content/word-serialize%2Cserialize.html">serialize</a>
an object and then
<a href="https://docs.factorcode.org/content/word-deserialize%2Cserialize.html">deserialize</a>
it as a kind of poor man’s <em>deep clone</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">deep-clone</span> <span class="nf">( </span><span class="nv">object</span> <span class="nf">-- </span><span class="nv">object'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> object>bytes bytes>object <span class="k">;
</span></span></span></code></pre></div><p>This works, and supports the <em>deep cloning</em> of most objects. There are still a
few edge cases that are not a full clone, for example:</p>
<ol>
<li>The <code>f</code> object is a singleton, and is the deep clone of itself.</li>
<li>Words are not deep cloned, but return as references to themselves.</li>
<li>Integers that are <code>fixnum</code> return as themselves.</li>
<li>Continuations are not supported.</li>
</ol>
<p>But, other than that, it is a very practical <em>deep clone</em>. And, because it
transitions through a
<a href="https://docs.factorcode.org/content/word-byte-array,byte-arrays.html">byte-array</a>,
in some ways is more defensive against errors introduced by a type-specific
deep-clone vocabulary, at the cost of some space/time tradeoff. Some <a href="https://github.com/factor/factor/issues/3008">future
discussion</a> is taking place on
what a <em>deeper clone</em> might look like, but this is quite useful as-is.</p>
<p>This is available in the latest <a href="https://docs.factorcode.org/content/word-deep-clone,serialize.html">development
version</a>.</p>
Compressed Images
https://re.factorcode.org/2024/05/compressed-images.html
Mon, 27 May 2024 08:00:00 -0700https://re.factorcode.org/2024/05/compressed-images.html<p>A recent contribution by <a href="https://github.com/nomennescio">@nomennescio</a> enables
support for loading <em>compressed images</em> in <a href="https://factorcode.org">Factor</a>!</p>
<p>I’m not talking about graphical images, but rather about the binary image that
Factor uses to load from. Specifically, the binary image includes mainly the
data and code heaps as well as some special objects that are used to initialize
the Factor libraries.</p>
<p>The <em>compressed image</em> support uses the <code>image_header</code> to communicate that a
newer <em>compressed</em> version of the Factor binary image should be loaded, instead
of an uncompressed one. We currently use the
<a href="https://facebook.github.io/zstd/">Zstandard</a> compression method, which offers
a reasonable balance of speed and compressibility.</p>
<h3 id="compressibility">Compressibility</h3>
<p>The released Factor binary image containing a reasonable default list of
vocabularies to be loaded is around 127 megabytes (compressed to 20 megabytes).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">127M factor.image
</span></span><span class="line"><span class="cl">20M factor.image.compressed
</span></span></code></pre></div><p>One of the criticisms that we have received in the past is that a
<a href="https://docs.factorcode.org/content/word-load-all,vocabs.hierarchy.html">load-all</a>
image that loads the over 300,000 lines of Factor code in the main <a href="https://github.com/factor/factor">Factor
repository</a> can be almost 500 megabytes.
While compressed, that gets significantly reduced down to 66 megabytes!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">483M factor.load-all.image
</span></span><span class="line"><span class="cl">66M factor.load-all.image.compressed
</span></span></code></pre></div><h3 id="performance">Performance</h3>
<p>This is not without some cost: there is a small runtime delay when starting the
Factor binary using a compressed image. For example, we can compare
uncompressed and compressed results of loading a default image and doing
nothing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ <span class="nb">time</span> ./factor -i<span class="o">=</span>factor.image -e<span class="o">=</span><span class="s2">""</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">real 0m0.105s
</span></span><span class="line"><span class="cl">user 0m0.048s
</span></span><span class="line"><span class="cl">sys 0m0.057s
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ <span class="nb">time</span> ./factor -i<span class="o">=</span>factor.image.compressed -e<span class="o">=</span><span class="s2">""</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">real 0m0.281s
</span></span><span class="line"><span class="cl">user 0m0.230s
</span></span><span class="line"><span class="cl">sys 0m0.050s
</span></span></code></pre></div><p>Or compare the results when using a <em>load-all</em> image:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ <span class="nb">time</span> ./factor -i<span class="o">=</span>factor.load-all.image -e<span class="o">=</span><span class="s2">""</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">real 0m0.515s
</span></span><span class="line"><span class="cl">user 0m0.258s
</span></span><span class="line"><span class="cl">sys 0m0.257s
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ <span class="nb">time</span> ./factor -i<span class="o">=</span>factor.load-all.image.compressed -e<span class="o">=</span><span class="s2">""</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">real 0m1.042s
</span></span><span class="line"><span class="cl">user 0m0.809s
</span></span><span class="line"><span class="cl">sys 0m0.233s
</span></span></code></pre></div><p>That is not quite an <em>apples-to-apples</em> comparison, as the uncompressed version
uses <code>mmap</code> and likely does not fully cache or page it all in, but the
uncompressed image is fully uncompressed. However, it gives you a sense of
where this feature is heading.</p>
<h3 id="deploy">Deploy</h3>
<p>If you run <code>"hello-world" deploy</code> you can create a relatively small deployed
binary that prints <em>Hello world</em> when run. This can then be compressed
manually, to see the difference in size (~25%) with negligible differences in
runtime:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ du -h hello-world*
</span></span><span class="line"><span class="cl">1.8M hello-world
</span></span><span class="line"><span class="cl">1.3M hello-world-compressed
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ <span class="nb">time</span> ./hello-world
</span></span><span class="line"><span class="cl">Hello world
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">real 0m0.005s
</span></span><span class="line"><span class="cl">user 0m0.001s
</span></span><span class="line"><span class="cl">sys 0m0.004s
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ <span class="nb">time</span> ./hello-world-compressed
</span></span><span class="line"><span class="cl">Hello world
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">real 0m0.005s
</span></span><span class="line"><span class="cl">user 0m0.001s
</span></span><span class="line"><span class="cl">sys 0m0.003s
</span></span></code></pre></div><p>Some additional work needs to be done to add support in the <a href="https://docs.factorcode.org/content/article-tools.deploy.html">deploy
tools</a> for a
<em>checkbox</em> to create binaries using compression, however this already
represents a big win for anyone that’s more concerned about file sizes than
startup latency.</p>
<p>Compression is currently supported using the <a href="https://docs.factorcode.org/content/article-tools.image.compressor.html">tools.image.compressor
vocabulary</a>
and uncompression using the <a href="https://docs.factorcode.org/content/article-tools.image.uncompressor.html">tools.image.uncompressor
vocabulary</a>.
This is a new feature and might change as it evolves, but this is a neat
preview of things to come in the next release.</p>
<p>Give it a try!</p>
Intersecting Ranges
https://re.factorcode.org/2024/05/intersecting-ranges.html
Thu, 23 May 2024 12:00:00 -0700https://re.factorcode.org/2024/05/intersecting-ranges.html<p>After <a href="https://re.factorcode.org/2023/08/factor-0-99-now-available.html">Factor 0.99</a>, we have
continued to add new features to what is likely to be an upcoming release.
One of those that I would like to discuss today was a patch to <a href="https://github.com/factor/factor/commit/8a56479f443aa83fb94c6581ac44d0408c92bbb1">add set
methods to
ranges</a>
that was contributed by <a href="https://github.com/Kacarott">Keldan Chapman</a>. It
was a really nice addition that makes our
<a href="https://docs.factorcode.org/content/article-ranges.html">ranges</a> support
efficient <a href="https://docs.factorcode.org/content/article-set-operations.html">set
operations</a>.</p>
<p>And that brings us to the topic of today’s blog post: <em>intersecting ranges</em>.</p>
<p>Specifically, a discussion that occurred over code style and I thought it would
be interesting to see 3 different approaches to writing a reasonably complex
<em>intersecting ranges</em> word in <a href="https://factorcode.org">Factor</a>.</p>
<p>You can see this in action, where
<a href="https://docs.factorcode.org/content/word-intersect,sets.html">intersect</a>
returns a <code>range</code> in the latest <a href="https://github.com/factor/factor">development
version</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="c">! Factor 0.99</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2 40 2 </span><range>
</span></span><span class="line"><span class="cl"> <span class="m">1 40 3 </span><range> intersect <span class="m">.
</span></span></span><span class="line"><span class="cl">V{ <span class="m">4 10 16 22 28 34 40 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">! Factor 0.100 (git)</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2 40 2 </span><range>
</span></span><span class="line"><span class="cl"> <span class="m">1 40 3 </span><range> intersect <span class="m">.
</span></span></span><span class="line"><span class="cl">T{ range { from <span class="m">4 </span>} { <span class="nb">length </span><span class="m">7 </span>} { step <span class="m">6 </span>} }
</span></span></code></pre></div><p>Some inspiration came from looking at the <a href="https://julialang.org">Julia programming
language</a> and their
<a href="https://github.com/JuliaLang/julia/blob/8e14322b5aa344639dd86bf9eabb84afe831fcba/base/range.jl#L1185">intersect</a>
function.</p>
<h3 id="local-variables">Local Variables</h3>
<p>The first version used <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a> and
after a smidge of cleanup, looked pretty good:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">intersect-range</span> <span class="nf">( </span><span class="nv">range1</span> <span class="nv">range2</span> <span class="nf">-- </span><span class="nv">range3</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> range1 <span class="nb">empty? </span>range2 <span class="nb">empty? or </span>[ empty-range ] [
</span></span><span class="line"><span class="cl"> range1 >forward-range< :> <span class="nf">( </span><span class="nv">start1</span> <span class="nv">stop1</span> <span class="nv">step1</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> range2 >forward-range< :> <span class="nf">( </span><span class="nv">start2</span> <span class="nv">stop2</span> <span class="nv">step2</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> step1 step2 gcd :> <span class="nf">( </span><span class="nv">x</span> <span class="nv">g</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> start1 start2 <span class="nb">- </span>g <span class="nb">/mod </span>:> <span class="nf">( </span><span class="nv">z</span> <span class="nv">y</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> y <span class="nb">zero? not </span>[ empty-range ] [
</span></span><span class="line"><span class="cl"> start1 x z step1 <span class="nb">* * - </span>:> b
</span></span><span class="line"><span class="cl"> step1 step2 lcm :> a
</span></span><span class="line"><span class="cl"> start1 start2 [ b <span class="nb">over - </span>a <span class="nb">rem + </span>] <span class="nb">bi@ </span>max :> m
</span></span><span class="line"><span class="cl"> stop1 stop2 [ <span class="nb">dup </span>b <span class="nb">- </span>a <span class="nb">rem - </span>] <span class="nb">bi@ </span>min :> n
</span></span><span class="line"><span class="cl"> m n a <range>
</span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><h3 id="stack-shuffling">Stack Shuffling</h3>
<p>Due to a bug with using <a href="https://github.com/factor/factor/commit/1f8e070b0d6e96069522da8bcbeacd9642bdf7ef">local definitions in a bootstrap
image</a>,
we temporarily switched to a version that used <a href="https://docs.factorcode.org/content/article-tour-stack-shuffling.html">stack
shuffling</a>.
As you can see – and is often the case when you are dealing with a large
number of items on the stack – it isn’t particularly readable.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">intersect-range</span> <span class="nf">( </span><span class="nv">range1</span> <span class="nv">range2</span> <span class="nf">-- </span><span class="nv">range3</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">2dup </span>[ <span class="nb">empty? </span>] <span class="nb">either? </span>[ <span class="nb">2drop </span>empty-range ] [
</span></span><span class="line"><span class="cl"> [ >forward-range< ] <span class="nb">bi@ </span>[ <span class="nb">-rot </span>] [ <span class="nb">swap </span>] [ ] <span class="nb">tri* </span>[
</span></span><span class="line"><span class="cl"> [ reach reach <span class="nb">- </span>] <span class="nb">2dip </span>gcd <span class="nb">swap </span>[ <span class="nb">/mod </span>] <span class="nb">dip swap
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">2keep rot zero? not </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">4drop 4drop </span>empty-range
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> [ reach ] <span class="nb">4dip dupd </span>lcm [ <span class="nb">* * - </span>] <span class="nb">dip </span>[
</span></span><span class="line"><span class="cl"> [ '[ [ _ <span class="nb">over - </span>_ <span class="nb">rem + </span>] <span class="nb">bi@ </span>max ] <span class="nb">2dip </span>]
</span></span><span class="line"><span class="cl"> [ '[ <span class="nb">dup </span>_ <span class="nb">- </span>_ <span class="nb">rem - </span>] <span class="nb">bi@ </span>min ] <span class="nb">2bi
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">keep </span><range>
</span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><h3 id="small-words">Small Words</h3>
<p>Usually when stack shuffling doesn’t help, sometimes extracting out pieces
of logic into words and naming them can often result in a simpler or cleaner
version. It did turn out a bit easier to read, but is still overly
<em>shuffly</em>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">explode-ranges</span> <span class="nf">( </span><span class="nv">range1</span> <span class="nv">range2</span> <span class="nf">-- </span><span class="nv">start1</span> <span class="nv">start2</span> <span class="nv">stop1</span> <span class="nv">stop2</span> <span class="nv">step1</span> <span class="nv">step2</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ >forward-range< ] <span class="nb">bi@ </span>[ <span class="nb">-rot </span>] [ <span class="nb">swap </span>] [ ] <span class="nb">tri* </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">compute-z-y-x</span> <span class="nf">( </span><span class="nv">start1</span> <span class="nv">start2</span> <span class="nv">step1</span> <span class="nv">step2</span> <span class="nf">-- </span><span class="nv">z</span> <span class="nv">y</span> <span class="nv">x</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> gcd [ <span class="nb">- </span>] <span class="nb">2dip swap </span>[ <span class="nb">/mod </span>] <span class="nb">dip </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">compute-b-a</span> <span class="nf">( </span><span class="nv">start1</span> <span class="nv">x</span> <span class="nv">z</span> <span class="nv">step1</span> <span class="nv">step2</span> <span class="nf">-- </span><span class="nv">b</span> <span class="nv">a</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dupd </span>lcm [ <span class="nb">* * - </span>] <span class="nb">dip </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">intersected-range</span> <span class="nf">( </span><span class="nv">start1</span> <span class="nv">start2</span> <span class="nv">stop1</span> <span class="nv">stop2</span> <span class="nv">b</span> <span class="nv">a</span> <span class="nf">-- </span><span class="nv">range</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ '[ [ _ <span class="nb">over - </span>_ <span class="nb">rem + </span>] <span class="nb">bi@ </span>max ] <span class="nb">2dip </span>]
</span></span><span class="line"><span class="cl"> [ '[ <span class="nb">dup </span>_ <span class="nb">- </span>_ <span class="nb">rem - </span>] <span class="nb">bi@ </span>min ] <span class="nb">2bi
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">keep </span><range> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">intersect-range</span> <span class="nf">( </span><span class="nv">range1</span> <span class="nv">range2</span> <span class="nf">-- </span><span class="nv">range3</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">2dup </span>[ <span class="nb">empty? </span>] <span class="nb">either? </span>[ <span class="nb">2drop </span>empty-range ] [
</span></span><span class="line"><span class="cl"> explode-ranges [
</span></span><span class="line"><span class="cl"> [ reach reach ] <span class="nb">2dip </span>compute-z-y-x <span class="nb">swap
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">2keep rot zero? not </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">4drop 4drop </span>empty-range
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> [ reach ] <span class="nb">4dip </span>compute-b-a intersected-range
</span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Well, likely the first version was the nicest, but it’s an interesting
exercise to think about <a href="https://re.factorcode.org/2012/02/readability.html">readability</a>,
<a href="https://re.factorcode.org/2011/04/verbosity.html">verbosity</a>, and <a href="https://re.factorcode.org/2011/07/concatenative-thinking.html">concatenative
thinking</a>. Sometimes you need to
puzzle through a piece of code to find the one you like the best.</p>
Argument Parser
https://re.factorcode.org/2024/05/argument-parser.html
Tue, 21 May 2024 10:00:00 -0700https://re.factorcode.org/2024/05/argument-parser.html<p>Recently, some discussions on our <a href="https://discord.gg/QxJYZx3QDf">Factor Discord
server</a> reminded me that we were missing an
important feature for parsing command-line arguments: an <em>argument parser</em>.</p>
<p>An <em>argument parser</em> is something that parses structured command-line arguments
and is often helpful in printing command-line usage information. I thought it
would be useful to go over a few different ways to parse command-line arguments
using <a href="https://factorcode.org">Factor</a>.</p>
<h3 id="version-0">Version 0</h3>
<p>In our <a href="https://re.factorcode.org/2023/08/factor-0-99-now-available.html">most recent release</a>,
command-line parsing was kind of manual and idiosyncratic. For example, this is
how arguments were parsed for the <a href="https://re.factorcode.org/2024/05/stomp.html">STOMP</a> command-line
interface that I built recently:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">stomp-options</span> <span class="nf">( </span><span class="nv">args</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="nb">unclip </span>>lower {
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>{ <span class="s">"-h"</span> <span class="s">"--host"</span> } <span class="nb">member? </span>] [ <span class="nb">unclip </span>stomp-host <span class="nb">set-global </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>{ <span class="s">"-p"</span> <span class="s">"--port"</span> } <span class="nb">member? </span>] [ <span class="nb">unclip </span>string>number stomp-port <span class="nb">set-global </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>{ <span class="s">"-u"</span> <span class="s">"--username"</span> } <span class="nb">member? </span>] [ <span class="nb">unclip </span>stomp-username <span class="nb">set-global </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>{ <span class="s">"-w"</span> <span class="s">"--password"</span> } <span class="nb">member? </span>] [ <span class="nb">unclip </span>stomp-password <span class="nb">set-global </span>] }
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span>stomp-options
</span></span><span class="line"><span class="cl"> ] <span class="nb">unless-empty </span><span class="k">;
</span></span></span></code></pre></div><h3 id="version-1">Version 1</h3>
<p>The Factor binary already does some amount of argument parsing, and so I
thought I would extend the <a href="https://docs.factorcode.org/content/vocab-command-line.html">command-line
vocabulary</a> to
provide some support for <em>simple options parsing</em>. Specifically, I
<a href="https://github.com/factor/factor/commit/4b1204bc4554aec9997bb048adb8451af9318cc3">added</a>
a
<a href="https://docs.factorcode.org/content/word-command-line-options,command-line.html">command-line-options</a>
word that could be used like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">stomp-options</span> <span class="nf">( </span><span class="nv">args</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> command-line-options <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> <span class="s">"host"</span> <span class="nb">get </span><span class="s">"127.0.0.1"</span> <span class="nb">or </span>stomp-host <span class="nb">set-global
</span></span></span><span class="line"><span class="cl"> <span class="s">"port"</span> <span class="nb">get </span>[ string>number ] [ <span class="m">61613 </span>] <span class="nb">if* </span>stomp-port <span class="nb">set-global
</span></span></span><span class="line"><span class="cl"> <span class="s">"username"</span> <span class="nb">get </span>stomp-username <span class="nb">set-global
</span></span></span><span class="line"><span class="cl"> <span class="s">"password"</span> <span class="nb">get </span>stomp-password <span class="nb">set-global </span><span class="k">;
</span></span></span></code></pre></div><p>This uses our existing parameter parsing code, but stores the parsed options as
string keys and string or boolean values in the namespace, doesn’t easily allow
for boolean values that default to true, and doesn’t do any conversion or
validation of arguments.</p>
<h3 id="version-2">Version 2</h3>
<p>I was finally able to build something better that could serve our users. It is
modeled after the <a href="https://python.org">Python</a>
<a href="https://docs.python.org/3/library/argparse.html">argparse</a> module. Using the
newly developed
<a href="https://docs.factorcode.org/content/article-command-line.parser.html">command-line.parser</a>
vocabulary, we can now define some
<a href="https://docs.factorcode.org/content/word-option%2Ccommand-line.parser.html">option</a>
objects to create the original example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> T{ option
</span></span><span class="line"><span class="cl"> { name <span class="s">"--host"</span> }
</span></span><span class="line"><span class="cl"> { help <span class="s">"set the hostname"</span> }
</span></span><span class="line"><span class="cl"> { type ipv4 }
</span></span><span class="line"><span class="cl"> { variable stomp-host }
</span></span><span class="line"><span class="cl"> { default T{ ipv4 <span class="no">f </span><span class="s">"127.0.0.1"</span> } }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ option
</span></span><span class="line"><span class="cl"> { name <span class="s">"--port"</span> }
</span></span><span class="line"><span class="cl"> { help <span class="s">"set the port"</span> }
</span></span><span class="line"><span class="cl"> { type <span class="nb">integer </span>}
</span></span><span class="line"><span class="cl"> { variable stomp-port }
</span></span><span class="line"><span class="cl"> { default <span class="m">61613 </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ option
</span></span><span class="line"><span class="cl"> { name <span class="s">"--username"</span> }
</span></span><span class="line"><span class="cl"> { help <span class="s">"set the username"</span> }
</span></span><span class="line"><span class="cl"> { variable stomp-username }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ option
</span></span><span class="line"><span class="cl"> { name <span class="s">"--password"</span> }
</span></span><span class="line"><span class="cl"> { help <span class="s">"set the password"</span> }
</span></span><span class="line"><span class="cl"> { variable stomp-password }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">} [
</span></span><span class="line"><span class="cl"> stomp-host <span class="nb">get </span><span class="m">.
</span></span></span><span class="line"><span class="cl"> stomp-port <span class="nb">get </span><span class="m">.
</span></span></span><span class="line"><span class="cl"> stomp-username <span class="nb">get </span><span class="m">.
</span></span></span><span class="line"><span class="cl"> stomp-password <span class="nb">get </span><span class="m">.
</span></span></span><span class="line"><span class="cl">] with-options
</span></span></code></pre></div><p>This now allows for better default values and automatic <code>--help</code> output:</p>
<pre tabindex="0"><code>$ ./factor -run=stomp.cli --help
Usage:
factor -run=stomp.cli [options] [arguments]
Options:
--help show this help and exit
--host HOST set the hostname (default: 127.0.0.1)
--port PORT set the port (default: 61613)
--username USERNAME set the username
--password PASSWORD set the password
</code></pre><p>And some automatic error checking:</p>
<pre tabindex="0"><code>$ ./factor -run=stomp.cli --port
ERROR: Expected more arguments for option 'port'
$ ./factor -run=stomp.cli --port asdf
ERROR: Invalid value 'asdf' for option 'port'
$ ./factor -run=stomp.cli --prot
ERROR: Unknown option 'prot'
$ ./factor -run=stomp.cli a b c d
ERROR: Unrecognized arguments: a b c d
$ ./factor -run=stomp.cli -h
ERROR: The option 'h' matches more than one (host, help)
</code></pre><p>Additionally, this includes some features such as fuzzy matching of options,
validation that all required options are provided, constant values when an
option is specified, support for type coercion and type validation of option
values, support for mixing optional and positional arguments, as well as
specifying the number of expected arguments that an option requires.</p>
<p>Some things that I would still like to build include support for short option
codes, exit codes when argument parsing errors occur, and perhaps support for
<a href="http://docopt.org">docopt</a> style declarations.</p>
<p>This is available in the <a href="https://github.com/factor/factor/tree/master/basis/command-line/parser">development
version</a>
– give it a try!</p>
STOMP
https://re.factorcode.org/2024/05/stomp.html
Thu, 09 May 2024 08:00:00 -0700https://re.factorcode.org/2024/05/stomp.html<p>Over the last few days, I implemented some support for
<a href="https://stomp.github.io">STOMP</a>, the <em>“Simple Text Oriented Messaging
Protocol”</em>:</p>
<blockquote>
<p>STOMP provides an interoperable wire format so that STOMP clients can
communicate with any STOMP message broker to provide easy and widespread
messaging interoperability among many languages, platforms and brokers.</p>
</blockquote>
<p>There are three versions of the protocol available:</p>
<ul>
<li><a href="https://stomp.github.io/stomp-specification-1.0.html">STOMP 1.0</a></li>
<li><a href="https://stomp.github.io/stomp-specification-1.1.html">STOMP 1.1</a></li>
<li><a href="https://stomp.github.io/stomp-specification-1.2.html">STOMP 1.2</a> (the latest
version)</li>
</ul>
<p>In the interest of learning <a href="https://factorcode.org">Factor</a>, I thought I would
write a bit about parsing the STOMP protocol, and then about how to implement a
client library using <a href="https://docs.factorcode.org/content/article-network-connection.html">connection-oriented
networking</a>,
interacting with it using
<a href="https://docs.factorcode.org/content/article-concurrency.mailboxes.html">mailboxes</a>,
and then building a command-line interface using the <a href="https://docs.factorcode.org/content/vocab-command-loop.html">command-loop
vocabulary</a>.</p>
<p>There are many <a href="https://stomp.github.io/implementations.html">STOMP servers and
clients</a> available in different
languages. I tried a few and decided that <a href="https://activemq.apache.org">Apache
ActiveMQ</a> was one of the most convenient to setup
and reliable to work with, but others are available as well. On my
<a href="https://www.apple.com/macos/">macOS</a> laptop, this can be accomplished by:</p>
<pre tabindex="0"><code>$ brew install activemq
$ brew services start activemq
</code></pre><h2 id="protocol">Protocol</h2>
<p>The STOMP protocol consists of <em>frames</em> that are sent and received between the
STOMP client and the STOMP server. Each <em>frame</em> consists of a command, some
headers, and a body:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">frame</span> <span class="nv">command</span> <span class="nv">headers</span> <span class="nv">body</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><frame></span> <span class="nf">( </span><span class="nv">command</span> <span class="nf">-- </span><span class="nv">frame</span> <span class="nf">) </span>LH{ } <span class="nb">clone </span><span class="no">f </span>frame <span class="nb">boa </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">set-header</span> <span class="nf">( </span><span class="nv">frame</span> <span class="nv">header-value</span> <span class="nv">header-name</span> <span class="nf">-- </span><span class="nv">frame</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">pick </span>headers>> <span class="nb">set-at </span><span class="k">;
</span></span></span></code></pre></div><p>An example <code>SEND</code> message might look like this, with the <code>^@</code> indicating a
<code>NUL</code> byte to end the message:</p>
<pre tabindex="0"><code>SEND
destination:/queue/a
hello queue a
^@
</code></pre><p>We will begin by implementing words to read each of these sections. The
<em>command</em> is the first line, followed by a series of <code>name:value</code> headers
before a blank line, and then a <em>body</em> (specified either by a
<code>content-length</code> header, or reading until a <code>NUL</code> byte is encountered):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-command</span> <span class="nf">( -- </span><span class="nv">command</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">readln </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-headers</span> <span class="nf">( -- </span><span class="nv">headers</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">readln dup empty? not </span>] [ <span class="s">":"</span> split1 <span class="nb">2array </span>] <span class="nb">produce nip </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-body</span> <span class="nf">( </span><span class="nv">content-length/f</span> <span class="nf">-- </span><span class="nv">body</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">read read1 </span>] [ B{ <span class="m">0 </span>} <span class="nb">read-until </span>] <span class="nb">if* </span><span class="m">0 </span><span class="nb">assert= </span><span class="k">;
</span></span></span></code></pre></div><p>And then implement a <code>read-frame</code> word that uses those to build up a
<code>frame</code> tuple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-frame</span> <span class="nf">( -- </span><span class="nv">frame</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> read-command
</span></span><span class="line"><span class="cl"> read-headers
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"content-length"</span> <span class="nb">of </span>string>number
</span></span><span class="line"><span class="cl"> read-body frame <span class="nb">boa </span><span class="k">;
</span></span></span></code></pre></div><p>We can implement a <code>write-frame</code> word writing it out in the expected
structure:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-frame</span> <span class="nf">( </span><span class="nv">frame</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ command>> <span class="nb">print </span>]
</span></span><span class="line"><span class="cl"> [ headers>> [ <span class="s">":"</span> <span class="nb">swap </span>[ <span class="nb">write </span>] <span class="nb">tri@ nl </span>] <span class="nb">assoc-each nl </span>]
</span></span><span class="line"><span class="cl"> [ body>> [ <span class="nb">write </span>] <span class="nb">when* </span><span class="m">0 </span><span class="nb">write1 </span>] <span class="nb">tri flush </span><span class="k">;
</span></span></span></code></pre></div><p>The <code>CONNECT</code> <em>frame</em> is typically the first one sent to the server:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">stomp-username</span>
</span></span><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">stomp-password</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">stomp-connect</span> <span class="nf">( -- </span><span class="nv">frame</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"CONNECT"</span> <frame>
</span></span><span class="line"><span class="cl"> stomp-username <span class="nb">get </span>[ <span class="s">"login"</span> set-header ] <span class="nb">when*
</span></span></span><span class="line"><span class="cl"> stomp-password <span class="nb">get </span>[ <span class="s">"passcode"</span> set-header ] <span class="nb">when* </span><span class="k">;
</span></span></span></code></pre></div><p>The server will respond to <code>CONNECT</code> with a <code>CONNECTED</code> <em>frame</em>, which we
can wait for:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wait-for-connected</span> <span class="nf">( -- </span><span class="nv">frame</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span>[ <span class="nb">drop </span>read-frame <span class="nb">dup </span>command>> <span class="s">"CONNECTED"</span> <span class="nb">= not </span>] <span class="nb">loop </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">stomp-connect-and-wait</span> <span class="nf">( -- </span><span class="nv">frame</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> stomp-connect write-frame wait-for-connected <span class="k">;
</span></span></span></code></pre></div><p>The <code>SEND</code> frame contains a body that is sent to a <em>destination</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">stomp-send</span> <span class="nf">( </span><span class="nv">destination</span> <span class="nv">body</span> <span class="nf">-- </span><span class="nv">frame</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"SEND"</span> <frame>
</span></span><span class="line"><span class="cl"> body >>body
</span></span><span class="line"><span class="cl"> destination <span class="s">"destination"</span> set-header <span class="k">;
</span></span></span></code></pre></div><p>The <code>SEND</code> frame can also be used to send a file (using <a href="https://docs.factorcode.org/content/article-mime.types.html">MIME
types</a> for
automatic content encoding):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">stomp-sendfile</span> <span class="nf">( </span><span class="nv">destination</span> <span class="nv">path</span> <span class="nf">-- </span><span class="nv">frame</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"SEND"</span> <frame>
</span></span><span class="line"><span class="cl"> destination <span class="s">"destination"</span> set-header
</span></span><span class="line"><span class="cl"> path <span class="nb">dup </span>mime-type
</span></span><span class="line"><span class="cl"> [ mime-type-encoding file-contents >>body ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"content-type"</span> set-header ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>The <em>destination</em> is a message queue, which we can <code>SUBSCRIBE</code> or
<code>UNSUBSCRIBE</code> from:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">stomp-subscribe</span> <span class="nf">( </span><span class="nv">destination</span> <span class="nf">-- </span><span class="nv">frame</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"SUBSCRIBE"</span> <frame>
</span></span><span class="line"><span class="cl"> destination <span class="s">"destination"</span> set-header <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">stomp-unsubscribe</span> <span class="nf">( </span><span class="nv">destination</span> <span class="nf">-- </span><span class="nv">frame</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"UNSUBSCRIBE"</span> <frame>
</span></span><span class="line"><span class="cl"> destination <span class="s">"destination"</span> set-header <span class="k">;
</span></span></span></code></pre></div><p>There are also words to support transactions (<code>BEGIN</code>, <code>COMMIT</code>,
<code>ABORT</code>), indicate message receipt (<code>ACK</code>, <code>NACK</code>), as well as to
<code>DISCONNECT</code> from the server.</p>
<h2 id="client">Client</h2>
<p>Using those words, we can move on to the
<a href="https://docs.factorcode.org/content/article-network-streams.html">networking</a>
component that will enable connecting to a STOMP server and interacting with
it. An inner loop takes a
<a href="https://docs.factorcode.org/content/article-concurrency.mailboxes.html">mailbox</a>
that a client can use to enqueue <em>frames</em> to be sent, and a
<a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a> that
will be called with each received <em>frame</em> from the server.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">stomp-loop</span> <span class="nf">( </span><span class="nv">mailbox</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">frame</span> <span class="nf">-- ) -- )
</span></span></span><span class="line"><span class="cl"> stomp-connect-and-wait <span class="nb">drop
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ mailbox mailbox-get write-frame <span class="no">t </span>]
</span></span><span class="line"><span class="cl"> <span class="s">"stomp writer"</span> spawn-server <span class="nb">drop
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ read-frame quot <span class="nb">call </span><span class="no">t </span>] <span class="nb">loop </span><span class="k">; inline
</span></span></span></code></pre></div><p>The client library can use this to connect and print out any received frames,
for example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="s">"127.0.0.1"</span> <span class="m">61613 </span>utf8 [
</span></span><span class="line"><span class="cl"> <mailbox> [ [ <span class="m">. </span><span class="nb">flush </span>] <span class="nb">with-global </span>] stomp-loop
</span></span><span class="line"><span class="cl">] with-client
</span></span></code></pre></div><h2 id="command-line">Command-Line</h2>
<p>To build a simple <a href="https://en.wikipedia.org/wiki/Command-line_interface">command-line
interface</a>, we first need
to create our mailbox and store a reference to it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">stomp-mailbox</span> $[ <mailbox> ]
</span></span></code></pre></div><p>And then make a word to put <em>frames</em> into it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">put-frame</span> <span class="nf">( </span><span class="nv">frame</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> stomp-mailbox mailbox-put <span class="k">;
</span></span></span></code></pre></div><p>Using the <a href="https://docs.factorcode.org/content/vocab-command-loop.html">command-loop
vocabulary</a>, we
can define some supported commands:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">COMMANDS</span> {
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"send"</span> }
</span></span><span class="line"><span class="cl"> { quot [ <span class="s">" "</span> split1 stomp-send put-frame ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Send a message to a destination in the messaging system."</span> } }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"sendfile"</span> }
</span></span><span class="line"><span class="cl"> { quot [ <span class="s">" "</span> split1 stomp-sendfile put-frame ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Send a file to a destination in the messaging system."</span> } }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"subscribe"</span> }
</span></span><span class="line"><span class="cl"> { quot [ stomp-subscribe put-frame ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Subscribe to a destination."</span> } }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"unsubscribe"</span> }
</span></span><span class="line"><span class="cl"> { quot [ stomp-unsubscribe put-frame ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Unsubscribe from a destination."</span> } }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Before we run our command-loop, we start a thread to connect to the STOMP
server, and configure it to send <em>frames</em> queued in the mailbox and print out
the <em>frames</em> that are received:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">INITIALIZED-SYMBOL: stomp-host [ <span class="s">"127.0.0.1"</span> ]
</span></span><span class="line"><span class="cl">INITIALIZED-SYMBOL: stomp-port [ <span class="m">61613 </span>]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">start-stomp-client</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> stomp-host <span class="nb">get </span>stomp-port <span class="nb">get </span><inet4> utf8 [
</span></span><span class="line"><span class="cl"> stomp-mailbox [ [ <span class="nb">nl </span><span class="m">. </span><span class="nb">flush </span>] <span class="nb">with-global </span>] stomp-loop
</span></span><span class="line"><span class="cl"> ] with-client
</span></span><span class="line"><span class="cl"> ] in-thread <span class="k">;
</span></span></span></code></pre></div><p>And then a simple word to start the command-loop:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">stomp-main</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"Welcome to STOMP!"</span> <span class="s">"STOMP>"</span> <command-loop>
</span></span><span class="line"><span class="cl"> COMMANDS [ <span class="nb">over </span>add-command ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> start-stomp-client run-command-loop <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">stomp-main</span>
</span></span></code></pre></div><p>And then you can try running it!</p>
<pre tabindex="0"><code>$ ./factor -run=stomp.cli
Welcome to STOMP!
STOMP> subscribe /queue/test
STOMP> send /queue/test hello world
T{ frame
{ command "MESSAGE" }
{ headers
{
{ "expires" "0" }
{ "destination" "/queue/test" }
{ "subscription" "1" }
{ "priority" "4" }
{
"message-id"
"ID\\chostname-59660-1715273573218-3\\c3\\c-1\\c1\\c1"
}
{ "timestamp" "1715276926454" }
}
}
{ body "hello world" }
}
</code></pre><p>In addition to this, support was added for all the other client messages, all
three versions of the STOMP protocol, automatic heartbeats, graceful disconnect
using message receipts, words to make using transactions easier, as well as a
debug mode to print sent and received frames from the network, and command-line
options for configuring the hostname, port, username, and passwords.</p>
<p>As always, there are a few things it would be nice to add – for example:
better support for SSL/TLS connections, automatic reconnect attempts with
backoff algorithm, better display of connection status in the command-line, and
additional protocol-level support like automatic generation of <code>ACK</code> and
<code>NACK</code>, and testing with additional STOMP compliant message brokers.</p>
<p>This is available in the <a href="https://github.com/factor/factor/tree/master/extra/stomp">development
version</a>. Take a
look!</p>
Time My Meeting
https://re.factorcode.org/2024/05/time-my-meeting.html
Wed, 01 May 2024 20:00:00 -0700https://re.factorcode.org/2024/05/time-my-meeting.html<p>Recently, I bumped into <a href="https://timemymeeting.com">Time My Meeting</a>, a cute
website that runs a timer for how long a meeting has run and then shows you a
fun comparison versus something memorable that has taken a similar amount of
time.</p>
<p>I thought it might make a nice demo in <a href="https://factorcode.org">Factor</a>:</p>
<p>
<img src="https://re.factorcode.org/images/2024-05-01-time-my-meeting.png" alt="" width="645" height="363" />
</p>
<p>Our program starts with a list of <em>things that take time</em> and how many
milliseconds they take:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">THINGS-THAT-TAKE-TIME</span> {
</span></span><span class="line"><span class="cl"> <span class="c">! <10 seconds</span>
</span></span><span class="line"><span class="cl"> { <span class="s">"A single frame of a film"</span> <span class="m">100 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"It would take light to go around the Earth"</span> <span class="m">133 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"A blink of an eye"</span> <span class="m">400 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"The time it takes light to reach Earth from the moon"</span> <span class="m">1255 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"The fastest Formula 1 pit stop"</span> <span class="m">1820 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"The fastest 1/4 mile drag race time"</span> <span class="m">3580 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"The fastest Rubik's cube solve"</span> <span class="m">4221 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"The fastest 40-yard time at the NFL Combine"</span> <span class="m">4240 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"The fastest 1 liter beer chug"</span> <span class="m">4370 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"A skippable Youtube ad"</span> <span class="m">5000 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"A full bull ride"</span> <span class="m">8000 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"The fastest 100m sprint"</span> <span class="m">9580 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! 10 Seconds</span>
</span></span><span class="line"><span class="cl"> { <span class="s">"The Wright Brothers first flight"</span> <span class="m">12000 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"The fastest 200m sprint"</span> <span class="m">19190 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"The fastest 50m freestyle swim lap"</span> <span class="m">21300 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"The Westminster Kennel Club dog agility record"</span> <span class="m">28440 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"A typical television ad"</span> <span class="m">30000 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"The fastest NASCAR lap at Daytona"</span> <span class="m">40364 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"The fastest 400m sprint"</span> <span class="m">43030 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"The fastest NASCAR lap at Talladega"</span> <span class="m">44270 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"The fastest 100m freestyle swim lap"</span> <span class="m">47050 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>We need a small word to turn those milliseconds into a useful
<a href="https://docs.factorcode.org/content/article-strings.html">string</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">human-time</span> <span class="nf">( </span><span class="nv">milliseconds</span> <span class="nf">-- </span><span class="nv">string</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1000 </span><span class="nb">/ dup </span><span class="m">60 </span><span class="nb"><
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"%.1f seconds"</span> sprintf ]
</span></span><span class="line"><span class="cl"> [ seconds duration>human-readable ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>We may also need to know what the next <em>thing that takes time</em> will be,
based on the total elapsed time:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-thing-that-takes-time</span> <span class="nf">( </span><span class="nv">elapsed-millis</span> <span class="nf">-- </span><span class="nv">elt</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> THINGS-THAT-TAKE-TIME [ <span class="nb">second < </span>] <span class="nb">with find nip </span><span class="k">;
</span></span></span></code></pre></div><h3 id="command-line">Command-Line</h3>
<p>First, we are going to make a simple word to run this on the command-line,
iterating through the <em>things that take time</em> and then
<a href="https://docs.factorcode.org/content/word-sleep-until,threads.html">sleeping</a>
the appropriate amount of time, and then
<a href="https://docs.factorcode.org/content/word-print,io.html">printing</a> them out
as they pass:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">time-my-meeting.</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> now THINGS-THAT-TAKE-TIME [
</span></span><span class="line"><span class="cl"> [ milliseconds <span class="nb">pick </span>time+ sleep-until ]
</span></span><span class="line"><span class="cl"> [ human-time <span class="s">"%s (%s)\n"</span> printf <span class="nb">flush </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">assoc-each drop </span><span class="k">;
</span></span></span></code></pre></div><p>You can run it and get something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> time-my-meeting.
</span></span><span class="line"><span class="cl">A single frame <span class="nb">of </span>a film (0.1 seconds)
</span></span><span class="line"><span class="cl">It would take light to go around the Earth (0.1 seconds)
</span></span><span class="line"><span class="cl">A blink <span class="nb">of </span>an eye (0.4 seconds)
</span></span><span class="line"><span class="cl">The time it takes light to reach Earth from the moon (1.3 seconds)
</span></span><span class="line"><span class="cl">The fastest Formula <span class="m">1 </span>pit stop (1.8 seconds)
</span></span><span class="line"><span class="cl">The fastest 1/4 mile drag race time (3.6 seconds)
</span></span><span class="line"><span class="cl">The fastest Rubik's cube solve (4.2 seconds)
</span></span><span class="line"><span class="cl">The fastest 40-yard time <span class="nb">at </span>the NFL Combine (4.2 seconds)
</span></span><span class="line"><span class="cl">The fastest <span class="m">1 </span>liter beer chug (4.4 seconds)
</span></span><span class="line"><span class="cl">A skippable Youtube ad (5.0 seconds)
</span></span><span class="line"><span class="cl">A full bull ride (8.0 seconds)
</span></span><span class="line"><span class="cl">The fastest 100m sprint (9.6 seconds)
</span></span><span class="line"><span class="cl">The Wright Brothers <span class="nb">first </span>flight (12.0 seconds)
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><h3 id="user-interface">User Interface</h3>
<p>We are also going to build the interface shown above, starting with a
<a href="https://docs.factorcode.org/content/word-gadget%2Cui.gadgets.html">gadget</a>
that stores a <a href="https://docs.factorcode.org/content/article-timers.html">timer</a>,
a total elapsed time in milliseconds, and a meeting start
<a href="https://docs.factorcode.org/content/word-timestamp,calendar.html">timestamp</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">meeting-gadget</span> < <span class="nc">track</span> <span class="nv">timer</span> <span class="nv">total</span> <span class="nv">start</span> <span class="k">;
</span></span></span></code></pre></div><p>There are different strategies for building user interfaces, depending on
the data model, and how composable or how separate the elements being
displayed are from each other.</p>
<p>In the interest of tutorials, I want to demonstrate one strategy below that
uses <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a> to bind
the elements to each other, allowing them to be updated in a kind of
reactive manner. It is a long word, but the structure of the code matches
somewhat to the rendered output that we are going for.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf"><meeting-gadget></span> <span class="nf">( -- </span><span class="nv">gadget</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> vertical meeting-gadget new-track <span class="nb">dup </span>:> meeting
</span></span><span class="line"><span class="cl"> COLOR: #f7f08b <solid> >>interior
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>>>total
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">""</span> <label> :> current-text
</span></span><span class="line"><span class="cl"> <span class="s">""</span> <label> :> current-time
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">""</span> <label> :> total-time
</span></span><span class="line"><span class="cl"> <span class="s">""</span> <label> :> start-time
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> THINGS-THAT-TAKE-TIME <span class="nb">first first2 </span>human-time
</span></span><span class="line"><span class="cl"> [ <label> ] <span class="nb">bi@ </span>:> <span class="nf">( </span><span class="nv">next-text</span> <span class="nv">next-time</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> meeting total>>
</span></span><span class="line"><span class="cl"> meeting [ now <span class="nb">dup </span>] change-start <span class="nb">drop swap </span>time- duration>milliseconds <span class="nb">+
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>meeting total<<
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>next-thing-that-takes-time <span class="nb">first2
</span></span></span><span class="line"><span class="cl"> <span class="nb">over </span>next-text string>> <span class="nb">= </span>[ <span class="nb">2drop </span>] [
</span></span><span class="line"><span class="cl"> next-text string>> current-text string<<
</span></span><span class="line"><span class="cl"> next-time string>> current-time string<<
</span></span><span class="line"><span class="cl"> human-time
</span></span><span class="line"><span class="cl"> next-time string<<
</span></span><span class="line"><span class="cl"> next-text string<<
</span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> human-time total-time string<<
</span></span><span class="line"><span class="cl"> ] <span class="no">f </span><span class="m">100 </span>milliseconds <timer> >>timer
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> vertical <track>
</span></span><span class="line"><span class="cl"> current-text <span class="no">f </span>track-add
</span></span><span class="line"><span class="cl"> current-time <span class="no">f </span>track-add
</span></span><span class="line"><span class="cl"> <span class="s">"This meeting is longer than..."</span> <labeled-gadget> <span class="no">f </span>track-add
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> vertical <track>
</span></span><span class="line"><span class="cl"> total-time <span class="no">f </span>track-add
</span></span><span class="line"><span class="cl"> start-time <span class="no">f </span>track-add
</span></span><span class="line"><span class="cl"> <span class="s">"It has been going on for..."</span> <labeled-gadget> <span class="no">f </span>track-add
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> vertical <track>
</span></span><span class="line"><span class="cl"> next-text <span class="no">f </span>track-add
</span></span><span class="line"><span class="cl"> next-time <span class="no">f </span>track-add
</span></span><span class="line"><span class="cl"> <span class="s">"The next milestone is..."</span> <labeled-gadget> <span class="no">f </span>track-add
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"Start"</span> <label> :> start-label
</span></span><span class="line"><span class="cl"> <span class="s">"Reset"</span> <label> :> reset-label
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> horizontal <track>
</span></span><span class="line"><span class="cl"> start-label [
</span></span><span class="line"><span class="cl"> <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> meeting
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>start>> [
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>>>total now timestamp>hms
</span></span><span class="line"><span class="cl"> <span class="s">"Started at "</span> <span class="nb">prepend </span>start-time string<<
</span></span><span class="line"><span class="cl"> ] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> now >>start
</span></span><span class="line"><span class="cl"> timer>> <span class="nb">dup </span>thread>>
</span></span><span class="line"><span class="cl"> [ stop-timer <span class="s">"Resume"</span> start-label string<< ]
</span></span><span class="line"><span class="cl"> [ start-timer <span class="s">"Pause"</span> start-label string<< ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <border-button> <span class="no">f </span>track-add
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> reset-label [
</span></span><span class="line"><span class="cl"> <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> meeting <span class="m">0 </span>>>total <span class="no">f </span>>>start timer>> stop-timer
</span></span><span class="line"><span class="cl"> <span class="s">"Start"</span> start-label string<<
</span></span><span class="line"><span class="cl"> <span class="s">""</span> current-text string<<
</span></span><span class="line"><span class="cl"> <span class="s">""</span> current-time string<<
</span></span><span class="line"><span class="cl"> <span class="s">""</span> total-time string<<
</span></span><span class="line"><span class="cl"> <span class="s">""</span> start-time string<<
</span></span><span class="line"><span class="cl"> THINGS-THAT-TAKE-TIME <span class="nb">first first2 </span>human-time
</span></span><span class="line"><span class="cl"> next-time string<<
</span></span><span class="line"><span class="cl"> next-text string<<
</span></span><span class="line"><span class="cl"> ] <border-button> <span class="no">f </span>track-add
</span></span><span class="line"><span class="cl"> <span class="no">f </span>track-add <span class="k">;
</span></span></span></code></pre></div><p>And, then a main entrypoint to open a window when the vocabulary is
<a href="https://docs.factorcode.org/content/word-run,vocabs.loader.html">run</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">MAIN-WINDOW: time-my-meeting
</span></span><span class="line"><span class="cl"> { { title <span class="s">"Time My Meeting"</span> } }
</span></span><span class="line"><span class="cl"> <meeting-gadget> >>gadgets <span class="k">;
</span></span></span></code></pre></div><p>With a smidge of improved fonts and better gadget spacing, this is now
available in my
<a href="https://github.com/mrjbq7/re-factor/blob/master/time-my-meeting/time-my-meeting.factor">GitHub</a>.</p>
<p>You can try it out!</p>
Factor Language Tutorial
https://re.factorcode.org/2024/04/factor-language-tutorial.html
Sun, 28 Apr 2024 09:00:00 -0700https://re.factorcode.org/2024/04/factor-language-tutorial.html<p>A few days ago, one of our <a href="https://discord.gg/QxJYZx3QDf">Factor Discord
server</a> members posted a <a href="https://odysee.com/@FactorLanguageTutorial:6/factor-tutorial:1">video
tutorial</a> that
they made for <a href="https://factorcode.org">Factor</a>. It is a pretty neat hour long
introduction going over a lot of features that users new to the language might
be interested in:</p>
<blockquote>
<p>This is an introductory tutorial for a stack-based (concatenative)
programming language Factor. It covers some basic language constructs and a
few features of the interactive development environment that is shipped with
Factor.</p>
</blockquote>
<p>You can watch it here:</p>
<iframe id="odysee-iframe" style="width:100%; aspect-ratio:16 / 9;" src="https://odysee.com/$/embed/@FactorLanguageTutorial:6/factor-tutorial:1?r=68hNrCmCWxHyHNseqatNf4ULivqbZcT3" allowfullscreen></iframe>
Reverse Vowels
https://re.factorcode.org/2024/02/reverse-vowels.html
Mon, 12 Feb 2024 12:00:00 -0700https://re.factorcode.org/2024/02/reverse-vowels.html<p>Our task today is to “reverse vowels of a string”. This sounds like (and
probably is) a <a href="https://www.codinginterview.com">coding interview question</a>
as well as a <a href="https://leetcode.com/problems/reverse-vowels-of-a-string/description/">LeetCode
problem</a>,
a <a href="https://www.codewars.com/kata/585db3e8eec141ce9a00008f">Codewars kata</a>,
and the second task in the <a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-254/">Perl Weekly Challenge
#254</a>.</p>
<p><em>If you don’t want spoilers, maybe stop reading here!</em></p>
<hr>
<p>We are going to use <a href="https://factorcode.org">Factor</a> to solve this problem as
well as a variant that is a bit more challenging.</p>
<h3 id="lets-reverse-the-vowels">Let’s Reverse The Vowels</h3>
<p>One of the benefits of the <a href="https://en.wikipedia.org/wiki/Monorepo">monorepo
approach</a> that we have taken to
building the extensive <a href="https://docs.factorcode.org/content/article-vocab-index.html">Factor standard
library</a> is
developing higher-level words that solve specific kind of tasks.</p>
<p>One of those is
<a href="https://docs.factorcode.org/content/word-arg-where,sequences.extras.html">arg-where</a>
– currently in the miscellaneous <a href="https://docs.factorcode.org/content/vocab-sequences.extras.html">sequences.extras
vocabulary</a> –
which we can use to find all the indices in a string that contain a
<a href="https://docs.factorcode.org/content/word-vowel__que__,english.html">vowel?</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"hello"</span> [ vowel? ] arg-where <span class="m">.
</span></span></span><span class="line"><span class="cl">V{ <span class="m">1 4 </span>}
</span></span></code></pre></div><p>We’ll want to group the beginning and ending indices, ignoring the middle index
if the number of indices is odd since it would not change:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">split-indices</span> <span class="nf">( </span><span class="nv">indices</span> <span class="nf">-- </span><span class="nv">head</span> <span class="nv">tail</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup length 2/ </span>[ <span class="nb">head-slice </span>] [ <span class="nb">tail-slice* </span>] <span class="nb">2bi </span><span class="k">;
</span></span></span></code></pre></div><p>We can then build a word to reverse specified indices:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">reverse-indices</span> <span class="nf">( </span><span class="nv">str</span> <span class="nv">indices</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> split-indices <span class="nb"><reversed> </span>[ <span class="nb">pick exchange </span>] <span class="nb">2each </span><span class="k">;
</span></span></span></code></pre></div><p>And then use it to reverse the vowels:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">reverse-vowels</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>>lower [ vowel? ] arg-where reverse-indices <span class="k">;
</span></span></span></code></pre></div><p>And see how it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"factor"</span> reverse-vowels <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"foctar"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"concatenative"</span> reverse-vowels <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"cencitanetavo"</span>
</span></span></code></pre></div><p>Pretty cool!</p>
<h3 id="lets-reverse-the-vowels-maintain-the-case">Let’s Reverse The Vowels, Maintain The Case</h3>
<p>A somewhat more challenging task is to reverse the vowels, and to swap their
<a href="https://en.wikipedia.org/wiki/Letter_case">letter case</a>.</p>
<p>Let’s start by building a word to swap the case of two letters:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">swap-case</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">a'</span> <span class="nv">b'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">2dup </span>[ letter? ] <span class="nb">bi@ 2array </span>{
</span></span><span class="line"><span class="cl"> { { <span class="no">t f </span>} [ [ ch>upper ] [ ch>lower ] <span class="nb">bi* </span>] }
</span></span><span class="line"><span class="cl"> { { <span class="no">f t </span>} [ [ ch>lower ] [ ch>upper ] <span class="nb">bi* </span>] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div><p>And then another word to
<a href="https://docs.factorcode.org/content/word-exchange,sequences.html">exchange</a>
two indices, but also swap their case:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">exchange-case</span> <span class="nf">( </span><span class="nv">i</span> <span class="nv">j</span> <span class="nv">seq</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ '[ _ <span class="nb">nth </span>] <span class="nb">bi@ </span>swap-case ]
</span></span><span class="line"><span class="cl"> [ '[ _ <span class="nb">set-nth </span>] <span class="nb">bi@ </span>] <span class="nb">3bi </span><span class="k">; inline
</span></span></span></code></pre></div><p>A word to reverse the indices, but also swap their case:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">reverse-indices-case</span> <span class="nf">( </span><span class="nv">str</span> <span class="nv">indices</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> split-indices <span class="nb"><reversed> </span>[ <span class="nb">pick </span>exchange-case ] <span class="nb">2each </span><span class="k">;
</span></span></span></code></pre></div><p>And, finally, a word to reverse the vowels, but also swap their case:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">reverse-vowels-case</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>>lower [ vowel? ] arg-where reverse-indices-case <span class="k">;
</span></span></span></code></pre></div><p>And then see how it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"FActor"</span> reverse-vowels-case <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"FOctar"</span>
</span></span></code></pre></div><p>A pretty fun problem!</p>
Dragonbox
https://re.factorcode.org/2024/02/dragonbox.html
Sun, 11 Feb 2024 19:30:00 -0700https://re.factorcode.org/2024/02/dragonbox.html<p>One of the challenging problems in <a href="https://en.wikipedia.org/wiki/Computer_science">computer
science</a> is to efficiently take
a binary representation of a floating-point number and convert it to the
“shortest decimal representation” that will roundtrip back to the same
floating-point number when it is parsed.</p>
<p>A few days ago, one of the members of <a href="https://discord.gg/QxJYZx3QDf">the Factor Discord
server</a> posted about an issue they were
having where three separate floating-point numbers printed as the same
decimal value:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> 0x1.1ffffffffffffp7 <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">144.0
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> 0x1.2p7 <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">144.0
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> 0x1.2000000000001p7 <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">144.0
</span></span></span></code></pre></div><p>Well, that’s not ideal!</p>
<p>And you can see that in other languages like <a href="https://python.org">Python</a>, they
parse properly into three distinct values:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">>>></span> <span class="nb">float</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="s1">'0x1.1ffffffffffffp7'</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mf">143.99999999999997</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">>>></span> <span class="nb">float</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="s1">'0x1.2p7'</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mf">144.0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">>>></span> <span class="nb">float</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="s1">'0x1.2000000000001p7'</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mf">144.00000000000003</span>
</span></span></code></pre></div><p>In the process of investigating this issue, I re-discovered a few algorithms
that have been developed to do this. There is a neat project called
<a href="https://github.com/abolz/Drachennest">Drachennest</a> that investigates the
relative performance of several of these algorithms and claims:</p>
<blockquote>
<p>Grisu3, Ryu, Schubfach, and Dragonbox are optimal, i.e. the output string</p>
<ol>
<li>rounds back to the input number when read in,</li>
<li>is as short as possible,</li>
<li>is as close to the input number as possible.</li>
</ol>
</blockquote>
<p>Well, it turns out that the “Dragonbox” algorithm is one of the current best
and is described in a paper called <a href="https://github.com/jk-jeon/dragonbox/blob/master/other_files/Dragonbox.pdf">A New Floating-Point Binary-to-Decimal
Conversion
Algorithm</a>
as well as a fantastic <a href="https://github.com/jk-jeon/dragonbox">reference implementation of Dragonbox in
C++</a>.</p>
<p>I was able to quickly fix the bug by temporarily using a “modern formatting
library” called <a href="https://fmt.dev">{fmt}</a> that works in C++11 and provides a
version of the C++20 function
<a href="https://en.cppreference.com/w/cpp/utility/format/format">std::format</a>, but
thought it would be a good idea to implement the Dragonbox algorithm someday
in pure Factor code and filed an issue to track that idea.</p>
<p>Well, one of our awesome contributors,
<a href="https://github.com/gifti258">Giftpflanze</a>, jumped in and <a href="https://github.com/factor/factor/pull/2943">implemented
Dragonbox in Factor</a> – providing a
very readable and understandable and <a href="https://re.factorcode.org/2011/07/concatenative-thinking.html">nicely
concatenative</a> version – and it was
merged today!</p>
<p>Not only does this solve the issue of decimal representation of floats, but it
provides quite a large speedup to our <a href="https://github.com/factor/factor/blob/master/extra/benchmark/parse-float/parse-float.factor">float-parsing
benchmark</a>:</p>
<p>Currently in <a href="https://re.factorcode.org/2023/08/factor-0-99-now-available.html">Factor 0.99</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ parse-float-benchmark ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">3.181906583 </span>seconds
</span></span></code></pre></div><p>And now after the patch:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ parse-float-benchmark ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.378132792 </span>seconds
</span></span></code></pre></div><p>Very impressive!</p>
<p>I’m excited to say that this is now available in the <a href="https://github.com/factor/factor">development version of
Factor</a>.</p>
Divmods
https://re.factorcode.org/2024/02/divmods.html
Fri, 02 Feb 2024 08:00:00 -0700https://re.factorcode.org/2024/02/divmods.html<p>There’s a discussion on <a href="https://discuss.python.org/t/support-multiple-divisors-in-divmod/33109">support multiple divisors in
divmod()</a>
on the Python.</p>
<blockquote>
<p>So instead of</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">minutes</span><span class="p">,</span> <span class="n">seconds</span> <span class="o">=</span> <span class="nb">divmod</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="mi">60</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">hours</span><span class="p">,</span> <span class="n">minutes</span> <span class="o">=</span> <span class="nb">divmod</span><span class="p">(</span><span class="n">minutes</span><span class="p">,</span> <span class="mi">60</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">days</span><span class="p">,</span> <span class="n">hours</span> <span class="o">=</span> <span class="nb">divmod</span><span class="p">(</span><span class="n">hours</span><span class="p">,</span> <span class="mi">24</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">weeks</span><span class="p">,</span> <span class="n">days</span> <span class="o">=</span> <span class="nb">divmod</span><span class="p">(</span><span class="n">days</span><span class="p">,</span> <span class="mi">7</span><span class="p">)</span>
</span></span></code></pre></div><p>you could write:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">weeks</span><span class="p">,</span> <span class="n">days</span><span class="p">,</span> <span class="n">hours</span><span class="p">,</span> <span class="n">minutes</span><span class="p">,</span> <span class="n">seconds</span> <span class="o">=</span> <span class="nb">divmod</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">60</span><span class="p">,</span> <span class="mi">60</span><span class="p">)</span>
</span></span></code></pre></div><p>Sample implementation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">new_divmod</span><span class="p">(</span><span class="n">dividend</span><span class="p">,</span> <span class="o">*</span><span class="n">divisors</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="ow">not</span> <span class="n">divisors</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s1">'required at least one divisor'</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">remainders</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">divisor</span> <span class="ow">in</span> <span class="nb">reversed</span><span class="p">(</span><span class="n">divisors</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">dividend</span><span class="p">,</span> <span class="n">remainder</span> <span class="o">=</span> <span class="n">old_divmod</span><span class="p">(</span><span class="n">dividend</span><span class="p">,</span> <span class="n">divisor</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">remainders</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">remainder</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">(</span><span class="n">dividend</span><span class="p">,</span> <span class="o">*</span><span class="n">remainders</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
</span></span></code></pre></div></blockquote>
<p>Along with the sample implementation in <a href="https://python.org">Python</a> above, the
original author provides some thoughts on whether the order of arguments should
be reversed or not, and some of the comments in the thread discuss various
implementation details and some other use-cases for this approach.</p>
<p>You can see how it might work by trying the code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">>>></span> <span class="n">new_divmod</span><span class="p">(</span><span class="mi">1234567</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">60</span><span class="p">,</span> <span class="mi">60</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">56</span><span class="p">,</span> <span class="mi">7</span><span class="p">)</span>
</span></span></code></pre></div><p>Okay, so how might we do this in <a href="https://factorcode.org">Factor</a>?</p>
<p>Well, our version of <code>divmod</code> is
<a href="https://docs.factorcode.org/content/word-__slash__mod,math.html">/mod</a> and we
could just run it a few times to get the result:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1234567 60 </span><span class="nb">/mod swap </span><span class="m">60 </span><span class="nb">/mod swap </span><span class="m">24 </span><span class="nb">/mod swap </span><span class="m">7 </span><span class="nb">/mod swap
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- Data stack:
</span></span><span class="line"><span class="cl"><span class="m">7
</span></span></span><span class="line"><span class="cl"><span class="m">56
</span></span></span><span class="line"><span class="cl"><span class="m">6
</span></span></span><span class="line"><span class="cl"><span class="m">0
</span></span></span><span class="line"><span class="cl"><span class="m">2
</span></span></span></code></pre></div><p>Alternatively, we could pass the arguments as a sequence and return the result
as a sequence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1234567 </span>{ <span class="m">60 60 24 7 </span>} [ <span class="nb">/mod </span>] <span class="nb">map swap suffix
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- Data stack:
</span></span><span class="line"><span class="cl">{ <span class="m">7 56 6 0 2 </span>}
</span></span></code></pre></div><p>Or, perhaps, we could make a
<a href="https://docs.factorcode.org/content/article-macros.html">macro</a>, taking the
input argument as a sequence, but generating code to put the result onto the
stack:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MACRO:</span> <span class="nf">/mods</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">quot</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ '[ _ <span class="nb">/mod swap </span>] ] <span class="nb">map concat </span><span class="k">;
</span></span></span></code></pre></div><p>And then use it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1234567 </span>{ <span class="m">60 60 24 7 </span>} /mods
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- Data stack:
</span></span><span class="line"><span class="cl"><span class="m">7
</span></span></span><span class="line"><span class="cl"><span class="m">56
</span></span></span><span class="line"><span class="cl"><span class="m">6
</span></span></span><span class="line"><span class="cl"><span class="m">0
</span></span></span><span class="line"><span class="cl"><span class="m">2
</span></span></span></code></pre></div><p>Kind of an interesting idea!</p>
Crontab
https://re.factorcode.org/2024/01/crontab.html
Wed, 31 Jan 2024 08:00:00 -0700https://re.factorcode.org/2024/01/crontab.html<p><a href="https://cron.com">Cron</a> might be the latest, greatest, and coolest
“<em>next-generation calendar</em>” as well as now a product called <a href="https://www.notion.so/product/calendar">Notion
Calendar</a>. But in the good ol’ days,
<a href="https://en.wikipedia.org/wiki/Cron">cron</a> was instead known as:</p>
<blockquote>
<p>The <code>cron</code> command-line utility is a <a href="https://en.wikipedia.org/wiki/Job_scheduler">job
scheduler</a> on <a href="https://en.wikipedia.org/wiki/Operating_system">Unix-like
operating
systems</a>. Users who set up and
maintain software environments use cron to
schedule jobs (commands or <a href="https://en.wikipedia.org/wiki/Shell_script">shell
scripts</a>), also known as <strong>cron
jobs</strong>, to run periodically at fixed times, dates, or intervals. It typically
automates system maintenance or administration—though its general-purpose
nature makes it useful for things like downloading files from the Internet
and downloading email at regular intervals.</p>
</blockquote>
<p>There are implementations of <code>crond</code> – the cron daemon – on most operating
systems. Many of them have standardized on a
<a href="https://linux.die.net/man/5/crontab">crontab</a> format that looks something like
this:</p>
<pre tabindex="0"><code># ┌───────────── minute (0–59)
# │ ┌───────────── hour (0–23)
# │ │ ┌───────────── day of the month (1–31)
# │ │ │ ┌───────────── month (1–12)
# │ │ │ │ ┌───────────── day of the week (0–6) (Sunday to Saturday;
# │ │ │ │ │ 7 is also Sunday on some systems)
# │ │ │ │ │
# │ │ │ │ │
# * * * * * <command to execute>
</code></pre><p>At first (and sometimes second and third and fourth) glance, this looks a bit
inscrutable, and so websites such as <a href="https://crontab.guru">crontab guru</a> pop
up to help you unpack and explain when a <strong>cronentry</strong> is expected to be run.</p>
<p>I thought it would be fun to build a parser for these cronentries in
<a href="https://factorcode.org">Factor</a>.</p>
<p>Let’s start by defining a <code>cronentry</code> type:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">cronentry</span> <span class="nv">minutes</span> <span class="nv">hours</span> <span class="nv">days</span> <span class="nv">months</span> <span class="nv">days-of-week</span> <span class="nv">command</span> <span class="k">;
</span></span></span></code></pre></div><p>For each component, there is a variety of allowed inputs:</p>
<ul>
<li>all values in the range: <code>*</code></li>
<li>list of values: <code>3,5,7</code></li>
<li>range of values: <code>10-15</code></li>
<li>step values: <code>1-20/5</code></li>
<li>random value in range: <code>10~30</code></li>
</ul>
<p>We build a <code>parse-value</code> word that will take an <code>input</code> string, a <code>quot</code>
to parse the input, and a <code>seq</code> of possible values, as well as a
<code>parse-range</code> word to help with optional starting and ending input values.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">parse-range</span> <span class="nf">( </span><span class="nv">from/f</span> <span class="nv">to/f</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">input</span> <span class="nf">-- </span><span class="nv">value</span> <span class="nf">) </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">from</span> <span class="nv">to</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> from/f [ seq <span class="nb">first </span>] quot <span class="nb">if-empty
</span></span></span><span class="line"><span class="cl"> to/f [ seq <span class="nb">last </span>] quot <span class="nb">if-empty </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">parse-value</span> <span class="nf">( </span><span class="nv">input</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">input</span> <span class="nf">-- </span><span class="nv">value</span> <span class="nf">) </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">value</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> input {
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="s">"*"</span> <span class="nb">= </span>] [ <span class="nb">drop </span>seq ] }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> { [ <span class="sc">CHAR: , </span><span class="nb">over member? </span>] [
</span></span><span class="line"><span class="cl"> <span class="s">","</span> split [ quot seq parse-value ] <span class="nb">map concat </span>] }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> { [ <span class="sc">CHAR: / </span><span class="nb">over member? </span>] [
</span></span><span class="line"><span class="cl"> <span class="s">"/"</span> split1 [
</span></span><span class="line"><span class="cl"> quot seq parse-value <span class="nb">dup length </span><span class="m">1 </span><span class="nb">=
</span></span></span><span class="line"><span class="cl"> [ seq <span class="nb">swap first </span>seq <span class="nb">index </span>seq <span class="nb">length </span>]
</span></span><span class="line"><span class="cl"> [ <span class="m">0 </span><span class="nb">over length </span>] <span class="nb">if </span><span class="m">1 </span><span class="nb">-
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">dip </span>string>number <range> <span class="nb">swap nths </span>] }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> { [ <span class="sc">CHAR: - </span><span class="nb">over member? </span>] [
</span></span><span class="line"><span class="cl"> <span class="s">"-"</span> split1 quot seq parse-range [a..b] ] }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> { [ <span class="sc">CHAR: ~ </span><span class="nb">over member? </span>] [
</span></span><span class="line"><span class="cl"> <span class="s">"~"</span> split1 quot seq parse-range [a..b] random <span class="nb">1array </span>] }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ quot <span class="nb">call 1array </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span>members sort <span class="k">; inline recursive
</span></span></span></code></pre></div><p>We can then make <code>parse-cronentry</code> to parse the entry description, handling
days and months differently to allow their abbreviations to be passed as input
(e.g., <code>sun</code> for Sunday or <code>jan</code> for January).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-day</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ string>number <span class="nb">dup </span><span class="m">7 </span><span class="nb">= </span>[ <span class="nb">drop </span><span class="m">0 </span>] <span class="nb">when </span>] [
</span></span><span class="line"><span class="cl"> >lower $[ day-abbreviations3 [ >lower ] <span class="nb">map </span>] <span class="nb">index
</span></span></span><span class="line"><span class="cl"> ] ?unless <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-month</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ string>number ] [
</span></span><span class="line"><span class="cl"> >lower $[ month-abbreviations [ >lower ] <span class="nb">map </span>] <span class="nb">index
</span></span></span><span class="line"><span class="cl"> ] ?unless <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-cronentry</span> <span class="nf">( </span><span class="nv">entry</span> <span class="nf">-- </span><span class="nv">cronentry</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">" "</span> split1 <span class="s">" "</span> split1 <span class="s">" "</span> split1 <span class="s">" "</span> split1 <span class="s">" "</span> split1 {
</span></span><span class="line"><span class="cl"> [ [ string>number ] T{ range <span class="no">f </span><span class="m">0 60 1 </span>} parse-value ]
</span></span><span class="line"><span class="cl"> [ [ string>number ] T{ range <span class="no">f </span><span class="m">0 24 1 </span>} parse-value ]
</span></span><span class="line"><span class="cl"> [ [ string>number ] T{ range <span class="no">f </span><span class="m">1 31 1 </span>} parse-value ]
</span></span><span class="line"><span class="cl"> [ [ parse-month ] T{ range <span class="no">f </span><span class="m">1 12 1 </span>} parse-value ]
</span></span><span class="line"><span class="cl"> [ [ parse-day ] T{ circular <span class="no">f </span>T{ range <span class="no">f </span><span class="m">0 7 1 </span>} <span class="m">1 </span>} parse-value ]
</span></span><span class="line"><span class="cl"> [ ]
</span></span><span class="line"><span class="cl"> } <span class="nb">spread </span>cronentry <span class="nb">boa </span><span class="k">;
</span></span></span></code></pre></div><p>We can try using it to see what a parsed cronentry looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"20-30/5 5 */5 * * /path/to/command"</span> parse-cronentry <span class="m">.
</span></span></span><span class="line"><span class="cl">T{ cronentry
</span></span><span class="line"><span class="cl"> { minutes { <span class="m">20 25 30 </span>} }
</span></span><span class="line"><span class="cl"> { hours { <span class="m">5 </span>} }
</span></span><span class="line"><span class="cl"> { days { <span class="m">1 6 11 16 21 26 31 </span>} }
</span></span><span class="line"><span class="cl"> { months { <span class="m">1 2 3 4 5 6 7 8 9 10 11 12 </span>} }
</span></span><span class="line"><span class="cl"> { days-of-week { <span class="m">0 1 2 3 4 5 6 </span>} }
</span></span><span class="line"><span class="cl"> { command <span class="s">"/path/to/command"</span> }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Now that we have that working, we can use it to calculate the
<code>next-time-after</code> a given
<a href="https://docs.factorcode.org/content/word-timestamp,calendar.html">timestamp</a>
that the cronentry will trigger, applying a waterfall to rollover the timestamp
until a valid one is found:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">(next-time-after)</span> <span class="nf">( </span><span class="nv">cronentry</span> <span class="nv">timestamp</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="no">f </span><span class="c">! should we keep searching for a matching time</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> timestamp month>> :> month
</span></span><span class="line"><span class="cl"> cronentry months>> [ month <span class="nb">>= </span>] <span class="nb">find nip
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>month <span class="nb">= </span>[ <span class="nb">drop </span>] [
</span></span><span class="line"><span class="cl"> [ cronentry months>> <span class="nb">first </span>timestamp <span class="m">1 </span>+year <span class="nb">drop </span>] <span class="nb">unless*
</span></span></span><span class="line"><span class="cl"> timestamp <span class="m">1 </span>>>day <span class="m">0 </span>>>hour <span class="m">0 </span>>>minute month<< <span class="nb">drop </span><span class="no">t
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> timestamp day-of-week :> weekday
</span></span><span class="line"><span class="cl"> cronentry days-of-week>> [ weekday <span class="nb">>= </span>] <span class="nb">find nip </span>[
</span></span><span class="line"><span class="cl"> cronentry days-of-week>> <span class="nb">first </span><span class="m">7 </span><span class="nb">+
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">unless* </span>weekday <span class="nb">- </span>:> days-to-weekday
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> timestamp day>> :> day
</span></span><span class="line"><span class="cl"> cronentry days>> [ day <span class="nb">>= </span>] <span class="nb">find nip </span>[
</span></span><span class="line"><span class="cl"> cronentry days>> <span class="nb">first </span>timestamp days-in-month <span class="nb">+
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">unless* </span>day <span class="nb">- </span>:> days-to-day
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> cronentry days-of-week>> <span class="nb">length </span><span class="m">7 </span><span class="nb">=
</span></span></span><span class="line"><span class="cl"> cronentry days>> <span class="nb">length </span><span class="m">31 </span><span class="nb">= 2array
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { { <span class="no">f t </span>} [ days-to-weekday ] }
</span></span><span class="line"><span class="cl"> { { <span class="no">t f </span>} [ days-to-day ] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>days-to-weekday days-to-day min ]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span>[
</span></span><span class="line"><span class="cl"> timestamp <span class="m">0 </span>>>hour <span class="m">0 </span>>>minute <span class="nb">swap </span>+day <span class="nb">2drop </span><span class="no">t
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">unless-zero
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> timestamp hour>> :> hour
</span></span><span class="line"><span class="cl"> cronentry hours>> [ hour <span class="nb">>= </span>] <span class="nb">find nip
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>hour <span class="nb">= </span>[ <span class="nb">drop </span>] [
</span></span><span class="line"><span class="cl"> [ cronentry hours>> <span class="nb">first </span>timestamp <span class="m">1 </span>+day <span class="nb">drop </span>] <span class="nb">unless*
</span></span></span><span class="line"><span class="cl"> timestamp <span class="m">0 </span>>>minute hour<< <span class="nb">drop </span><span class="no">t
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> timestamp minute>> :> minute
</span></span><span class="line"><span class="cl"> cronentry minutes>> [ minute <span class="nb">>= </span>] <span class="nb">find nip
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>minute <span class="nb">= </span>[ <span class="nb">drop </span>] [
</span></span><span class="line"><span class="cl"> [ cronentry minutes>> <span class="nb">first </span>timestamp <span class="m">1 </span>+hour <span class="nb">drop </span>] <span class="nb">unless*
</span></span></span><span class="line"><span class="cl"> timestamp minute<< <span class="nb">drop </span><span class="no">t
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ cronentry timestamp (next-time-after) ] <span class="nb">when </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-time-after</span> <span class="nf">( </span><span class="nv">cronentry</span> <span class="nv">timestamp</span> <span class="nf">-- </span><span class="nv">timestamp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>cronentry? [ parse-cronentry ] <span class="nb">unless </span>]
</span></span><span class="line"><span class="cl"> [ <span class="m">1 </span>minutes time+ <span class="m">0 </span>>>second ] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> [ (next-time-after) ] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div><p>This is great, because we can find the next time that a cronentry will trigger.
For example, if we wanted to specify something to trigger at midnight on every
leap day:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"0 0 29 2 *"</span> now next-time-after timestamp>rfc822 <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"Thu, 29 Feb 2024 00:00:00 -0800"</span>
</span></span></code></pre></div><p>Or even, the next several times that the cronentry will trigger:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"0 0 29 2 *"</span> now <span class="m">5 </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">dupd </span>next-time-after [ timestamp>rfc822 <span class="m">. </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">times 2drop
</span></span></span><span class="line"><span class="cl"><span class="s">"Thu, 29 Feb 2024 00:00:00 -0800"</span>
</span></span><span class="line"><span class="cl"><span class="s">"Tue, 29 Feb 2028 00:00:00 -0800"</span>
</span></span><span class="line"><span class="cl"><span class="s">"Sun, 29 Feb 2032 00:00:00 -0800"</span>
</span></span><span class="line"><span class="cl"><span class="s">"Fri, 29 Feb 2036 00:00:00 -0800"</span>
</span></span><span class="line"><span class="cl"><span class="s">"Wed, 29 Feb 2040 00:00:00 -0800"</span>
</span></span></code></pre></div><p>This is available in the <a href="https://github.com/factor/factor/blob/master/extra/crontab/crontab.factor">crontab
vocabulary</a>
including some features such as support for aliases (e.g., <code>@daily</code> and
<code>@weekly</code>) and some higher-level words for working with crontabs and
cronentries.</p>
Codewars
https://re.factorcode.org/2024/01/codewars.html
Mon, 29 Jan 2024 08:00:00 -0700https://re.factorcode.org/2024/01/codewars.html<p><a href="https://codewars.com">Codewars</a> is an online platform for learning programming
languages by solving small programming exercises called “kata” and subsequently
increasing your degree of proficiency via levels of “kyu”. It has useful
features such as extensive unit tests, leaderboards, allies for allowing
friendly competition, and discussion boards.</p>
<p>It supports an incredible number of programming languages – albeit some of these
are in “beta” status – including <a href="https://factorcode.org">Factor</a>!</p>
<p>
<img src="https://re.factorcode.org/images/2024-01-29-codewars.png" alt="" width="902" height="540" />
</p>
<p>I wanted to draw attention to the <a href="https://codewars.com">Codewars</a> website and
point out that it has newly released support for <a href="https://re.factorcode.org/2023/08/factor-0-99-now-available.html">Factor
0.99</a> due to great community support
and some work on the <a href="https://github.com/codewars/testest">Codewars test
vocabulary</a> that was developed
specifically for use with the Codewars system.</p>
<p>It’s pretty fun to complete the katas and then see the solutions that other
users have contributed.</p>
<p>Give it a try!</p>
Special Numbers
https://re.factorcode.org/2024/01/special-numbers.html
Mon, 15 Jan 2024 08:00:00 -0700https://re.factorcode.org/2024/01/special-numbers.html<p>Lots of <a href="https://www.skillsyouneed.com/num/special-numbers-concepts.html">numbers are
special</a> in
various definitions of specialness. This often forms the basis of different
programming challenges. In the case of the most recent <a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-252/">Perl Weekly Challenge
#252</a>, the
problem statement declares that a number is “special” in this way:</p>
<blockquote>
<p>You are given an array of integers, <code>@ints</code>.</p>
<p>Write a script to find the sum of the squares of all special elements of the
given array.</p>
<p>An element <code>$int[i]</code> of <code>@ints</code> is called special if <code>i</code> divides <code>n</code>,
i.e. <code>n % i == 0</code>, where <code>n</code> is the length of the given array. Also the
array is 1-indexed for the task.</p>
</blockquote>
<p>And it gives two examples, which we can use as test cases later when we solve
this in <a href="https://factorcode.org">Factor</a>.</p>
<p><em><strong>Spoiler Alert</strong>: This weekly challenge deadline is due in a few days from now
(on January 21, 2024 at 23:59). This blog post provides some solutions to this
challenge. Please don’t read on if you intend to complete the challenge on your
own.</em></p>
<h2 id="solution">Solution</h2>
<p>Let’s find the special indices – which are just the
<a href="https://docs.factorcode.org/content/word-divisors,math.primes.factors.html">divisors</a>
of the length of the input sequence – and then take the elements at those
special indices:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">special-numbers</span> <span class="nf">( </span><span class="nv">ints</span> <span class="nf">-- </span><span class="nv">ints'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>divisors <span class="m">1 </span>v-n ] [ <span class="nb">nths </span>] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>And so, we can solve this problem for both provided examples:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="m">21 </span>} [ { <span class="m">1 2 3 4 </span>} special-numbers sum-of-squares ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="m">63 </span>} [ { <span class="m">2 7 1 19 18 3 </span>} special-numbers sum-of-squares ] unit-test
</span></span></code></pre></div><p>And a “script”, if we wanted to take input from the command-line, as requested:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">[</span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">readln </span>] [
</span></span><span class="line"><span class="cl"> split-words <span class="nb">harvest </span>[ string>number ] <span class="nb">map
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>special-numbers sum-of-squares
</span></span><span class="line"><span class="cl"> <span class="s">"%u => %u\n"</span> printf
</span></span><span class="line"><span class="cl"> ] while*
</span></span><span class="line"><span class="cl">]
</span></span></code></pre></div>Building Hangman
https://re.factorcode.org/2023/12/building-hangman.html
Tue, 26 Dec 2023 08:00:00 -0700https://re.factorcode.org/2023/12/building-hangman.html<p>Recently, <a href="https://realpython.com/team/jfincher/">Jon Fincher</a> published an
interesting <a href="https://python.org">Python</a> tutorial describing steps to <a href="https://realpython.com/python-hangman/">build a
hangman game for the command line in
Python</a>. It provides for a nice demo of
different programming language features including taking user input, printing
to the screen, storing game state, and performing some logic until the game is
completed.</p>
<p>A game in progress might look like this – with <em>hangman</em> as the word being
guessed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> ┌───┐
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> O │
</span></span><span class="line"><span class="cl"> ─┼─ │
</span></span><span class="line"><span class="cl"> <span class="nb">/ </span>│ │
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> └──────┘
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Your word is: _ a n _ _ a n
</span></span><span class="line"><span class="cl">Your guesses: a e r s n
</span></span></code></pre></div><p>I thought it would be fun to show how to build a similar <a href="https://en.wikipedia.org/wiki/Hangman_(game)">hangman
game</a> in
<a href="https://factorcode.org">Factor</a>, using similar steps.</p>
<h2 id="step-1-set-up-the-hangman-project">Step 1: Set Up the Hangman Project</h2>
<p>We need to create the <code>hangman</code> vocabulary to store our work. We can use the
<code>scaffold-vocab</code> word to create a new vocabulary. It will prompt for which
vocab-root to place the new vocabulary into.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">tools.scaffold</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"hangman"</span> scaffold-vocab
</span></span></code></pre></div><p>And then open the vocab in your favorite text editor:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"hangman"</span> edit-vocab
</span></span></code></pre></div><p>We can start the file with all these imports, which we will be using in the
implementation below and two symbols that we will use to hold the state of
our game.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">combinators.short-circuit</span> <span class="nn">io</span> <span class="nn">io.encodings.utf8</span> <span class="nn">io.files</span>
</span></span><span class="line"><span class="cl"><span class="nn">kernel</span> <span class="nn">make</span> <span class="nn">math</span> <span class="nn">multiline</span> <span class="nn">namespaces</span> <span class="nn">random</span> <span class="nn">sequences</span>
</span></span><span class="line"><span class="cl"><span class="nn">sequences.interleaved</span> <span class="nn">sets</span> <span class="nn">sorting</span> <span class="nn">unicode</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">hangman</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">target-word</span> <span class="c">! the word being guessed</span>
</span></span><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">guesses</span> <span class="c">! all of the guessed letters</span>
</span></span></code></pre></div><h2 id="step-2-select-a-word-to-guess">Step 2: Select a Word to Guess</h2>
<p>Let’s create a <code>vocab:hangman/words.txt</code> file containing all of the possible
word choices. The original tutorial had a list of words that you can reference
– you are welcome to copy my file or create your own:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">http.client</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"https://raw.githubusercontent.com/mrjbq7/re-factor/master/hangman/words.txt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"vocab:hangman/words.txt"</span> download-to
</span></span></code></pre></div><p>Now we can add this word to read the file into memory and then choose a random
line.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-word</span> <span class="nf">( -- </span><span class="nv">word</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"vocab:hangman/words.txt"</span> utf8 file-lines random <span class="k">;
</span></span></span></code></pre></div><h2 id="step-3-get-and-validate-the-players-input">Step 3: Get and Validate the Player’s Input</h2>
<p>The user will use the
<a href="https://docs.factorcode.org/content/word-readln,io.html">readln</a> word to
provide input, and we will validate it by making sure the line contains a
single character that is not already in our <code>guesses</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">valid-guess?</span> <span class="nf">( </span><span class="nv">input</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ <span class="nb">length </span><span class="m">1 </span><span class="nb">= </span>]
</span></span><span class="line"><span class="cl"> [ lower? ]
</span></span><span class="line"><span class="cl"> [ <span class="nb">first </span>guesses <span class="nb">get </span>?adjoin ]
</span></span><span class="line"><span class="cl"> } 1&& <span class="k">;
</span></span></span></code></pre></div><p>Reading the player input is then just looping until we get a valid guess:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">player-guess</span> <span class="nf">( -- </span><span class="nv">ch</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span>[ <span class="nb">dup </span>valid-guess? ] [ <span class="nb">drop readln </span>] <span class="nb">do until first </span><span class="k">;
</span></span></span></code></pre></div><h2 id="step-4-display-the-guessed-letters-and-word">Step 4: Display the Guessed Letters and Word</h2>
<p>We can display the guessed letters as a sorted, space-separated list:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">spaces</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="sc">CHAR: \s </span><interleaved> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">guessed-letters</span> <span class="nf">( -- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> guesses <span class="nb">get </span>members sort spaces <span class="k">;
</span></span></span></code></pre></div><p>And the target word is also space-separated with blanks for letters we have not
guessed, or the actual letters if they have been guessed successfully:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">guessed-word</span> <span class="nf">( -- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> target-word <span class="nb">get </span>guesses <span class="nb">get </span>'[
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>_ in? [ <span class="nb">drop </span><span class="sc">CHAR: _ </span>] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map </span>spaces <span class="k">;
</span></span></span></code></pre></div><h2 id="step-5-draw-the-hanged-man">Step 5: Draw the Hanged Man</h2>
<p>We first calculate the number of wrong guesses, by <a href="https://docs.factorcode.org/content/word-diff,sets.html">set
difference</a> between
the guesses and our target word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">#wrong-guesses</span> <span class="nf">( -- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> guesses <span class="nb">get </span>target-word <span class="nb">get </span>diff cardinality <span class="k">;
</span></span></span></code></pre></div><p>Displaying the “hanged man” requires a bit more lines of code that the rest of
the program, using the number of wrong guesses to pick which to output:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">HANGED-MAN</span> {
</span></span><span class="line"><span class="cl">[[
</span></span><span class="line"><span class="cl"> ┌───┐
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> └──────┘
</span></span><span class="line"><span class="cl">]] [[
</span></span><span class="line"><span class="cl"> ┌───┐
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> O │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> └──────┘
</span></span><span class="line"><span class="cl">]] [[
</span></span><span class="line"><span class="cl"> ┌───┐
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> O │
</span></span><span class="line"><span class="cl"> ─┼─ │
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> └──────┘
</span></span><span class="line"><span class="cl">]] [[
</span></span><span class="line"><span class="cl"> ┌───┐
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> O │
</span></span><span class="line"><span class="cl"> ─┼─ │
</span></span><span class="line"><span class="cl"> <span class="nb">/ </span>│ │
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> └──────┘
</span></span><span class="line"><span class="cl">]] [[
</span></span><span class="line"><span class="cl"> ┌───┐
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> O │
</span></span><span class="line"><span class="cl"> ─┼─ │
</span></span><span class="line"><span class="cl"> <span class="nb">/ </span>│ <span class="no">\ │</span>
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> └──────┘
</span></span><span class="line"><span class="cl">]] [[
</span></span><span class="line"><span class="cl"> ┌───┐
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> O │
</span></span><span class="line"><span class="cl"> ─┼─ │
</span></span><span class="line"><span class="cl"> <span class="nb">/ </span>│ <span class="no">\ │</span>
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> ─┴─ │
</span></span><span class="line"><span class="cl"> <span class="nb">/ </span> │
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> └──────┘
</span></span><span class="line"><span class="cl">]] [[
</span></span><span class="line"><span class="cl"> ┌───┐
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> O │
</span></span><span class="line"><span class="cl"> ─┼─ │
</span></span><span class="line"><span class="cl"> <span class="nb">/ </span>│ <span class="no">\ │</span>
</span></span><span class="line"><span class="cl"> │ │
</span></span><span class="line"><span class="cl"> ─┴─ │
</span></span><span class="line"><span class="cl"> <span class="nb">/ </span> <span class="no">\ │</span>
</span></span><span class="line"><span class="cl"> │ │ │
</span></span><span class="line"><span class="cl"> │
</span></span><span class="line"><span class="cl"> └──────┘
</span></span><span class="line"><span class="cl">]]
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">hanged-man.</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> #wrong-guesses HANGED-MAN <span class="nb">nth print </span><span class="k">;
</span></span></span></code></pre></div><h2 id="step-6-figure-out-when-the-game-is-over">Step 6: Figure Out When the Game Is Over</h2>
<p>The game is lost when the player has too many wrong guesses:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">lose?</span> <span class="nf">( -- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> #wrong-guesses HANGED-MAN <span class="nb">length </span><span class="m">1 </span><span class="nb">- >= </span><span class="k">;
</span></span></span></code></pre></div><p>The game is won when the word has no unknown letters:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">win?</span> <span class="nf">( -- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> target-word <span class="nb">get </span>guesses <span class="nb">get </span>diff null? <span class="k">;
</span></span></span></code></pre></div><p>And the game is over when it is won or lost:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">game-over?</span> <span class="nf">( -- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { [ win? ] [ lose? ] } 0|| <span class="k">;
</span></span></span></code></pre></div><h2 id="step-7-run-the-game-loop">Step 7: Run the Game Loop</h2>
<p>It is frequently useful in <a href="https://factorcode.org">Factor</a> to build helper
words that, for example, set up some of the state that our program will use and
then run a provided
<a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">with-hangman</span> <span class="nf">( </span><span class="nv">quot</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> random-word target-word ,,
</span></span><span class="line"><span class="cl"> HS{ } <span class="nb">clone </span>guesses ,,
</span></span><span class="line"><span class="cl"> ] H{ } make <span class="nb">swap with-variables </span><span class="k">; inline
</span></span></span></code></pre></div><p>And then we can use that to build and run the game:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">play-hangman</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="s">"Welcome to Hangman!"</span> <span class="nb">print
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ game-over? ] [
</span></span><span class="line"><span class="cl"> hanged-man.
</span></span><span class="line"><span class="cl"> <span class="s">"Your word is: "</span> <span class="nb">write </span>guessed-word <span class="nb">print
</span></span></span><span class="line"><span class="cl"> <span class="s">"Your guesses: "</span> <span class="nb">write </span>guessed-letters <span class="nb">print
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nb">nl </span><span class="s">"What is your guess? "</span> <span class="nb">write flush
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> player-guess target-word <span class="nb">get </span>in?
</span></span><span class="line"><span class="cl"> <span class="s">"Great guess!"</span> <span class="s">"Sorry, it's not there."</span> <span class="nb">? print
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">until
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> hanged-man.
</span></span><span class="line"><span class="cl"> lose? <span class="s">"Sorry, you lost!"</span> <span class="s">"Congrats! You did it!"</span> <span class="nb">? print
</span></span></span><span class="line"><span class="cl"> <span class="s">"Your word was: "</span> <span class="nb">write </span>target-word <span class="nb">get print
</span></span></span><span class="line"><span class="cl"> ] with-hangman <span class="k">;
</span></span></span></code></pre></div><p>One last thing we can do is set this word as the <a href="https://docs.factorcode.org/content/word-MAIN__colon__,syntax.html">main entry
point</a> of
our vocabulary:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">play-hangman</span>
</span></span></code></pre></div><h2 id="next-steps">Next Steps</h2>
<p>Well, that’s kind of fun. You can run this in the listener:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"hangman"</span> run
</span></span></code></pre></div><p>Or at the command-line:</p>
<pre tabindex="0"><code>$ ./factor -run=hangman
</code></pre><p>The source code is available on my
<a href="https://github.com/mrjbq7/re-factor/tree/master/hangman">GitHub</a>.</p>
JavaScript Arrays
https://re.factorcode.org/2023/11/javascript-arrays.html
Wed, 22 Nov 2023 08:00:00 -0700https://re.factorcode.org/2023/11/javascript-arrays.html<p><a href="https://en.wikipedia.org/wiki/JSON">JSON</a> or “JavaScript Object Notation”
is widely used as a data format for storing, transmitting, and retrieving
data objects. It is language-independent and has parsers in most modern
programming languages. <a href="https://factorcode.org">Factor</a> is no exception,
containing the <a href="https://docs.factorcode.org/content/article-json.html">json
vocabulary</a>.</p>
<p>I wanted to go over some <a href="https://wtfjs.com">wtfjs</a> that relates to JavaScript
Arrays and JavaScript Objects, and then see how something similar might work in
<a href="https://factorcode.org">Factor</a>!</p>
<h3 id="in-javascript">In JavaScript</h3>
<p>You can define an array:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">person</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"John"</span><span class="p">,</span> <span class="s2">"Doe"</span><span class="p">,</span> <span class="mi">46</span><span class="p">];</span>
</span></span></code></pre></div><p>Or you can define an object:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">person</span> <span class="o">=</span> <span class="p">{</span><span class="nx">firstName</span><span class="o">:</span> <span class="s2">"John"</span><span class="p">,</span> <span class="nx">lastName</span><span class="o">:</span> <span class="s2">"Doe"</span><span class="p">,</span> <span class="nx">age</span><span class="o">:</span> <span class="mi">46</span><span class="p">};</span>
</span></span></code></pre></div><p>You can start with an array and set it’s values by index:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">person</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl"><span class="nx">person</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"John"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">person</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"Doe"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">person</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">46</span><span class="p">;</span>
</span></span></code></pre></div><p>You can start with an object and set it’s values by key:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">person</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl"><span class="nx">person</span><span class="p">[</span><span class="s2">"firstName"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"John"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">person</span><span class="p">[</span><span class="s2">"lastName"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"Doe"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">person</span><span class="p">[</span><span class="s2">"age"</span><span class="p">]</span> <span class="o">=</span> <span class="mi">46</span><span class="p">;</span>
</span></span></code></pre></div><p>Or, using “dot notation” on an object:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">person</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl"><span class="nx">person</span><span class="p">.</span><span class="nx">firstName</span> <span class="o">=</span> <span class="s2">"John"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">person</span><span class="p">.</span><span class="nx">lastName</span> <span class="o">=</span> <span class="s2">"Doe"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">person</span><span class="p">.</span><span class="nx">age</span> <span class="o">=</span> <span class="mi">46</span><span class="p">;</span>
</span></span></code></pre></div><p>In JavaScript, arrays are indexed by number, and objects are indexed by
name. But, you can mix these and create <em>association arrays</em> that can be…
both?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="o">></span> <span class="kr">const</span> <span class="nx">list</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"A"</span><span class="p">,</span> <span class="s2">"B"</span><span class="p">,</span> <span class="s2">"C"</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="o">></span> <span class="nx">list</span><span class="p">.</span><span class="nx">length</span>
</span></span><span class="line"><span class="cl"><span class="mi">3</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">></span> <span class="nx">list</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="s2">"A"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">></span> <span class="nx">list</span><span class="p">[</span><span class="s2">"0"</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="s2">"A"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">></span> <span class="nx">list</span><span class="p">.</span><span class="nx">key</span> <span class="o">=</span> <span class="s2">"value"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">></span> <span class="nx">list</span><span class="p">.</span><span class="nx">length</span>
</span></span><span class="line"><span class="cl"><span class="mi">3</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">></span> <span class="nx">list</span><span class="p">.</span><span class="nx">key</span>
</span></span><span class="line"><span class="cl"><span class="s2">"value"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">></span> <span class="nb">Object</span><span class="p">.</span><span class="nx">assign</span><span class="p">({},</span> <span class="nx">list</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="mi">0</span><span class="o">:</span> <span class="s2">"A"</span><span class="p">,</span> <span class="mi">1</span><span class="o">:</span> <span class="s2">"B"</span><span class="p">,</span> <span class="mi">2</span><span class="o">:</span> <span class="s2">"C"</span><span class="p">,</span> <span class="nx">key</span><span class="o">:</span> <span class="s2">"value"</span><span class="p">}</span>
</span></span></code></pre></div><p>That’s kinda weird.</p>
<h3 id="in-factor">In Factor</h3>
<p>Maybe <a href="https://factorcode.org">Factor</a> needs something like that? What if we
define a type that has both a sequence and an assoc, and supports both the
<a href="https://docs.factorcode.org/content/article-sequence-protocol.html">sequence
protocol</a>
and the <a href="https://docs.factorcode.org/content/article-assocs-protocol.html">assoc
protocol</a>.
How might that look?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">js-array</span> <span class="nv">seq</span> <span class="nv">assoc</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><js-array></span> <span class="nf">( -- </span><span class="nv">js-array</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> V{ } <span class="nb">clone </span>H{ } <span class="nb">clone </span>js-array <span class="nb">boa </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">INSTANCE:</span> <span class="nc">js-array</span> <span class="nc">sequence</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">CONSULT: sequence-protocol js-array seq>> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">INSTANCE:</span> <span class="nc">js-array</span> <span class="nc">assoc</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">CONSULT: assoc-protocol js-array assoc>> <span class="k">;
</span></span></span></code></pre></div><p>And now we can do something kinda similar:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <js-array>
</span></span><span class="line"><span class="cl"> { <span class="s">"A"</span> <span class="s">"B"</span> <span class="s">"C"</span> } [ <span class="nb">suffix! </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> <span class="s">"value"</span> <span class="s">"key"</span> <span class="nb">pick set-at
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup first </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"A"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup length </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">3
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup </span>members <span class="m">.
</span></span></span><span class="line"><span class="cl">V{ <span class="s">"A"</span> <span class="s">"B"</span> <span class="s">"C"</span> }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup >alist </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ { <span class="s">"key"</span> <span class="s">"value"</span> } }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"key"</span> <span class="nb">of </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"value"</span>
</span></span></code></pre></div><p>Well, it doesn’t handle converting
<a href="https://docs.factorcode.org/content/article-strings.html">string</a> keys so that
<code>"0" of</code> would return the same value as <code>first</code>. And it doesn’t handle
combining all the number indexed keys and name indexed keys to an <code>alist</code>
output like the <code>Object.assign({}, ...)</code> call above. And probably a few
other idiosyncrasies of the JavaScript association array that I’m not familiar
with…</p>
<p>But do we like this? I dunno yet.</p>
Magic Dict
https://re.factorcode.org/2023/11/magic-dict.html
Tue, 21 Nov 2023 08:00:00 -0700https://re.factorcode.org/2023/11/magic-dict.html<p>The <a href="https://raku.org">Raku programming language</a> community comes up with
interesting modules, and I enjoy getting emails from the <a href="https://rakudoweekly.blog">Rakudo Weekly
News</a>. This week, there was a link to a <a href="https://mathstodon.xyz/@julia/111428207881074674">post from a
few days ago about making a weird data
structure</a> in Raku:</p>
<blockquote>
<p>I came up with a weird data structure. It’s a hash, but you can also add
functions that receive the hash as input so you can do math with it (if you
squint, it’s vaguely like a spreadsheet). Something like:</p>
<pre tabindex="0"><code>m = MagicDict()
m["a"] = 1
m["b"] = 2
m["sum"] = lambda self: self["a"] + self["b"]
print(m["sum"])
</code></pre><p>Ideally, I’d want to do this in #RakuLang. (I know it’s possible because I
did something much weirder once (I gave Str a CALL-ME method).)</p>
</blockquote>
<p>I thought it would be fun to build this in <a href="https://factorcode.org">Factor</a> –
starting with a <code>magic-dict</code> class that:</p>
<ol>
<li>wraps an <a href="https://docs.factorcode.org/content/article-assocs.html">assoc</a>
which we will use to store all of the items</li>
<li>has a constructor using a
<a href="https://docs.factorcode.org/content/article-hashtables.html">hashtable</a> by
default</li>
<li>marked as an instance of assoc, so we support <a href="https://docs.factorcode.org/content/article-generic.html">generic
words</a> defined on
assocs</li>
<li>support the <a href="https://docs.factorcode.org/content/article-assocs-protocol.html">assoc
protocol</a>
using the <a href="https://docs.factorcode.org/content/article-delegate.html">delegate
vocabulary</a> to
defer to the wrapped assoc</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">magic-dict</span> <span class="nv">assoc</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><magic-dict></span> <span class="nf">( -- </span><span class="nv">magic-dict</span> <span class="nf">) </span>H{ } <span class="nb">clone </span>magic-dict <span class="nb">boa </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">INSTANCE:</span> <span class="nc">magic-dict</span> <span class="nc">assoc</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">CONSULT: assoc-protocol magic-dict assoc>> <span class="k">;
</span></span></span></code></pre></div><p>And the main piece of logic we need is to implement the <code>at*</code> lookup word
and, if the value is a
<a href="https://docs.factorcode.org/content/word-callable,quotations.html">callable</a>,
call it with the assoc on the stack as an argument.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">magic-dict</span> <span class="nf">at*</span>
</span></span><span class="line"><span class="cl"> <span class="nb">swap over </span>assoc>> <span class="nb">at* over </span>callable?
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>call( <span class="nb">assoc </span>-- value ) <span class="no">t </span>] [ nipd ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>This allows us to make a <a href="https://factorcode.org">Factor</a> version of the
original example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <magic-dict>
</span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="s">"a"</span> <span class="nb">pick set-at
</span></span></span><span class="line"><span class="cl"> <span class="m">2 </span><span class="s">"b"</span> <span class="nb">pick set-at
</span></span></span><span class="line"><span class="cl"> [ [ <span class="s">"a"</span> <span class="nb">of </span>] [ <span class="s">"b"</span> <span class="nb">of </span>] <span class="nb">bi + </span>] <span class="s">"sum"</span> <span class="nb">pick set-at
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"sum"</span> <span class="nb">of </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">3
</span></span></span></code></pre></div><p>Pretty cool!</p>
Stack Effects
https://re.factorcode.org/2023/11/stack-effects.html
Thu, 16 Nov 2023 09:00:00 -0700https://re.factorcode.org/2023/11/stack-effects.html<p><a href="https://factorcode.org">Factor</a> word definitions have required <a href="https://docs.factorcode.org/content/article-effects.html">stack
effects</a>. These
are used by the <a href="https://docs.factorcode.org/content/article-inference.html">stack
checker</a> to
verify that word definitions are consistent with their stack effects, have
internal branches that have similar stack effects, and other validness
checks.</p>
<p>Long ago, these stack effects were optional – words might have looked
something like this:</p>
<pre tabindex="0"><code>: add2 2 + ;
</code></pre><p>At some point over a decade ago, we made stack effects required, and now it
looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">add2</span> <span class="nf">( </span><span class="nv">m</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">) </span><span class="m">2 </span><span class="nb">+ </span><span class="k">;
</span></span></span></code></pre></div><p>That change has been generally positive for the <a href="https://docs.factorcode.org/content/article-vocab-index.html">Factor standard
library</a> –
helping readability, improving natural word documentation, and improving the
locality of stack checker errors. Every once in awhile, I think fondly back to
those simpler days.</p>
<p>Well, it’s still possible to have the good ol’ days back – check this out!</p>
<p>The stack-checker has an
<a href="https://docs.factorcode.org/content/word-infer,stack-checker.html">infer</a> word
to apply the stack checker algorithm to a quotation, returning the inferred
stack effect. You can try it in the Listener using the <code>Ctrl-I</code> shortcut or
calling it directly:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">2 </span><span class="nb">+ </span>] infer <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="nf">( </span><span class="nv">x</span> <span class="nf">-- </span><span class="nv">x</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ [ <span class="nb">+ </span>] <span class="nb">with map </span>] infer <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="nf">( </span><span class="nv">x</span> <span class="nv">x</span> <span class="nf">-- </span><span class="nv">x</span> <span class="nf">)
</span></span></span></code></pre></div><p>We can use this to make <a href="https://docs.factorcode.org/content/article-syntax.html">new
syntax</a> that defines a
word with the stack effect that was inferred:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">effects.parser</span> <span class="nn">kernel</span> <span class="nn">parser</span> <span class="nn">stack-checker</span> <span class="nn">words</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">SYNTAX: </span>INFER:
</span></span><span class="line"><span class="cl"> [ scan-new-word parse-definition ] with-definition
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>infer define-declared <span class="k">;
</span></span></span></code></pre></div><p>And now we can just write our words without bothering to write their stack
effects:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">INFER: add2 <span class="m">2 </span><span class="nb">+ </span><span class="k">;
</span></span></span></code></pre></div><p>And then use it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1 </span>add2 <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">3
</span></span></span></code></pre></div><p>Pretty neat!</p>
Anonymous Predicates
https://re.factorcode.org/2023/11/anonymous-predicates.html
Wed, 15 Nov 2023 08:00:00 -0700https://re.factorcode.org/2023/11/anonymous-predicates.html<p><a href="https://factorcode.org">Factor</a> has several types of
<a href="https://docs.factorcode.org/content/article-classes.html">classes</a> that can
be used to specialize methods on generic words and to disambiguate objects
from other objects. Some examples are:</p>
<ul>
<li><a href="https://docs.factorcode.org/content/article-builtin-classes.html">built-in classes</a> (defined in the VM),</li>
<li><a href="https://docs.factorcode.org/content/article-tuples.html">tuple classes</a>,</li>
<li><a href="https://docs.factorcode.org/content/article-unions.html">union classes</a>,</li>
<li><a href="https://docs.factorcode.org/content/article-intersections.html">intersection classes</a>,</li>
<li><a href="https://docs.factorcode.org/content/article-maybes.html">maybe classes</a>,</li>
<li><a href="https://docs.factorcode.org/content/article-mixins.html">mixin classes</a>,</li>
<li><a href="https://docs.factorcode.org/content/article-singletons.html">singleton classes</a>, and</li>
<li><a href="https://docs.factorcode.org/content/article-predicates.html">predicate classes</a>.</li>
</ul>
<p>Today, we are going to discuss predicate classes and a recent feature that
was contributed by <a href="https://github.com/Capital-EX">@Capital-Ex</a> to <a href="https://github.com/factor/factor/pull/2884">support
anonymous predicates</a>. A typical
definition of a predicate class might look like this definition which
describes all positive integers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">PREDICATE:</span> <span class="nc">positive</span> < <span class="nc">integer</span> <span class="m">0 </span><span class="nb">> </span><span class="k">;
</span></span></span></code></pre></div><p>You can use it in the listener to identify instances:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">12 </span>positive? <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">t
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">-5 </span>positive? <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">f
</span></span></span></code></pre></div><p>You can also dispatch on them:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">GENERIC:</span> <span class="nf">wat</span> <span class="nf">( </span><span class="nv">obj</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">object</span> <span class="nf">wat</span> <span class="nb">drop </span><span class="s">"object"</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">positive</span> <span class="nf">wat</span> <span class="nb">drop </span><span class="s">"positive integer"</span> <span class="k">;
</span></span></span></code></pre></div><p>And see how that works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">12 </span>wat <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"positive integer"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="no">f </span>wat <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"object"</span>
</span></span></code></pre></div><p>The new <em>anonymous predicates</em> feature allows us to rewrite that word with
inline predicate definitions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">GENERIC:</span> <span class="nf">wat</span> <span class="nf">( </span><span class="nv">obj</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">object</span> <span class="nf">wat</span> <span class="nb">drop </span><span class="s">"object"</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">predicate{</span> <span class="nf">integer</span> [ <span class="m">0 </span><span class="nb">> </span>] } wat <span class="nb">drop </span><span class="s">"positive integer"</span> <span class="k">;
</span></span></span></code></pre></div><p>And, in fact, we could extend it with some of the other anonymous classes to
create this monstrosity:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">GENERIC:</span> <span class="nf">wat</span> <span class="nf">( </span><span class="nv">obj</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">object</span> <span class="nf">wat</span> <span class="nb">drop </span><span class="s">"object"</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">predicate{</span> <span class="nf">integer</span> [ <span class="m">0 </span><span class="nb">> </span>] } wat <span class="nb">drop </span><span class="s">"positive integer"</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">intersection{</span> <span class="nf">bignum</span> positive } wat <span class="nb">drop </span><span class="s">"five"</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">union{</span> <span class="nf">fixnum</span> <span class="nb">bignum </span>} wat <span class="nb">drop </span><span class="s">"integer"</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">maybe{</span> <span class="nf">string</span> } wat <span class="nb">drop </span><span class="s">"maybe a string"</span> <span class="k">;
</span></span></span></code></pre></div><p>And then test most (all? who really knows?) of the cases:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">5 </span>wat <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"positive integer"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">5 </span><span class="nb">>bignum </span>wat <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"five"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">-5 </span>wat <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"integer"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"hello"</span> wat <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"maybe a string"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> B{ } wat <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"object"</span>
</span></span></code></pre></div><p>Pretty cool! This is available in the development version of Factor.</p>
Factor is faster than Zig!
https://re.factorcode.org/2023/11/factor-is-faster-than-zig.html
Thu, 09 Nov 2023 08:00:00 -0700https://re.factorcode.org/2023/11/factor-is-faster-than-zig.html<p>Recently, I was looking at the <a href="https://ziglang.org">Zig programming language</a>.
As I often do, I started implementing a few typical things in new languages to learn them. Well, one of them was super slow and Zig is supposed to be super
fast, so I was trying to understand where the disconnect was and compare it to
<a href="https://factorcode.org">Factor</a>!</p>
<p>I was able to reduce the issue to a small test case and it turns out that there
is a behavioral issue in their <a href="https://github.com/ziglang/zig/blob/master/lib/std/hash_map.zig">implementation of
HashMap</a> that
makes their <a href="https://github.com/ziglang/zig/issues/17851">HashMaps get slow over
time</a>. The test case performs
these steps:</p>
<ul>
<li>creates a <code>HashMap</code> of 2 million items</li>
<li>decrements the map values, removing an item every third loop</li>
<li>inserts a replacement new item to maintain 2 million items in the <code>HashMap</code></li>
<li>for a total of 250 million actions, then</li>
<li>deletes the remaining items from the <code>HashMap</code></li>
</ul>
<p>We record the total time each block of 1 million actions takes:</p>
<p>
<img src="https://re.factorcode.org/images/2023-11-09-zig-hashmap-bug.png" alt="" width="531" height="386" />
</p>
<p>Something is very wrong!</p>
<h3 id="zig">Zig</h3>
<p>This is the simple test case implemented in Zig using the <code>std.HashMap</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-zig" data-lang="zig"><span class="line"><span class="cl"><span class="kr">const</span><span class="w"> </span><span class="n">std</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">@import</span><span class="p">(</span><span class="s">"std"</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kr">pub</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="o">!</span><span class="kt">void</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">var</span><span class="w"> </span><span class="n">map</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="nf">AutoHashMap</span><span class="p">(</span><span class="kt">u64</span><span class="p">,</span><span class="w"> </span><span class="kt">u64</span><span class="p">).</span><span class="nf">init</span><span class="p">(</span><span class="n">std</span><span class="p">.</span><span class="n">heap</span><span class="p">.</span><span class="n">page_allocator</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">defer</span><span class="w"> </span><span class="n">map</span><span class="p">.</span><span class="nf">deinit</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">var</span><span class="w"> </span><span class="n">list</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="nf">ArrayList</span><span class="p">(</span><span class="kt">u64</span><span class="p">).</span><span class="nf">init</span><span class="p">(</span><span class="n">std</span><span class="p">.</span><span class="n">heap</span><span class="p">.</span><span class="n">page_allocator</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">defer</span><span class="w"> </span><span class="n">list</span><span class="p">.</span><span class="nf">deinit</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">var</span><span class="w"> </span><span class="n">prng</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">rand</span><span class="p">.</span><span class="n">DefaultPrng</span><span class="p">.</span><span class="nf">init</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">const</span><span class="w"> </span><span class="n">random</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">prng</span><span class="p">.</span><span class="nf">random</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">var</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">time</span><span class="p">.</span><span class="nf">milliTimestamp</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">var</span><span class="w"> </span><span class="n">i</span><span class="p">:</span><span class="w"> </span><span class="kt">u64</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mi">2_000_000</span><span class="p">)</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">map</span><span class="p">.</span><span class="nf">put</span><span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">list</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">i</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mi">250_000_000</span><span class="p">)</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">var</span><span class="w"> </span><span class="n">index</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">random</span><span class="p">.</span><span class="nf">uintLessThan</span><span class="p">(</span><span class="kt">usize</span><span class="p">,</span><span class="w"> </span><span class="n">list</span><span class="p">.</span><span class="n">items</span><span class="p">.</span><span class="n">len</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">var</span><span class="w"> </span><span class="n">j</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">list</span><span class="p">.</span><span class="n">items</span><span class="p">[</span><span class="n">index</span><span class="p">];</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">var</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">map</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">j</span><span class="p">)</span><span class="o">.?</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">map</span><span class="p">.</span><span class="nf">remove</span><span class="p">(</span><span class="n">j</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">map</span><span class="p">.</span><span class="nf">put</span><span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">list</span><span class="p">.</span><span class="n">items</span><span class="p">[</span><span class="n">index</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">i</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="n">map</span><span class="p">.</span><span class="nf">put</span><span class="p">(</span><span class="n">j</span><span class="p">,</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">1_000_000</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">var</span><span class="w"> </span><span class="n">end</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">time</span><span class="p">.</span><span class="nf">milliTimestamp</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">debug</span><span class="p">.</span><span class="nf">print</span><span class="p">(</span><span class="s">"{} block took {} ms</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="w"> </span><span class="p">.{</span><span class="w"> </span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="n">end</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span><span class="p">.</span><span class="n">time</span><span class="p">.</span><span class="nf">milliTimestamp</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">list</span><span class="p">.</span><span class="n">items</span><span class="p">.</span><span class="n">len</span><span class="w"> </span><span class="o">></span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">var</span><span class="w"> </span><span class="n">j</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">list</span><span class="p">.</span><span class="nf">pop</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">map</span><span class="p">.</span><span class="nf">remove</span><span class="p">(</span><span class="n">j</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>We can run it using <code>ReleaseFast</code> to get the best performance and see
that, over time, it gets super slow – so slow that it isn’t even able to
really finish the test case:</p>
<pre tabindex="0"><code>$ zig version
0.11.0
$ zig run -O ReleaseFast maptest.zig
2000000 block took 156 ms
3000000 block took 122 ms
4000000 block took 127 ms
5000000 block took 133 ms
6000000 block took 138 ms
7000000 block took 141 ms
8000000 block took 143 ms
9000000 block took 145 ms
10000000 block took 147 ms
11000000 block took 148 ms
12000000 block took 151 ms
13000000 block took 153 ms
14000000 block took 155 ms
15000000 block took 157 ms
16000000 block took 159 ms
17000000 block took 164 ms
18000000 block took 167 ms
19000000 block took 171 ms
20000000 block took 173 ms
21000000 block took 180 ms
22000000 block took 186 ms
23000000 block took 190 ms
24000000 block took 195 ms
25000000 block took 205 ms
26000000 block took 213 ms
27000000 block took 221 ms
28000000 block took 234 ms
29000000 block took 247 ms
30000000 block took 264 ms
31000000 block took 282 ms
32000000 block took 301 ms
33000000 block took 320 ms
34000000 block took 346 ms
35000000 block took 377 ms
36000000 block took 409 ms
37000000 block took 448 ms
38000000 block took 502 ms
39000000 block took 550 ms
40000000 block took 614 ms
41000000 block took 694 ms
42000000 block took 767 ms
43000000 block took 853 ms
44000000 block took 961 ms
45000000 block took 1088 ms
46000000 block took 1250 ms
47000000 block took 1420 ms
48000000 block took 1612 ms
49000000 block took 1826 ms
50000000 block took 2056 ms
51000000 block took 2320 ms
52000000 block took 2688 ms
53000000 block took 3015 ms
54000000 block took 3467 ms
55000000 block took 3971 ms
56000000 block took 4618 ms
57000000 block took 5377 ms
58000000 block took 6172 ms
59000000 block took 7094 ms
60000000 block took 8173 ms
61000000 block took 9469 ms
62000000 block took 11083 ms
63000000 block took 12737 ms
64000000 block took 14000 ms
65000000 block took 16243 ms
66000000 block took 17912 ms
67000000 block took 20452 ms
68000000 block took 24356 ms
...
</code></pre><p>We can switch the example above to use <code>std.ArrayHashMap</code> which does not have
this problem, although it is a bit slower with each block taking roughly 250
milliseconds.</p>
<h3 id="factor">Factor</h3>
<p>We can compare that to a simple implementation in Factor:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">assocs</span> <span class="nn">calendar</span> <span class="nn">formatting</span> <span class="nn">io</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">random</span>
</span></span><span class="line"><span class="cl"><span class="nn">sequences</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">maptest</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> H{ } <span class="nb">clone </span>:> m
</span></span><span class="line"><span class="cl"> V{ } <span class="nb">clone </span>:> l
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> now :> start!
</span></span><span class="line"><span class="cl"> <span class="m">0 </span> :> i!
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ i <span class="m">2,000,000 </span><span class="nb">< </span>] [
</span></span><span class="line"><span class="cl"> <span class="m">3 </span>i m <span class="nb">set-at
</span></span></span><span class="line"><span class="cl"> i l <span class="nb">push
</span></span></span><span class="line"><span class="cl"> i <span class="m">1 </span><span class="nb">+ </span>i!
</span></span><span class="line"><span class="cl"> ] <span class="nb">while
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ i <span class="m">250,000,000 </span><span class="nb">< </span>] [
</span></span><span class="line"><span class="cl"> l <span class="nb">length </span>random :> j
</span></span><span class="line"><span class="cl"> j l <span class="nb">nth </span>:> k
</span></span><span class="line"><span class="cl"> k m <span class="nb">at </span><span class="m">1 </span><span class="nb">- </span>[
</span></span><span class="line"><span class="cl"> k m <span class="nb">delete-at
</span></span></span><span class="line"><span class="cl"> <span class="m">3 </span>i m <span class="nb">set-at
</span></span></span><span class="line"><span class="cl"> i j l <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> k m <span class="nb">set-at
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-zero
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> i <span class="m">1,000,000 </span><span class="nb">mod zero? </span>[
</span></span><span class="line"><span class="cl"> i now <span class="nb">start </span>time- duration>milliseconds
</span></span><span class="line"><span class="cl"> <span class="s">"%d block took %d ms\n"</span> printf <span class="nb">flush
</span></span></span><span class="line"><span class="cl"> now start!
</span></span><span class="line"><span class="cl"> ] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> i <span class="m">1 </span><span class="nb">+ </span>i!
</span></span><span class="line"><span class="cl"> ] <span class="nb">while
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ l <span class="nb">empty? </span>] [
</span></span><span class="line"><span class="cl"> l <span class="nb">pop </span>m <span class="nb">delete-at
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">until </span><span class="k">;
</span></span></span></code></pre></div><p>We can run it in Factor and see how long it takes. There are notably some long
delays in the first few blocks which I’d like to understand better – possibly
due to excessive rehashing or some allocation pattern with the Factor garbage
collector – and then it quickly reaches a steady state where each block takes
about 250 milliseconds.</p>
<pre tabindex="0"><code>$ factor maptest.factor
2000000 block took 855 ms
3000000 block took 198 ms
4000000 block took 205 ms
5000000 block took 3579 ms
6000000 block took 4438 ms
7000000 block took 3624 ms
8000000 block took 2996 ms
9000000 block took 232 ms
10000000 block took 243 ms
11000000 block took 248 ms
12000000 block took 298 ms
13000000 block took 233 ms
14000000 block took 238 ms
15000000 block took 298 ms
16000000 block took 233 ms
17000000 block took 521 ms
18000000 block took 231 ms
19000000 block took 236 ms
20000000 block took 280 ms
21000000 block took 235 ms
22000000 block took 235 ms
23000000 block took 281 ms
24000000 block took 231 ms
25000000 block took 236 ms
26000000 block took 294 ms
27000000 block took 231 ms
28000000 block took 236 ms
29000000 block took 506 ms
30000000 block took 234 ms
31000000 block took 237 ms
32000000 block took 277 ms
33000000 block took 232 ms
34000000 block took 239 ms
35000000 block took 279 ms
36000000 block took 235 ms
37000000 block took 239 ms
38000000 block took 275 ms
39000000 block took 234 ms
40000000 block took 514 ms
41000000 block took 231 ms
42000000 block took 236 ms
43000000 block took 282 ms
44000000 block took 235 ms
45000000 block took 235 ms
46000000 block took 282 ms
47000000 block took 231 ms
48000000 block took 233 ms
49000000 block took 280 ms
50000000 block took 234 ms
51000000 block took 238 ms
52000000 block took 507 ms
53000000 block took 231 ms
54000000 block took 236 ms
55000000 block took 276 ms
56000000 block took 231 ms
57000000 block took 238 ms
58000000 block took 278 ms
59000000 block took 234 ms
60000000 block took 235 ms
61000000 block took 278 ms
62000000 block took 237 ms
63000000 block took 239 ms
64000000 block took 510 ms
65000000 block took 234 ms
66000000 block took 284 ms
...
</code></pre><p>Not bad!</p>
<h3 id="whats-the-bug">What’s the Bug?</h3>
<p>So, Zig <em>could</em> be super fast, but the default <code>std.HashMap</code> implementation
uses tombstone buckets to mark slots as being deleted, and over time these
tombstone buckets create fragmentation in the HashMap, which causes their
linear probing to trend towards the worst case examination of every bucket in
the HashMap when looking for a key.</p>
<p>We can implement a <code>rehash()</code> method on the HashMap that performs an in-place
rehashing of all the elements, without allocations. Ideally, this would be done
when the number of filled and deleted slots reaches some capacity threshold.
But, for now, we can just run <code>map.rehash()</code> once per block, and see how that
improves performance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"><span class="gh">diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig
</span></span></span><span class="line"><span class="cl"><span class="gh">index 8a3d78283..7192ba733 100644
</span></span></span><span class="line"><span class="cl"><span class="gd">--- a/lib/std/hash_map.zig
</span></span></span><span class="line"><span class="cl"><span class="gi">+++ b/lib/std/hash_map.zig
</span></span></span><span class="line"><span class="cl"><span class="gu">@@ -681,6 +681,11 @@ pub fn HashMap(
</span></span></span><span class="line"><span class="cl"> self.unmanaged = .{};
</span></span><span class="line"><span class="cl"> return result;
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ /// Rehash the map, in-place
</span></span></span><span class="line"><span class="cl"><span class="gi">+ pub fn rehash(self: *Self) void {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ self.unmanaged.rehash(self.ctx);
</span></span></span><span class="line"><span class="cl"><span class="gi">+ }
</span></span></span><span class="line"><span class="cl"> };
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">@@ -1505,6 +1510,92 @@ pub fn HashMapUnmanaged(
</span></span></span><span class="line"><span class="cl"> return result;
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gi">+ /// Rehash the map, in-place
</span></span></span><span class="line"><span class="cl"><span class="gi">+ pub fn rehash(self: *Self, ctx: anytype) void {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ const mask = self.capacity() - 1;
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ var metadata = self.metadata.?;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ var keys_ptr = self.keys();
</span></span></span><span class="line"><span class="cl"><span class="gi">+ var values_ptr = self.values();
</span></span></span><span class="line"><span class="cl"><span class="gi">+ var curr: Size = 0;
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ // While we are re-hashing every slot, we will use the
</span></span></span><span class="line"><span class="cl"><span class="gi">+ // fingerprint to mark used buckets as being used and either free
</span></span></span><span class="line"><span class="cl"><span class="gi">+ // (needing to be rehashed) or tombstone (already rehashed).
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ while (curr < self.capacity()) : (curr += 1) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ metadata[curr].fingerprint = Metadata.free;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ }
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ // Now iterate over all the buckets, rehashing them
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ curr = 0;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ while (curr < self.capacity()) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ if (!metadata[curr].isUsed()) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ assert(metadata[curr].isFree());
</span></span></span><span class="line"><span class="cl"><span class="gi">+ curr += 1;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ continue;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ }
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ var hash = ctx.hash(keys_ptr[curr]);
</span></span></span><span class="line"><span class="cl"><span class="gi">+ var fingerprint = Metadata.takeFingerprint(hash);
</span></span></span><span class="line"><span class="cl"><span class="gi">+ var idx = @as(usize, @truncate(hash & mask));
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ // For each bucket, rehash to an index:
</span></span></span><span class="line"><span class="cl"><span class="gi">+ // 1) before the cursor, probed into a free slot, or
</span></span></span><span class="line"><span class="cl"><span class="gi">+ // 2) equal to the cursor, no need to move, or
</span></span></span><span class="line"><span class="cl"><span class="gi">+ // 3) ahead of the cursor, probing over already rehashed
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ while ((idx < curr and metadata[idx].isUsed()) or
</span></span></span><span class="line"><span class="cl"><span class="gi">+ (idx > curr and metadata[idx].fingerprint == Metadata.tombstone))
</span></span></span><span class="line"><span class="cl"><span class="gi">+ {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ idx = (idx + 1) & mask;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ }
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ if (idx < curr) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ assert(metadata[idx].isFree());
</span></span></span><span class="line"><span class="cl"><span class="gi">+ metadata[idx].fingerprint = fingerprint;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ metadata[idx].used = 1;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ keys_ptr[idx] = keys_ptr[curr];
</span></span></span><span class="line"><span class="cl"><span class="gi">+ values_ptr[idx] = values_ptr[curr];
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ metadata[curr].used = 0;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ assert(metadata[curr].isFree());
</span></span></span><span class="line"><span class="cl"><span class="gi">+ keys_ptr[curr] = undefined;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ values_ptr[curr] = undefined;
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ curr += 1;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ } else if (idx == curr) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ metadata[idx].fingerprint = fingerprint;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ curr += 1;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ } else {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ assert(metadata[idx].fingerprint != Metadata.tombstone);
</span></span></span><span class="line"><span class="cl"><span class="gi">+ metadata[idx].fingerprint = Metadata.tombstone;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ if (metadata[idx].isUsed()) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ var tmpkey = keys_ptr[idx];
</span></span></span><span class="line"><span class="cl"><span class="gi">+ var tmpvalue = values_ptr[idx];
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ keys_ptr[idx] = keys_ptr[curr];
</span></span></span><span class="line"><span class="cl"><span class="gi">+ values_ptr[idx] = values_ptr[curr];
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ keys_ptr[curr] = tmpkey;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ values_ptr[curr] = tmpvalue;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ } else {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ metadata[idx].used = 1;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ keys_ptr[idx] = keys_ptr[curr];
</span></span></span><span class="line"><span class="cl"><span class="gi">+ values_ptr[idx] = values_ptr[curr];
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ metadata[curr].fingerprint = Metadata.free;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ metadata[curr].used = 0;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ keys_ptr[curr] = undefined;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ values_ptr[curr] = undefined;
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ curr += 1;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ }
</span></span></span><span class="line"><span class="cl"><span class="gi">+ }
</span></span></span><span class="line"><span class="cl"><span class="gi">+ }
</span></span></span><span class="line"><span class="cl"><span class="gi">+ }
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl">oid {
</span></span><span class="line"><span class="cl"> @setCold(true);
</span></span><span class="line"><span class="cl"> const new_cap = @max(new_capacity, minimal_capacity);
</span></span><span class="line"><span class="cl"><span class="gu">@@ -2218,3 +2309,35 @@ test "std.hash_map repeat fetchRemove" {
</span></span></span><span class="line"><span class="cl"> try testing.expect(map.get(2) != null);
</span></span><span class="line"><span class="cl"> try testing.expect(map.get(3) != null);
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+test "std.hash_map rehash" {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ var map = AutoHashMap(u32, u32).init(std.testing.allocator);
</span></span></span><span class="line"><span class="cl"><span class="gi">+ defer map.deinit();
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ var prng = std.rand.DefaultPrng.init(0);
</span></span></span><span class="line"><span class="cl"><span class="gi">+ const random = prng.random();
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ const count = 6 * random.intRangeLessThan(u32, 100_000, 500_000);
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ var i: u32 = 0;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ while (i < count) : (i += 1) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ try map.put(i, i);
</span></span></span><span class="line"><span class="cl"><span class="gi">+ if (i % 3 == 0) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ try expectEqual(map.remove(i), true);
</span></span></span><span class="line"><span class="cl"><span class="gi">+ }
</span></span></span><span class="line"><span class="cl"><span class="gi">+ }
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ map.rehash();
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ try expectEqual(map.count(), count * 2 / 3);
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+ i = 0;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ while (i < count) : (i += 1) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ if (i % 3 == 0) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ try expectEqual(map.get(i), null);
</span></span></span><span class="line"><span class="cl"><span class="gi">+ } else {
</span></span></span><span class="line"><span class="cl"><span class="gi">+ try expectEqual(map.get(i).?, i);
</span></span></span><span class="line"><span class="cl"><span class="gi">+ }
</span></span></span><span class="line"><span class="cl"><span class="gi">+ }
</span></span></span><span class="line"><span class="cl"><span class="gi">+}
</span></span></span></code></pre></div><p>We can apply that diff to <code>lib/std/hash_map.zig</code> and try again, now taking
about 165 milliseconds per block including the time for <code>map.rehash()</code>:</p>
<pre tabindex="0"><code>$ zig run -O ReleaseFast maptest.zig --zig-lib-dir ~/Dev/zig/lib
2000000 block took 155 ms
3000000 block took 147 ms
4000000 block took 154 ms
5000000 block took 160 ms
6000000 block took 163 ms
7000000 block took 164 ms
8000000 block took 165 ms
9000000 block took 166 ms
10000000 block took 166 ms
11000000 block took 165 ms
12000000 block took 166 ms
13000000 block took 165 ms
14000000 block took 166 ms
15000000 block took 172 ms
16000000 block took 165 ms
17000000 block took 167 ms
18000000 block took 165 ms
19000000 block took 167 ms
20000000 block took 169 ms
21000000 block took 168 ms
22000000 block took 167 ms
23000000 block took 166 ms
24000000 block took 167 ms
25000000 block took 167 ms
26000000 block took 165 ms
27000000 block took 166 ms
28000000 block took 166 ms
29000000 block took 165 ms
30000000 block took 165 ms
31000000 block took 165 ms
32000000 block took 166 ms
33000000 block took 165 ms
34000000 block took 167 ms
35000000 block took 170 ms
36000000 block took 165 ms
37000000 block took 166 ms
38000000 block took 166 ms
39000000 block took 164 ms
40000000 block took 165 ms
41000000 block took 167 ms
42000000 block took 166 ms
43000000 block took 167 ms
44000000 block took 169 ms
45000000 block took 166 ms
46000000 block took 165 ms
47000000 block took 166 ms
48000000 block took 166 ms
49000000 block took 166 ms
50000000 block took 166 ms
51000000 block took 166 ms
52000000 block took 164 ms
53000000 block took 165 ms
54000000 block took 167 ms
55000000 block took 165 ms
56000000 block took 166 ms
57000000 block took 166 ms
58000000 block took 165 ms
59000000 block took 166 ms
60000000 block took 169 ms
61000000 block took 165 ms
62000000 block took 165 ms
63000000 block took 166 ms
64000000 block took 166 ms
65000000 block took 165 ms
66000000 block took 166 ms
67000000 block took 176 ms
68000000 block took 166 ms
...
</code></pre><p>Well now, Zig is fast and everything is right again with the world – and
Factor takes only about 50% more time than Zig’s <code>std.HashMap</code> with
<code>rehash()</code> and about the same as <code>std.ArrayHashMap</code>, which is pretty
good for a dynamic language.</p>
<p>I submitted a <a href="https://github.com/ziglang/zig/pull/17890">pull request adding a rehash() method to
HashMap</a> and hopefully it gets into
the upcoming Zig 0.12 release and maybe for Zig 0.13 they can adjust it to
automatically rehash when it gets sufficiently fragmented, consider using
quadratic probing instead of linear probing, or perhaps switch to using a
completely different HashMap algorithm like <a href="https://engineering.fb.com/2019/04/25/developer-tools/f14/">Facebook’s F14 hash
table</a>, which
doesn’t have this issue.</p>
<p>Maybe we should consider some of these improvements for Factor as well!</p>
<p>Open source is fun!</p>
SMAC
https://re.factorcode.org/2023/10/smac.html
Fri, 27 Oct 2023 07:00:00 -0700https://re.factorcode.org/2023/10/smac.html<p>Recently, I was looking into the <a href="https://ziglang.org">Zig programming
language</a> and bumped into this video tutorial:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/E-MPhgtC_2s?autoplay=0&controls=1&end=0&loop=0&mute=0&start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
</div>
<p>It presents an example – in the spirit of <a href="https://en.wikipedia.org/wiki/Fizz_buzz">Fizz
buzz</a> – called <em>SMAC</em>, which is
basically a program that converts the first million numbers into strings or
<code>"SMAC"</code> (if the number is divisible by 7 or ends with a 7) and prints them
out. This is then implemented in three different forms:</p>
<ol>
<li>single-threaded</li>
<li>multi-threaded, and</li>
<li>networked using sockets.</li>
</ol>
<p>We first start with the basics, a word to check if a number is <em>SMAC</em> or not:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">smac?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { [ <span class="m">7 </span><span class="nb">mod </span><span class="m">0 </span><span class="nb">= </span>] [ <span class="m">10 </span><span class="nb">mod </span><span class="m">7 </span><span class="nb">= </span>] } 1|| <span class="k">;
</span></span></span></code></pre></div><p>And then a word that converts a number into its string representation or <em>SMAC</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">smac</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>smac? [ <span class="nb">drop </span><span class="s">"SMAC"</span> ] [ number>string ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><h3 id="single-threaded">Single-Threaded</h3>
<p>The simplest example – run in a single-thread – iteratively prints to the
<a href="https://docs.factorcode.org/content/word-output-stream,io.html">output-stream</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">smac.</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [1..b] [ smac <span class="nb">print </span>] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>Trying it out, you can see that it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">20 </span>smac.
</span></span><span class="line"><span class="cl"><span class="m">1
</span></span></span><span class="line"><span class="cl"><span class="m">2
</span></span></span><span class="line"><span class="cl"><span class="m">3
</span></span></span><span class="line"><span class="cl"><span class="m">4
</span></span></span><span class="line"><span class="cl"><span class="m">5
</span></span></span><span class="line"><span class="cl"><span class="m">6
</span></span></span><span class="line"><span class="cl">SMAC
</span></span><span class="line"><span class="cl"><span class="m">8
</span></span></span><span class="line"><span class="cl"><span class="m">9
</span></span></span><span class="line"><span class="cl"><span class="m">10
</span></span></span><span class="line"><span class="cl"><span class="m">11
</span></span></span><span class="line"><span class="cl"><span class="m">12
</span></span></span><span class="line"><span class="cl"><span class="m">13
</span></span></span><span class="line"><span class="cl">SMAC
</span></span><span class="line"><span class="cl"><span class="m">15
</span></span></span><span class="line"><span class="cl"><span class="m">16
</span></span></span><span class="line"><span class="cl">SMAC
</span></span><span class="line"><span class="cl"><span class="m">18
</span></span></span><span class="line"><span class="cl"><span class="m">19
</span></span></span><span class="line"><span class="cl"><span class="m">20
</span></span></span></code></pre></div><h3 id="multi-threaded">Multi-Threaded</h3>
<p>Slightly more complex, the multi-threaded example splits the numbers into four
<a href="https://docs.factorcode.org/content/word-__lt__n-groups__gt__,grouping.extras.html">n-groups</a>,
computes them as a
<a href="https://docs.factorcode.org/content/article-concurrency.futures.html">future</a>
in four background threads, and then waits for those computations to finish and
iteratively prints them out.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">smac.</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [1..b] <span class="m">4 </span><n-groups>
</span></span><span class="line"><span class="cl"> [ '[ _ [ smac ] <span class="nb">map </span>] future ] <span class="nb">map
</span></span></span><span class="line"><span class="cl"> [ ?future [ <span class="nb">print </span>] <span class="nb">each </span>] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>It produces the same output as the single-threaded <code>smac.</code> word above.</p>
<h3 id="networked">Networked</h3>
<p>The simple networked example creates a server configured to print <code>n</code> results
when a client connects:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">smac-server</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">server</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> utf8 <threaded-server>
</span></span><span class="line"><span class="cl"> <span class="s">"smac"</span> >>name
</span></span><span class="line"><span class="cl"> <span class="m">7979 </span>>>insecure
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span>'[ _ smac. ] >>handler <span class="k">;
</span></span></span></code></pre></div><p>You can run it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">20 </span>smac-server start-server
</span></span></code></pre></div><p>And then try it out:</p>
<pre tabindex="0"><code>$ nc localhost 7979
1
2
3
4
5
6
SMAC
8
9
10
11
12
13
SMAC
15
16
SMAC
18
19
20
</code></pre><p>A fun example for learning about a few introductory concepts in
<a href="https://factorcode.org">Factor</a>!</p>
Memoization Syntax
https://re.factorcode.org/2023/10/memoization-syntax.html
Thu, 26 Oct 2023 09:00:00 -0700https://re.factorcode.org/2023/10/memoization-syntax.html<p>A couple of days ago an interesting article was posted about the
<a href="https://railsatscale.com/2023-10-24-memoization-pattern-and-object-shapes/">performance impact of the memoization idiom on modern
Ruby</a>.
It covers some of the internal changes that have been happening in the last
few releases of <a href="https://www.ruby-lang.org">Ruby</a> and how some “object
shape” optimizations have impacted memoization performance.</p>
<p>If you look at the Wikipedia article for
<a href="https://en.wikipedia.org/wiki/Memoization">memoization</a>, you can see it
described as:</p>
<blockquote>
<p>In computing, <strong>memoization</strong> or <strong>memoisation</strong> is an optimization
technique used primarily to speed up computer programs by storing the
results of expensive function calls to pure functions and returning the
cached result when the same inputs occur again.</p>
</blockquote>
<p>While I use the <a href="https://docs.factorcode.org/content/article-memoize.html">memoize
vocabulary</a> in some
of the posts on this blog and it is used in over 100 words in the <a href="https://docs.factorcode.org/content/article-vocab-index.html">Factor
standard
library</a>, there
is an opportunity to talk a bit more about how it works in
<a href="https://factorcode.org">Factor</a>.</p>
<p>The first example often provided to show the benefit is a recursive
definition of the <a href="https://en.wikipedia.org/wiki/Fibonacci_sequence">Fibonacci
sequence</a>, which could be
implemented as:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fib</span> <span class="nf">( </span><span class="nv">m</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">1 </span><span class="nb">> </span>[ [ <span class="m">2 </span><span class="nb">- </span>fib ] [ <span class="m">1 </span><span class="nb">- </span>fib ] <span class="nb">bi + </span>] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><p>If you time this, it is quite slow:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">40 </span>fib ] time <span class="m">.
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">1.800567 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="m">102334155
</span></span></span></code></pre></div><p>In a similar manner to the <a href="https://docs.python.org/3/library/functools.html#functools.cache">functools.cache decorator in
Python</a>, you
can easily improve this by caching the results of previous computations by
using the <code>MEMO:</code> syntax which works with any
<a href="https://en.wikipedia.org/wiki/Arity">arity</a> function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MEMO:</span> <span class="nf">fib</span> <span class="nf">( </span><span class="nv">m</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">1 </span><span class="nb">> </span>[ [ <span class="m">2 </span><span class="nb">- </span>fib ] [ <span class="m">1 </span><span class="nb">- </span>fib ] <span class="nb">bi + </span>] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><p>And see that it is now much faster:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">40 </span>fib ] time <span class="m">.
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">0.005302375 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="m">102334155
</span></span></span></code></pre></div><p>If we wanted to memoize some internal part of a word, we have historically used
words like <a href="https://docs.factorcode.org/content/word-cache,assocs.html">cache</a>
or <a href="https://docs.factorcode.org/content/word-2cache,assocs.html">2cache</a> with a
<a href="https://docs.factorcode.org/content/article-hashtables.html">hashtable</a>
literal to compute a result by key, storing and returning if it was previously
calculated. This is a simple form of memoization – and I realized that we
could create a simpler syntax for this that uses the <a href="https://github.com/factor/factor/blob/master/core/memoize/memoize.factor">memoize
implementation</a>
to support arbitrary
<a href="https://docs.factorcode.org/content/article-quotations.html">quotations</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYNTAX: </span>MEMO[ parse-quotation <span class="nb">dup </span>infer memoize-quot <span class="nb">append! </span><span class="k">;
</span></span></span></code></pre></div><p>And then we use it in the middle of a word, pretending that something takes a
long time to compute and we need to memoize the computation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">answer</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"The answer is: "</span> <span class="nb">write </span>MEMO[ <span class="nb">+ </span><span class="m">1 </span>seconds sleep ] <span class="m">. </span><span class="k">;
</span></span></span></code></pre></div><p>Trying it out shows that the first time is slow and the second time is fast:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">1 2 </span>answer ] time
</span></span><span class="line"><span class="cl">The answer is: <span class="m">3
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">1.011060583 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">1 2 </span>answer ] time
</span></span><span class="line"><span class="cl">The answer is: <span class="m">3
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">0.00117075 </span>seconds
</span></span></code></pre></div><p>That’s a cool syntax word – and it’s available in the <a href="https://docs.factorcode.org/content/vocab-memoize.syntax.html">memoize.syntax
vocabulary</a>!</p>
SHA-256 from URL
https://re.factorcode.org/2023/09/sha-256-from-url.html
Tue, 12 Sep 2023 08:00:00 -0700https://re.factorcode.org/2023/09/sha-256-from-url.html<p>Álvaro Ramírez wrote a blog post about generating a <a href="https://xenodium.com/sha-256-hash-from-url-the-easy-way/">SHA-256 hash from URL, the
easy way</a> where they
describe wanting to download a file and generate a
<a href="https://en.wikipedia.org/wiki/SHA-2">SHA-256</a> hash of the contents easily.
Their solution involves copying a URL and then having some <a href="https://en.wikipedia.org/wiki/Emacs_Lisp">Emacs
Lisp</a> be able to read the clipboard,
download the file, then generate and return the hash on the clipboard.</p>
<p>I thought I’d show how this can be done in <a href="https://factorcode.org">Factor</a>, by
breaking the problem into smaller parts.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">checksums</span> <span class="nn">checksums.sha</span> <span class="nn">http.client</span> <span class="nn">io.directories</span> <span class="nn">io.files.temp</span> <span class="nn">kernel</span>
</span></span><span class="line"><span class="cl"><span class="nn">math.parser</span> <span class="nn">namespaces</span> <span class="nn">sequences</span> <span class="nn">ui.clipboards</span> <span class="k">;
</span></span></span></code></pre></div><p>The first step is downloading a file to a <a href="https://docs.factorcode.org/content/article-io.files.temp.html">temporary
file</a>,
returning the path of the downloaded file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">download-to-temp</span> <span class="nf">( </span><span class="nv">url</span> <span class="nf">-- </span><span class="nv">path</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>download-name temp-file [
</span></span><span class="line"><span class="cl"> [ ?delete-file ] [ download-to ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div><p>The next step is to build a word that applies a
<a href="https://docs.factorcode.org/content/article-checksums.html">checksum</a> to the
downloaded file contents:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">checksum-url</span> <span class="nf">( </span><span class="nv">url</span> <span class="nv">checksum</span> <span class="nf">-- </span><span class="nv">value</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ download-to-temp ] [ checksum-file ] <span class="nb">bi* </span><span class="k">;
</span></span></span></code></pre></div><p>The last step is to use the
<a href="https://docs.factorcode.org/content/article-clipboard-protocol.html">clipboard</a>
to access the URL that was copied – checking minimally that it looks like an
<code>http</code> or <code>https</code> URL – and then putting the checksum value back onto the
clipboard:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">checksum-clipboard</span> <span class="nf">( </span><span class="nv">checksum</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> clipboard <span class="nb">get </span>clipboard-contents
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"http"</span> <span class="nb">head? </span>[ <span class="nb">throw </span>] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> <span class="nb">swap </span>checksum-url bytes>hex-string
</span></span><span class="line"><span class="cl"> clipboard <span class="nb">get </span>set-clipboard-contents <span class="k">;
</span></span></span></code></pre></div><p>This could be improved with better error checking, and maybe cleaning up the
temporary file that was downloaded after running the checksum.</p>
<p>Give it a try!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> sha-256 checksum-clipboard
</span></span></code></pre></div>Sequence Case
https://re.factorcode.org/2023/09/sequence-case.html
Sun, 10 Sep 2023 08:00:00 -0700https://re.factorcode.org/2023/09/sequence-case.html<p>Some languages allow
<a href="https://en.wikipedia.org/wiki/Range_(computer_programming)">ranges</a> to be used
in <a href="https://en.wikipedia.org/wiki/Switch_statement">switch statements</a>. For
example, like in this Swift code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">count</span> <span class="p">=</span> <span class="mi">3_000_000_000_000</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">countedThings</span> <span class="p">=</span> <span class="s">"stars in the Milky Way"</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nv">naturalCount</span><span class="p">:</span> <span class="nb">String</span>
</span></span><span class="line"><span class="cl"><span class="k">switch</span> <span class="bp">count</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">naturalCount</span> <span class="p">=</span> <span class="s">"no"</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="mf">1.</span><span class="p">..</span><span class="mi">3</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">naturalCount</span> <span class="p">=</span> <span class="s">"a few"</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="mf">4.</span><span class="p">..</span><span class="mi">9</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">naturalCount</span> <span class="p">=</span> <span class="s">"several"</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="mf">10.</span><span class="p">..</span><span class="mi">99</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">naturalCount</span> <span class="p">=</span> <span class="s">"tens of"</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="mf">100.</span><span class="p">..</span><span class="mi">999</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">naturalCount</span> <span class="p">=</span> <span class="s">"hundreds of"</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="mf">1000.</span><span class="p">..</span><span class="mi">999_999</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">naturalCount</span> <span class="p">=</span> <span class="s">"thousands of"</span>
</span></span><span class="line"><span class="cl"><span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">naturalCount</span> <span class="p">=</span> <span class="s">"millions and millions of"</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="bp">println</span><span class="p">(</span><span class="s">"There are </span><span class="si">\(</span><span class="n">naturalCount</span><span class="si">)</span><span class="s"> </span><span class="si">\(</span><span class="n">countedThings</span><span class="si">)</span><span class="s">."</span><span class="p">)</span>
</span></span></code></pre></div><p>Let’s build this functionality in <a href="https://factorcode.org">Factor</a>!</p>
<h3 id="range-syntax">Range Syntax</h3>
<p>First, let’s look at how we can construct a <a href="https://docs.factorcode.org/content/article-ranges.html">range</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="m">1000 999,999 </span>[a..b]
</span></span></code></pre></div><p>If we wanted to use that in a case statement, we could wrap it with a
<a href="https://docs.factorcode.org/content/article-literals.html">literal</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ $[ <span class="m">1000 999,999 </span>[a..b] ] [ <span class="s">"thousands of"</span> ] }
</span></span></code></pre></div><p>But that’s not that elegant, instead let’s define a <a href="https://docs.factorcode.org/content/word-SYNTAX__colon__,syntax.html">syntax
word</a> to
construct our range:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYNTAX: </span>..= <span class="nb">dup pop </span>scan-object [a..b] <span class="nb">suffix! </span><span class="k">;
</span></span></span></code></pre></div><p>This is much cleaner:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="m">1000 </span>..= <span class="m">999,999 </span>[ <span class="s">"thousands of"</span> ] }
</span></span></code></pre></div><h3 id="combinators">Combinators</h3>
<p>In Factor, we have
<a href="https://docs.factorcode.org/content/article-combinators.html">combinators</a>
which are, at some level, just words that take code as input. We sometimes
refer to these as higher-level concepts, but they allow us to be more
expressive with fewer tokens.</p>
<p>Let’s build our combinator using a
<a href="https://docs.factorcode.org/content/article-macros.html">macro</a> to transform
some input cases:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MACRO:</span> <span class="nf">sequence-case</span> <span class="nf">( </span><span class="nv">assoc</span> <span class="nf">-- </span><span class="nv">quot</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>callable? [
</span></span><span class="line"><span class="cl"> [ <span class="nb">first dup </span>set? [ in? ] [ <span class="nb">= </span>] <span class="nb">? </span>'[ <span class="nb">dup </span>_ @ ] ]
</span></span><span class="line"><span class="cl"> [ <span class="nb">second </span>'[ <span class="nb">drop </span>@ ] ] <span class="nb">bi 2array
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map </span>[ <span class="nb">cond </span>] <span class="nb">curry </span><span class="k">;
</span></span></span></code></pre></div><p>Now we can write a Factor version of the original Swift example above:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="m">3,000,000,000,000 </span>{
</span></span><span class="line"><span class="cl"> { <span class="m">0 </span>[ <span class="s">"no"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">1 </span>..= <span class="m">3 </span>[ <span class="s">"a few"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">4 </span>..= <span class="m">9 </span>[ <span class="s">"several"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">10 </span>..= <span class="m">99 </span>[ <span class="s">"tens of"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">100 </span>..= <span class="m">999 </span>[ <span class="s">"hundreds of"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">1000 </span>..= <span class="m">999,999 </span>[ <span class="s">"thousands of"</span> ] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">"millions and millions of"</span> ]
</span></span><span class="line"><span class="cl">} sequence-case <span class="s">"There are %s stars in the Milky Way.\n"</span> printf
</span></span></code></pre></div><p>Cool!</p>
<p>This is available in the <code>combinators.extras</code> vocabulary in the development
version of Factor.</p>
ASCII Art
https://re.factorcode.org/2023/09/ascii-art.html
Fri, 08 Sep 2023 08:00:00 -0700https://re.factorcode.org/2023/09/ascii-art.html<p>Raymond Hettinger, an active contributor to <a href="https://python.org">Python</a> and
responsible for many useful improvements to the language, likes to tweet short
and sweet bits of useful Python knowledge. I stumbled into one <a href="https://twitter.com/raymondh/status/52810992366788609">fun
one-liner</a> from long
ago:</p>
<p>
<img src="https://re.factorcode.org/images/2023-09-08-raymondh.png" alt="" width="587" height="173" />
</p>
<p>I thought I would show how it might translate into
<a href="https://factorcode.org">Factor</a>. To translate this code into a
<a href="https://concatenative.org/">concatenative</a> language, we are going to work from
the inside and move out.</p>
<h3 id="step-1">Step 1</h3>
<p>We start with the <code>sum</code> of the <a href="https://docs.python.org/3/library/itertools.html#itertools.product">cartesian
product</a> of
eight sequences of the numbers 0 through 5:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nb">map</span><span class="p">(</span><span class="nb">sum</span><span class="p">,</span> <span class="n">product</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">6</span><span class="p">),</span> <span class="n">repeat</span><span class="o">=</span><span class="mi">8</span><span class="p">))</span>
</span></span></code></pre></div><p><em>vs.</em></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="m">8 6 </span><iota> <span class="nb"><repetition> </span>[ <span class="nb">sum </span>] product-map
</span></span></code></pre></div><h3 id="step-2">Step 2</h3>
<p>Next, we see that it counts each element, and produces a sorted list of
items:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nb">sorted</span><span class="p">(</span><span class="n">Counter</span><span class="p">(</span><span class="o">...</span><span class="p">)</span><span class="o">.</span><span class="n">items</span><span class="p">())</span>
</span></span></code></pre></div><p><em>vs.</em></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">histogram sort-keys
</span></span></code></pre></div><h3 id="step-3">Step 3</h3>
<p>And finally, create a string of lines of stars and print it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nb">print</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s1">'*'</span><span class="o">*</span><span class="p">(</span><span class="n">c</span><span class="o">//</span><span class="mi">2000</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span><span class="n">c</span> <span class="ow">in</span> <span class="o">...</span><span class="p">)</span>
</span></span></code></pre></div><p><em>vs.</em></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="nb">values </span>[ <span class="m">2000 </span><span class="nb">/i </span><span class="sc">CHAR: * </span><span class="nb"><string> </span>] <span class="nb">map </span><span class="s">"\n"</span> <span class="nb">join print
</span></span></span></code></pre></div><h3 id="solution">Solution</h3>
<p>Putting it all together:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USING:</span> <span class="nn">assocs</span> <span class="nn">io</span> <span class="nn">math</span> <span class="nn">math.statistics</span> <span class="nn">sequences</span>
</span></span><span class="line"><span class="cl"> <span class="nn">sequences.product</span> <span class="nn">sorting</span> <span class="nn">strings</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">8 6 </span><iota> <span class="nb"><repetition> </span>[ <span class="nb">sum </span>] product-map
</span></span><span class="line"><span class="cl"> histogram sort-keys <span class="nb">values
</span></span></span><span class="line"><span class="cl"> [ <span class="m">2000 </span><span class="nb">/i </span><span class="sc">CHAR: * </span><span class="nb"><string> </span>] <span class="nb">map </span><span class="s">"\n"</span> <span class="nb">join print
</span></span></span></code></pre></div><p>It makes this nice <a href="https://en.wikipedia.org/wiki/ASCII_art">ASCII Art</a>
visualization:</p>
<pre tabindex="0"><code>*
***
*****
********
************
******************
*************************
********************************
*****************************************
*************************************************
********************************************************
**************************************************************
******************************************************************
*******************************************************************
******************************************************************
**************************************************************
********************************************************
*************************************************
*****************************************
********************************
*************************
******************
************
********
*****
***
*
</code></pre>LEB128
https://re.factorcode.org/2023/09/leb128.html
Sun, 03 Sep 2023 08:00:00 -0700https://re.factorcode.org/2023/09/leb128.html<p><a href="https://en.wikipedia.org/wiki/LEB128">LEB128</a> – Little Endian Base 128 – is
a variable-length encoding format designed to store arbitrarily large integers
in a small number of bytes. There are two variations: unsigned LEB128 and
signed LEB128. These vary slightly, so a user program that wants to decode
LEB128 values would explictly choose the appropriate unsigned or signed
methods.</p>
<p>Recently, I implemented these in <a href="https://factorcode.org">Factor</a> and wanted to
describe the implementation:</p>
<h3 id="decoding">Decoding</h3>
<p>For decoding unsigned LEB128 values, we accumulate the integer 7 bits at a
time:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">uleb128></span> <span class="nf">( </span><span class="nv">byte-array</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>[ [ <span class="m">7 </span>bits ] [ <span class="m">7 </span><span class="nb">* shift </span>] <span class="nb">bi* + </span>] <span class="nb">reduce-index </span><span class="k">;
</span></span></span></code></pre></div><p>For decoding signed LEB128 values, we do the same thing, but the last byte
indicates if the value was negative, and if it was we bring the sign bit back
in:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">leb128></span> <span class="nf">( </span><span class="nv">byte-array</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ uleb128> ] <span class="nb">keep dup last </span><span class="m">6 </span><span class="nb">bit?
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span><span class="m">7 </span><span class="nb">* 2^ neg bitor </span>] [ <span class="nb">drop </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><h3 id="encoding">Encoding</h3>
<p>For encoding unsigned LEB128 values, we output in 7-bit segments, and then for
the final segment use the eighth bit to indicate the end of the stream of
bytes.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">>uleb128</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">byte-array</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> BV{ } <span class="nb">clone </span>:> accum
</span></span><span class="line"><span class="cl"> n assert-non-negative [
</span></span><span class="line"><span class="cl"> [ <span class="m">-7 </span><span class="nb">shift dup zero? not </span>] [ <span class="m">7 </span>bits ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> <span class="nb">over </span>[ <span class="m">0x80 </span><span class="nb">bitor </span>] <span class="nb">when </span>accum <span class="nb">push
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop drop </span>accum B{ } <span class="nb">like </span><span class="k">;
</span></span></span></code></pre></div><p>For encoding signed LEB128 values, we output in 7-bit segments, but our exit
condition depends on reaching the end of the integer and conditionally whether
the sixth bit was set in that segment.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">>leb128</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">byte-array</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> BV{ } <span class="nb">clone </span>:> accum
</span></span><span class="line"><span class="cl"> n [
</span></span><span class="line"><span class="cl"> [ <span class="m">-7 </span><span class="nb">shift dup </span>] [ <span class="m">7 </span>bits ] <span class="nb">bi </span>:> <span class="nf">( </span><span class="nv">i</span> <span class="nv">b</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ i <span class="m">0 </span><span class="nb">= </span>] [ b <span class="m">6 </span><span class="nb">bit? not </span>] }
</span></span><span class="line"><span class="cl"> { [ i <span class="m">-1 </span><span class="nb">= </span>] [ b <span class="m">6 </span><span class="nb">bit? </span>] }
</span></span><span class="line"><span class="cl"> [ <span class="no">f </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span>b <span class="nb">over </span>[ <span class="m">0x80 </span><span class="nb">bitor </span>] <span class="nb">when </span>accum <span class="nb">push
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop drop </span>accum B{ } <span class="nb">like </span><span class="k">;
</span></span></span></code></pre></div><h3 id="testing">Testing</h3>
<p>Some test cases for unsigned LEB128:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">[ <span class="m">-1 </span>>uleb128 ] [ non-negative-number-expected? ] must-fail-with
</span></span><span class="line"><span class="cl">{ B{ <span class="m">255 255 127 </span>} } [ <span class="m">0x1fffff </span>>uleb128 ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">0x1fffff </span>} [ B{ <span class="m">255 255 127 </span>} uleb128> ] unit-test
</span></span><span class="line"><span class="cl">{ B{ <span class="m">0xe5 0x8e 0x26 </span>} } [ <span class="m">624485 </span>>uleb128 ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">624485 </span>} [ B{ <span class="m">0xe5 0x8e 0x26 </span>} uleb128> ] unit-test
</span></span></code></pre></div><p>Some test cases for signed LEB128:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ B{ <span class="m">255 255 255 0 </span>} } [ <span class="m">0x1fffff </span>>leb128 ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">0x1fffff </span>} [ B{ <span class="m">255 255 255 0 </span>} leb128> ] unit-test
</span></span><span class="line"><span class="cl">{ B{ <span class="m">0xc0 0xbb 0x78 </span>} } [ <span class="m">-123456 </span>>leb128 ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">-123456 </span>} [ B{ <span class="m">0xc0 0xbb 0x78 </span>} leb128> ] unit-test
</span></span></code></pre></div><h3 id="performance">Performance</h3>
<p>This is available in a recent nightly build, with some support for reading and
writing LEB128 values to streams, and some performance improvements by
specifying some type information so the compiler can understand we are working
with integers and byte-arrays and produce more optimal code.</p>
<p>My current laptop decodes and encodes around 40 million per second, which is
pretty good for a dynamic language. There are some references in the <a href="https://en.wikipedia.org/wiki/LEB128#Fast_decoding">fast
decoding</a> section to some
papers that present some SIMD techniques called “Masked VByte” and “Stream
VByte” for significantly improving upon this simple scalar implementation.</p>
Periodic Table
https://re.factorcode.org/2023/09/periodic-table.html
Fri, 01 Sep 2023 16:00:00 -0700https://re.factorcode.org/2023/09/periodic-table.html<p>Despite the difficulty that I had with freshman chemistry, I’ve always enjoyed
learning about the <a href="https://en.wikipedia.org/wiki/Periodic_table">periodic
table</a>, writing blog posts about
<a href="https://re.factorcode.org/2011/12/elementology.html">making words with element symbols</a>, and I do think
it would be awesome to have a <a href="https://www.techeblog.com/bill-gates-wall-mounted-periodic-table-office/">giant wall-mounted periodic
table</a>
like Bill Gates has in his office.</p>
<p>The other day I wanted to demonstrate the <a href="https://docs.factorcode.org/content/article-ui.html">UI
framework</a> by making
another simple <a href="https://docs.factorcode.org/content/article-new-gadgets.html">UI
gadget</a>. It
occurred to me that we could make a simple periodic table as an example.</p>
<p>
<img src="https://re.factorcode.org/images/2023-09-01-periodic-table.png" alt="" width="1073" height="629" />
</p>
<p>The code is about 140 lines to define all the elements and their groups, 10
lines to organize them into a table, and about 40 lines to build the element
boxes, put them into a gadget, and add a legend at the bottom. Each element is
a <code><roll-button></code> that opens the appropriate
<a href="https://en.wikipedia.org/">Wikipedia</a> page for each <a href="https://en.wikipedia.org/wiki/Chemical_element">chemical
element</a>.</p>
<p>I was reminded of this recently by a funny comment on the <a href="https://discord.gg/QxJYZx3QDf">Factor
Discord</a>:</p>
<blockquote>
<p>Factor crushing the competition thanks to periodic-table vocab</p>
<p><a href="https://codegolf.stackexchange.com/questions/264714/is-it-an-element/264723#264723">https://codegolf.stackexchange.com/questions/264714/is-it-an-element/264723#264723</a></p>
</blockquote>
<p>You can view the source code for the <a href="https://github.com/factor/factor/blob/master/extra/periodic-table/periodic-table.factor">periodic table
vocabulary</a>
or try it yourself:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"periodic-table"</span> run
</span></span></code></pre></div>RGBA Clock
https://re.factorcode.org/2023/08/rgba-clock.html
Tue, 29 Aug 2023 07:00:00 -0700https://re.factorcode.org/2023/08/rgba-clock.html<p>Today, I’d like to build a little <a href="https://docs.factorcode.org/content/article-building-ui.html">UI
gadget</a> clock
that changes color as the time changes in <a href="https://factorcode.org">Factor</a>.</p>
<p>
<img src="https://re.factorcode.org/images/2023-08-29-rgba-clock.png" alt="" width="456" height="50" />
</p>
<p><a href="https://en.wikipedia.org/wiki/Unix_time">Unix time</a> is measured as the time –
usually seconds – since the Unix
<a href="https://en.wikipedia.org/wiki/Epoch_(computing)">epoch</a> at 00:00:00 UTC on
January 1, 1970. It is used on many systems and, perhaps, will cause <a href="https://en.wikipedia.org/wiki/Year_2038_problem">Year 2038
problems</a> on some when it
exceeds the maximum value of a signed 32-bit integer.</p>
<p>This is perfect – we need a way to convert a timestamp into a <a href="https://docs.factorcode.org/content/word-rgba%2Ccolors.html">rgba
color</a> – we can
calculate a <em>unix time</em> in integer seconds and then take each 8-bit segment to
refer to a red, green, blue, and alpha value.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">timestamp>rgba</span> <span class="nf">( </span><span class="nv">timestamp</span> <span class="nf">-- </span><span class="nv">color/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> timestamp>unix-time <span class="nb">>integer
</span></span></span><span class="line"><span class="cl"> <span class="m">24 </span><span class="nb">2^ /mod </span><span class="m">16 </span><span class="nb">2^ /mod </span><span class="m">8 </span><span class="nb">2^ /mod
</span></span></span><span class="line"><span class="cl"> [ <span class="m">255 </span><span class="nb">/f </span>] <span class="m">4 </span>napply <rgba> <span class="k">;
</span></span></span></code></pre></div><p>We can then extend a <a href="https://docs.factorcode.org/content/article-ui.gadgets.labels.html">UI
label</a> to
have a timer that starts when the gadget becomes visible and updates its
background colors based on the <a href="https://docs.factorcode.org/content/word-now,calendar.html">current
time</a> and chooses
an appropriate foreground text color to match. For that we can use the
<code>contrast-text-color</code> word from the <a href="https://docs.factorcode.org/content/vocab-colors.contrast.html">colors.contrast
vocabulary</a> to
select either <em>white-over-background</em> or <em>black-over-background</em> depending on
the <a href="https://www.w3.org/TR/WCAG20/#relativeluminancedef">relative luminance</a> of
the background color.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">rgba-clock</span> < <span class="nc">label</span> <span class="nv">timer</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">rgba-clock</span> <span class="nf">graft*</span>
</span></span><span class="line"><span class="cl"> [ timer>> start-timer ] [ call-next-method ] <span class="nb">bi </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">rgba-clock</span> <span class="nf">ungraft*</span>
</span></span><span class="line"><span class="cl"> [ timer>> stop-timer ] [ call-next-method ] <span class="nb">bi </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">update-colors</span> <span class="nf">( </span><span class="nv">color</span> <span class="nv">label</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ [ contrast-text-color ] <span class="nb">dip </span>font>> foreground<< ]
</span></span><span class="line"><span class="cl"> [ [ <solid> ] <span class="nb">dip </span>interior<< ] <span class="nb">2bi </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><rgba-clock></span> <span class="nf">( -- </span><span class="nv">gadget</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"99:99:99"</span> rgba-clock new-label
</span></span><span class="line"><span class="cl"> monospace-font >>font
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>'[
</span></span><span class="line"><span class="cl"> _ now
</span></span><span class="line"><span class="cl"> [ timestamp>hms >>string ]
</span></span><span class="line"><span class="cl"> [ timestamp>rgba <span class="nb">swap </span>update-colors ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] <span class="no">f </span><span class="m">1 </span>seconds <timer> >>timer <span class="k">;
</span></span></span></code></pre></div><p>And then we can try it out!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <rgba-clock> gadget.
</span></span></code></pre></div><p>This is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/rgba-clock/rgba-clock.factor">GitHub</a>.</p>
Drunken Bishop
https://re.factorcode.org/2023/08/drunken-bishop.html
Sun, 27 Aug 2023 08:00:00 -0700https://re.factorcode.org/2023/08/drunken-bishop.html<p>The <a href="https://www.openssh.com">OpenSSH project</a> is a widely available tool for
working with the <a href="https://en.wikipedia.org/wiki/Secure_Shell">SSH protocol</a> in
a variety of ways on a variety of operating systems. Their project description
states:</p>
<blockquote>
<p>OpenSSH is the premier connectivity tool for remote login with the SSH
protocol. It encrypts all traffic to eliminate eavesdropping, connection
hijacking, and other attacks. In addition, OpenSSH provides a large suite of
secure tunneling capabilities, several authentication methods, and
sophisticated configuration options.</p>
</blockquote>
<p>One of the interesting features that it contains is a method of visualizing
<a href="https://en.wikipedia.org/wiki/Public_key_fingerprint">public key fingerprints</a>
to allow a user to more easily see that a key has changed by examining a visual
output that looks something like this:</p>
<pre tabindex="0"><code>+----[RSA 2048]---+
| . o.+o .|
| . + * +o...|
| + * .. ... |
| o + . . |
| S o . |
| o . |
| . o|
| .o|
| Eo|
+------[MD5]------+
</code></pre><p>This is the <a href="https://github.com/openssh/openssh-portable/blob/master/sshkey.c#L976">Drunken Bishop
algorithm</a>,
a variant of a technique called <em>random art</em> that was originally described in
the paper <a href="http://users.ece.cmu.edu/~dawnsong/papers/randomart.pdf">Hash Visualization: a New Technique to improve Real-World
Security</a>. You can see
more information about it in <a href="http://www.dirk-loss.de/sshvis/drunken_bishop.pdf">The drunken bishop: An analysis of the OpenSSH fingerprint
visualization algorithm</a>.</p>
<p>This <a href="https://man.openbsd.org/ssh_config#VisualHostKey">OpenSSH feature</a> is
controlled by the <code>VisualHostKey</code> flag:</p>
<blockquote>
<p><strong>VisualHostKey</strong></p>
<p>If this flag is set to <strong>yes</strong>, an ASCII art representation of the remote
host key fingerprint is printed in addition to the fingerprint string at
login and for unknown host keys. If this flag is set to <strong>no</strong> (the default),
no fingerprint strings are printed at login and only the fingerprint string
will be printed for unknown host keys.</p>
</blockquote>
<p>This can be enabled by adding to your <code>~/.ssh/config</code>:</p>
<pre tabindex="0"><code>VisualHostKey yes
</code></pre><p>Or by adding this option in your <code>ssh</code> command:</p>
<pre tabindex="0"><code>$ ssh -o VisualHostKey=yes your.host.name
</code></pre><h2 id="implementation">Implementation</h2>
<p>We are going to be implementing this in the <a href="https://factorcode.org">Factor</a>
programming language.</p>
<p>The algorithm begins by defining a visual board – by default 9 rows by 17
columns – and a starting position in the middle of the board. Each 8-bit byte
of input is split into 2-bit groups which indicate where the bishop moves:</p>
<table>
<thead>
<tr>
<th>Value</th>
<th>Direction</th>
</tr>
</thead>
<tbody>
<tr>
<td>00</td>
<td>↖</td>
</tr>
<tr>
<td>01</td>
<td>↗</td>
</tr>
<tr>
<td>10</td>
<td>↙</td>
</tr>
<tr>
<td>11</td>
<td>↘</td>
</tr>
</tbody>
</table>
<p>As the bishop moves around the board diagonally, it increments a counter in
each cell. However, the bishop cannot move through the walls on the edge of
the board, remaining stuck in whichever direction is blocked.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">WIDTH</span> <span class="m">17
</span></span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">HEIGHT</span> <span class="m">9
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">drunken-bishop</span> <span class="nf">( </span><span class="nv">bytes</span> <span class="nf">-- </span><span class="nv">board</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> HEIGHT [ WIDTH <span class="m">0 </span><span class="nb"><array> </span>] <span class="nb">replicate </span>:> board
</span></span><span class="line"><span class="cl"> HEIGHT <span class="nb">2/ </span>:> y!
</span></span><span class="line"><span class="cl"> WIDTH <span class="nb">2/ </span>:> x!
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="m">15 </span>x y board <span class="nb">nth set-nth </span><span class="c">! starting position</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> bytes [
</span></span><span class="line"><span class="cl"> { <span class="m">0 -2 -4 -6 </span>} [
</span></span><span class="line"><span class="cl"> <span class="nb">shift </span><span class="m">2 </span>bits {
</span></span><span class="line"><span class="cl"> { <span class="mb">0b00 </span>[ <span class="m">-1 -1 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="mb">0b01 </span>[ <span class="m">-1 </span> <span class="m">1 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="mb">0b10 </span>[ <span class="m">1 -1 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="mb">0b11 </span>[ <span class="m">1 </span> <span class="m">1 </span>] }
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span>:> <span class="nf">( </span><span class="nv">dy</span> <span class="nv">dx</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> dy y <span class="nb">+ </span><span class="m">0 </span>HEIGHT <span class="m">1 </span><span class="nb">- </span>clamp y!
</span></span><span class="line"><span class="cl"> dx x <span class="nb">+ </span><span class="m">0 </span>WIDTH <span class="m">1 </span><span class="nb">- </span>clamp x!
</span></span><span class="line"><span class="cl"> x y board <span class="nb">nth </span>[ <span class="nb">dup </span><span class="m">14 </span><span class="nb">< </span>[ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">when </span>] <span class="nb">change-nth
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">with each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="m">16 </span>x y board <span class="nb">nth set-nth </span><span class="c">! ending position</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> board <span class="k">;
</span></span></span></code></pre></div><p>The output is rendered using an <code>.o+=*BOX@%&#/^</code> alphabet with <code>S</code> for the
starting position and <code>E</code> for the ending position specially rendered:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">SYMBOLS</span> <span class="s">" .o+=*BOX@%&#/^SE"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">drunken-bishop.</span> <span class="nf">( </span><span class="nv">bytes</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> drunken-bishop [ SYMBOLS <span class="nb">nths print </span>] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>We can try this out and see that it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"fc94b0c1e5b0987c5843997697ee9fb7"</span>
</span></span><span class="line"><span class="cl"> hex-string>bytes drunken-bishop.
</span></span><span class="line"><span class="cl"> .=o. <span class="m">. </span>
</span></span><span class="line"><span class="cl"> <span class="m">. </span>*+*. o
</span></span><span class="line"><span class="cl"> =.*..o
</span></span><span class="line"><span class="cl"> o <span class="nb">+ </span>..
</span></span><span class="line"><span class="cl"> S o.
</span></span><span class="line"><span class="cl"> o <span class="m">. </span>
</span></span><span class="line"><span class="cl"> <span class="m">. </span> <span class="m">. . </span>
</span></span><span class="line"><span class="cl"> o <span class="m">.
</span></span></span><span class="line"><span class="cl"> E.
</span></span></code></pre></div><p>This is available in the <a href="https://github.com/factor/factor/blob/master/extra/drunken-bishop/drunken-bishop.factor">drunken-bishop
vocabulary</a>
in a recent development version.</p>
Next Combination
https://re.factorcode.org/2023/08/next-combination.html
Fri, 25 Aug 2023 08:00:00 -0700https://re.factorcode.org/2023/08/next-combination.html<p>Eleven years ago, I blogged about <a href="https://re.factorcode.org/2012/03/next-permutation.html">computing the next
permutation</a> of a sequence.</p>
<p>As part of some performance improvements to the
<a href="https://docs.factorcode.org/content/vocab-math.combinatorics.html">math.combinatorics</a>
vocabulary that I was making around the same time, I needed a way of
calculating the indices of the next combination of a given sequence.</p>
<h3 id="implementation">Implementation</h3>
<p>Without going into a great explanation, we split the problem into a few
steps, operating on a sequence of indices and modifying in place – either
incrementing the indices after the “max index” or the last index.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-max-index</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">i</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">over length - </span>'[ _ <span class="nb">+ >= </span>] <span class="nb">find-index drop </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">increment-rest</span> <span class="nf">( </span><span class="nv">i</span> <span class="nv">seq</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ nth-unsafe ] [ <span class="nb">swap </span>index-to-tail <slice-unsafe> ] <span class="nb">2bi
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="m">1 </span><span class="nb">+ dup </span>] <span class="nb">map! 2drop </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">increment-last</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ index-of-last [ <span class="m">1 </span><span class="nb">+ </span>] change-nth-unsafe ] <span class="nb">unless-empty </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">next-combination</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> seq n find-max-index [
</span></span><span class="line"><span class="cl"> <span class="m">1 </span>[-] seq increment-rest
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> seq increment-last
</span></span><span class="line"><span class="cl"> ] <span class="nb">if* </span><span class="k">; inline
</span></span></span></code></pre></div><p>We can show how it works by using <code>clone</code> to see the intermediate results of
“<em>5 choose 3</em>”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">0 1 2 </span>} <span class="m">10 </span>[
</span></span><span class="line"><span class="cl"> [ <span class="nb">clone </span><span class="m">5 </span>next-combination ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">replicate nip </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> { <span class="m">0 1 2 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">0 1 3 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">0 1 4 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">0 2 3 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">0 2 4 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">0 3 4 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">1 2 3 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">1 2 4 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">1 3 4 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">2 3 4 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>This is used to implement our various <em>combinations</em> words:
<code>each-combination</code>, <code>map-combinations</code>, <code>filter-combinations</code>,
<code>all-combinations</code>, <code>find-combination</code>, <code>reduce-combinations</code>, etc.</p>
<h3 id="benchmarking">Benchmarking</h3>
<p>The benchmark I have chosen is one that intentionally creates a large array of
almost 65 million items with the result of allocating all combinations of 200
items choosing 4 items at a time.</p>
<p>We can run this benchmark on an Apple Mac mini (2018) with a 3.2 GHz 6-Core
Intel Core i7 processor and 16 GB of memory that is used as part of the Factor
nightly build farm.</p>
<p><a href="https://re.factorcode.org/2023/08/factor-0-99-now-available.html">Factor 0.99</a> takes <strong>8.3 seconds</strong>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">200 </span><iota> <span class="m">4 </span>all-combinations <span class="nb">length </span>] time <span class="m">.
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">8.269610590999999 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Additional information was collected.
</span></span><span class="line"><span class="cl">dispatch-stats. <span class="nb">- </span>Print method dispatch statistics
</span></span><span class="line"><span class="cl">gc-events. <span class="nb">- </span>Print all garbage collection events
</span></span><span class="line"><span class="cl">gc-stats. <span class="nb">- </span>Print breakdown <span class="nb">of </span>different garbage collection events
</span></span><span class="line"><span class="cl">gc-summary. <span class="nb">- </span>Print aggregate garbage collection statistics
</span></span><span class="line"><span class="cl"><span class="m">64684950
</span></span></span></code></pre></div><p>Look at the <code>gc-summary.</code> to see that more than half the time (60% or 4.9
seconds) is involved in garbage collection as part of allocating the almost 65
million results:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc-summary.
</span></span><span class="line"><span class="cl">Collections: <span class="m">1,480
</span></span></span><span class="line"><span class="cl">Cards scanned: <span class="m">7,070,518
</span></span></span><span class="line"><span class="cl">Decks scanned: <span class="m">9,124
</span></span></span><span class="line"><span class="cl">Code blocks scanned: <span class="m">2
</span></span></span><span class="line"><span class="cl">Total time: <span class="m">4,910,722 </span>µs
</span></span><span class="line"><span class="cl">Card scan time: <span class="m">3,256,711 </span>µs
</span></span><span class="line"><span class="cl">Code block scan time: <span class="m">162 </span>µs
</span></span><span class="line"><span class="cl">Marking time: <span class="m">0 </span>µs
</span></span><span class="line"><span class="cl">Data heap sweep time: <span class="m">0 </span>µs
</span></span><span class="line"><span class="cl">Code heap sweep time: <span class="m">0 </span>µs
</span></span><span class="line"><span class="cl">Data compaction time: <span class="m">0 </span>µs
</span></span></code></pre></div><h3 id="other-languages">Other Languages</h3>
<p>On that same machine we can try some other programming languages and see how
they compare, attempting to get the same behavior as the Factor example
above.</p>
<p><a href="https://python.org">Python</a> 3.11.4 takes <strong>8.4 seconds</strong>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">In</span> <span class="p">[</span><span class="mi">1</span><span class="p">]:</span> <span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">combinations</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">In</span> <span class="p">[</span><span class="mi">2</span><span class="p">]:</span> <span class="o">%</span><span class="n">time</span> <span class="nb">print</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">combinations</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">200</span><span class="p">),</span> <span class="mi">4</span><span class="p">))))</span>
</span></span><span class="line"><span class="cl"><span class="mi">64684950</span>
</span></span><span class="line"><span class="cl"><span class="n">CPU</span> <span class="n">times</span><span class="p">:</span> <span class="n">user</span> <span class="mf">5.64</span> <span class="n">s</span><span class="p">,</span> <span class="n">sys</span><span class="p">:</span> <span class="mf">2.77</span> <span class="n">s</span><span class="p">,</span> <span class="n">total</span><span class="p">:</span> <span class="mf">8.41</span> <span class="n">s</span>
</span></span><span class="line"><span class="cl"><span class="n">Wall</span> <span class="n">time</span><span class="p">:</span> <span class="mf">8.44</span> <span class="n">s</span>
</span></span></code></pre></div><p><a href="https://www.pypy.org">PyPy</a> 7.3.12 (compatible with Python 3.10) takes <strong>17 seconds</strong>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">>>>></span> <span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">combinations</span>
</span></span><span class="line"><span class="cl"><span class="o">>>>></span> <span class="kn">from</span> <span class="nn">time</span> <span class="kn">import</span> <span class="n">time</span>
</span></span><span class="line"><span class="cl"><span class="o">>>>></span> <span class="n">t0</span> <span class="o">=</span> <span class="n">time</span><span class="p">();</span> <span class="nb">print</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">combinations</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">200</span><span class="p">),</span> <span class="mi">4</span><span class="p">))));</span> <span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">t0</span>
</span></span><span class="line"><span class="cl"><span class="mi">64684950</span>
</span></span><span class="line"><span class="cl"><span class="mf">17.00031805038452</span>
</span></span></code></pre></div><p><a href="https://ruby-lang.org">Ruby</a> 3.2.2 takes <strong>37.4 seconds</strong>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">irb</span><span class="p">(</span><span class="n">main</span><span class="p">):</span><span class="mo">001</span><span class="p">:</span><span class="mi">0</span><span class="o">></span> <span class="n">measure</span>
</span></span><span class="line"><span class="cl"><span class="no">TIME</span> <span class="n">is</span> <span class="n">added</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="o">=></span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">irb</span><span class="p">(</span><span class="n">main</span><span class="p">):</span><span class="mo">002</span><span class="p">:</span><span class="mi">0</span><span class="o">></span> <span class="p">(</span><span class="mi">0</span><span class="o">...</span><span class="mi">200</span><span class="p">)</span><span class="o">.</span><span class="n">to_a</span><span class="o">.</span><span class="n">combination</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span><span class="o">.</span><span class="n">to_a</span><span class="o">.</span><span class="n">length</span>
</span></span><span class="line"><span class="cl"><span class="n">processing</span> <span class="ss">time</span><span class="p">:</span> <span class="mi">37</span><span class="o">.</span><span class="mi">413635</span><span class="n">s</span>
</span></span><span class="line"><span class="cl"><span class="o">=></span> <span class="mi">64684950</span>
</span></span></code></pre></div><p><a href="https://julialang.org">Julia</a> 1.9.2 takes <strong>10.7 seconds</strong>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-julia" data-lang="julia"><span class="line"><span class="cl"><span class="n">julia</span><span class="o">></span> <span class="p">]</span><span class="n">add</span> <span class="n">Combinatorics</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">julia</span><span class="o">></span> <span class="k">using</span> <span class="n">Combinatorics</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">julia</span><span class="o">></span> <span class="nd">@time</span> <span class="n">length</span><span class="p">(</span><span class="n">collect</span><span class="p">(</span><span class="n">combinations</span><span class="p">(</span><span class="n">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">200</span><span class="p">),</span> <span class="mi">4</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl"> <span class="mf">10.668982</span> <span class="n">seconds</span> <span class="p">(</span><span class="mf">129.37</span> <span class="n">M</span> <span class="n">allocations</span><span class="o">:</span> <span class="mf">8.193</span> <span class="n">GiB</span><span class="p">,</span> <span class="mf">37.74</span><span class="o">%</span> <span class="n">gc</span> <span class="n">time</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mi">64684950</span>
</span></span></code></pre></div><p><a href="https://crystal-lang.org">Crystal</a> 1.9.2 takes <strong>515 seconds</strong> (interpreted) or <strong>13.7 seconds</strong> (compiled):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="c1"># Run crystal interpreted</span>
</span></span><span class="line"><span class="cl">$ crystal i
</span></span><span class="line"><span class="cl">icr:1> <span class="nv">t0</span> <span class="o">=</span> Time.utc<span class="p">;</span>
</span></span><span class="line"><span class="cl"> puts <span class="o">(</span>0...200<span class="o">)</span>.to_a.combinations<span class="o">(</span>4<span class="o">)</span>.to_a.size<span class="p">;</span>
</span></span><span class="line"><span class="cl"> Time.utc - t0
</span></span><span class="line"><span class="cl"><span class="nv">64684950</span> <span class="o">=</span>> 00:08:34.728442000
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Make a crystal source file</span>
</span></span><span class="line"><span class="cl">$ <span class="nb">echo</span> <span class="s2">"t0 = Time.utc
</span></span></span><span class="line"><span class="cl"><span class="s2">puts (0...200).to_a.combinations(4).to_a.size
</span></span></span><span class="line"><span class="cl"><span class="s2">puts (Time.utc - t0)"</span> > combo.cr
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Run crystal compiled</span>
</span></span><span class="line"><span class="cl">$ crystal combo.cr
</span></span><span class="line"><span class="cl"><span class="m">64684950</span>
</span></span><span class="line"><span class="cl">00:00:13.739838000
</span></span></code></pre></div><p><a href="https://clojure.org">Clojure</a> 1.11.1 takes <strong>10.7 seconds</strong>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="nv">user=></span> <span class="p">(</span><span class="kd">ns </span><span class="nv">user</span> <span class="p">(</span><span class="ss">:use</span> <span class="p">[</span><span class="nv">clojure.math.combinatorics</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">user=></span> <span class="p">(</span><span class="nb">time </span><span class="p">(</span><span class="nb">count </span><span class="p">(</span><span class="nf">combinations</span> <span class="p">(</span><span class="nb">range </span><span class="mi">0</span> <span class="mi">200</span><span class="p">)</span> <span class="mi">4</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl"><span class="s">"Elapsed time: 10685.687736 msecs"</span>
</span></span><span class="line"><span class="cl"><span class="mi">64684950</span>
</span></span></code></pre></div><p><a href="https://rakudo.org">Rakudo</a> v2023.08 takes <strong>808.9 seconds</strong>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ <span class="nb">time</span> raku -e <span class="s2">"print (1..200).combinations(4).Array.elems"</span>
</span></span><span class="line"><span class="cl">759.45s user 44.32s system 99% cpu 13:28.86 total
</span></span><span class="line"><span class="cl"><span class="m">64684950</span>
</span></span></code></pre></div><p>Considering that we only take the length of the large array of combinations, it
is a bit artificial as a benchmark, with room for various optimizations, but it
does seem to highlight both algorithmic and garbage collector issues in various
languages.</p>
<p>Factor compares pretty favorably!</p>
Factor 0.99 now available
https://re.factorcode.org/2023/08/factor-0-99-now-available.html
Thu, 24 Aug 2023 08:00:00 -0700https://re.factorcode.org/2023/08/factor-0-99-now-available.html<p><em>“I hear and I forget. I see and I remember. I do and I understand.” -
Confucius</em></p>
<p>I’m very pleased to announce the release of
<a href="https://factorcode.org">Factor</a> 0.99!</p>
<table class="downloads" cellspacing="0">
<colgroup>
<col style="width: 25%" />
<col style="width: 25%" />
<col style="width: 25%" />
<col style="width: 25%" />
</colgroup>
<thead>
<tr class="header">
<th class="nobg" style="text-align: center;">OS/CPU</th>
<th class="bg" style="text-align: center;" scope="col">Windows</th>
<th class="bg" style="text-align: center;" scope="col">Mac OS</th>
<th class="bg" style="text-align: center;" scope="col">Linux</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<th class="bg" style="text-align: center;" scope="row">x86</th>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.99/factor-windows-x86-32-0.99.zip">0.99</a>
</td>
<td class="doesnotexist">
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.99/factor-linux-x86-32-0.99.tar.gz">0.99</a>
</td>
</tr>
<tr class="even">
<th class="bg" style="text-align: center;" scope="row">x86-64</th>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.99/factor-windows-x86-64-0.99.zip">0.99</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.99/factor-macosx-x86-64-0.99.dmg">0.99</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.99/factor-linux-x86-64-0.99.tar.gz">0.99</a>
</td>
</tr>
</tbody>
</table>
<p><strong>Source code</strong>: <a
href="https://downloads.factorcode.org/releases/0.99/factor-src-0.99.zip"
class="release">0.99</a></p>
<p>This release is brought to you with over 4,100 commits by the following
individuals:</p>
<blockquote>
<p>Abtin Molavi, Ales Huzik, Alex <code>null</code> Maestas, Alexander Ilin, Alexandre
Rousseau, Aleksander Sabak, Arnaut Daniel, Ashish Kurmi, Benjamin Pollack,
Cat Stevens, Cecilia Knäbchen, Chris Double, Craig Allen, Dave Carlton, David
Flores, David Mindlin, Doug Coleman, Dusk Banks, Fred Alger, Giftpflanze,
Ikko Ashimine, Jack Lucas, John Benediktsson, Jon Harper, Justin Hill,
KUSUMOTO Norio, Keldan Chapman, Kevin Cope, Konrad Hinsen, Kye Shi, Mark
Sweeney, Mohamed Akram, Nandeeka Nayak, Niklas Larsson, Raghu Ranganathan,
Rudi Grinberg, Samuel Tardieu, Sebastian Strobl, Sergii Fesenko, Silvio
Mayolo, Steve Ayerhart, Zoltán Kéri,
<a href="https://github.com/Capital-Ex">@Capital-EX</a>,
<a href="https://github.com/inivekin">@inivekin</a>,
<a href="https://github.com/mariari">@mariari</a>,
<a href="https://github.com/nicolas-p">@nicolas-p</a>,
<a href="https://github.com/nomennescio">@nomennescio</a>,
<a href="https://github.com/timor">@timor</a></p>
</blockquote>
<p>Besides some bug fixes and library improvements, I want to highlight the
following changes:</p>
<ul>
<li>Added a <a href="https://docs.factorcode.org/content/article-tour.html">Guided Tour of Factor</a></li>
<li>Upgraded to Unicode 15</li>
<li>The <code>fixups</code> vocabulary makes upgrading easier when words are renamed</li>
<li>Windows binaries now include OpenSSL 3.1.2 and SQLite 3.42.0 for convenience</li>
<li>Re-added some support for FreeBSD</li>
<li>Improved non-English text entry on macOS</li>
<li>Removed support for 32-bit macOS</li>
<li>File editors are now specified using <code>EDITOR:</code> syntax</li>
<li>Switched to newer <code>ucrtbase.dll</code> on Windows</li>
<li>Support disassembly using <a href="http://www.capstone-engine.org">Capstone</a> in addition to <a href="https://github.com/vmt/udis86">Udis86</a></li>
<li>String literals must be separated by whitespace – <code>"hello"length</code> and <code>"foo""bar"append</code> are no longer accepted by the parser</li>
<li>The <code>fry</code> and <code>locals</code> syntax words are now in <code>syntax</code> for use in all vocabularies</li>
<li>Any word can be referred to by it’s fully-qualified name (e.g., <code>math:+</code> or <code>xml.writer:pprint-xml</code>)</li>
<li>The Emacs “FUEL” and VIM plugins have been updated</li>
</ul>
<p>Some possible backwards compatibility issues:</p>
<ul>
<li>Moved <code>colors.constants</code> and <code>colors.hex</code> to <code>colors</code> vocabulary</li>
<li>Merged <code>io.binary.fast</code> into <code>io.binary</code></li>
<li>Merged <code>io.directories.{hierarchy,search}</code> into <code>io.directories</code></li>
<li>Merged <code>io.encodings.utf16n</code> into <code>io.encodings.utf16</code></li>
<li>Renamed <code>math.ranges</code> to <code>ranges</code></li>
<li>Renamed <code>ranges</code> words from <code>[a,b]</code> to <code>[a..b]</code></li>
<li>Changed <code>FUNCTION:</code> syntax to not require a semi-colon at the end</li>
<li>Renamed <code>exists?</code> to <code>file-exists?</code></li>
<li>Renamed vector dot product from <code>v.</code> to <code>vdot</code></li>
<li>Renamed <code>short</code> to <code>index-or-length</code></li>
<li>Renamed various sorting words to be more simple</li>
<li>Improved icons and other UI images on retina displays</li>
<li>URL query strings only split on ampersand (<code>?a=b&c=d</code>) not semi-colon (<code>?a=b;c=d</code>)</li>
<li>Renamed some words in <code>interval-sets</code> to prefix <code>interval-</code>…</li>
<li>Renamed <code>contents</code> to <code>read-contents</code></li>
<li>Renamed <code>lines</code> to <code>read-lines</code></li>
<li>Renamed <code>selections</code> to <code>all-selections</code></li>
<li>Renamed <code>intersection</code> to <code>intersect-all</code></li>
<li>Merged <code>json.reader</code> and <code>json.writer</code> into <code>json</code> vocabulary</li>
<li>Merged <code>bson.reader</code> and <code>bson.writer</code> into <code>bson</code> vocabulary</li>
<li>Moved <code>talks</code> to separate <a href="https://github.com/factor/factor-talks">factor-talks</a> repository</li>
<li>Renamed <code>ui.backend.gtk</code> to <code>ui.backend.gtk2</code> to prepare for newer GTK support</li>
</ul>
<h3 id="what-is-factor">What is Factor</h3>
<p>Factor is a <a href="https://www.concatenative.org/">concatenative</a>, stack-based
programming language with <a href="https://concatenative.org/wiki/view/Factor/Features/The%20language">high-level
features</a>
including dynamic types, extensible syntax, macros, and garbage
collection. On a practical side, Factor has a <a href="https://docs.factorcode.org/content/article-vocab-index.html">full-featured
library</a>,
supports many different platforms, and has been extensively documented.</p>
<p>The implementation is <a href="https://concatenative.org/wiki/view/Factor/Optimizing%20compiler">fully
compiled</a>
for performance, while still supporting <a href="https://concatenative.org/wiki/view/Factor/Interactive%20development">interactive
development</a>.
Factor applications are portable between all common platforms. Factor
can <a href="https://concatenative.org/wiki/view/Factor/Deployment">deploy stand-alone
applications</a> on
all platforms. Full source code for the Factor project is available
under a BSD license.</p>
<h3 id="new-libraries">New Libraries:</h3>
<ul>
<li><a href="https://docs.factorcode.org/content/vocab-L-system.html">L-system</a>: brought back from <a href="https://github.com/factor/factor-unmaintained">unmaintained</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-aws.html">aws</a>: support for the Amazon Web Services API</li>
<li><a href="https://docs.factorcode.org/content/vocab-bare.html">bare</a>: support for <a href="https://baremessages.org">Binary Application Record Encoding</a> format</li>
<li><a href="https://docs.factorcode.org/content/vocab-base16.html">base16</a>: implements “Base 16 encoding” from <a href="https://www.rfc-editor.org/rfc/rfc4648">RFC 4648</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-base24.html">base24</a>: implements <a href="https://www.kuon.ch/post/2020-02-27-base24/">Base 24 encoding</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-base32.html">base32</a>: implements “Base 32 encoding” from <a href="https://www.rfc-editor.org/rfc/rfc4648">RFC 4648</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-base32-crockford.html">base32-crockford</a>: implements <a href="https://www.crockford.com/base32.html">Douglas’ Crockford’s Base 32 encoding</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-base32hex.html">base32hex</a>: implements “Base 32 Encoding with Extended Hex Alphabet” from <a href="https://www.rfc-editor.org/rfc/rfc4648">RFC 4648</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-base36.html">base36</a>: implements <a href="https://en.wikipedia.org/wiki/Base36">Base 36 Encoding</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-base58.html">base58</a>: implements <a href="https://en.bitcoinwiki.org/wiki/Base58">Base 58 encoding</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-base62.html">base62</a>: implements <a href="https://en.wikipedia.org/wiki/Base62">Base 62 encoding</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-base91.html">base91</a>: implements <a href="https://base91.sourceforge.net">Base 91 encoding</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-bech32.html">bech32</a>: implements <a href="https://en.bitcoin.it/wiki/Bech32">Bech32 encoding</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-binhex.html">binhex</a>: support <a href="https://en.wikipedia.org/wiki/BinHex">BinHex</a> encoding scheme used on classic Mac OS</li>
<li><a href="https://docs.factorcode.org/content/vocab-bittorrent.html">bittorrent</a>: beginning to support the <a href="https://en.wikipedia.org/wiki/BitTorrent">BitTorrent protocol</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-brain-flak.html">brain-flak</a>: implementation of <a href="https://esolangs.org/wiki/Brain-Flak">brain-flak</a> esoteric language</li>
<li><a href="https://docs.factorcode.org/content/vocab-broadcast-server.html">broadcast-server</a>: network discovery udp broadcast client/server</li>
<li><a href="https://docs.factorcode.org/content/vocab-build-from-source.html">build-from-source</a>: allow building dependent libraries from their source</li>
<li><a href="https://docs.factorcode.org/content/vocab-calendar.ranges.html">calendar.ranges</a>: support for <a href="https://re.factorcode.org/2023/05/calendar-ranges.html">calendar ranges</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-cbor.html">cbor</a>: implement <a href="https://cbor.io">Concise Binary Object Representation</a> format</li>
<li><a href="https://docs.factorcode.org/content/vocab-certs.html">certs</a>: for working with <a href="https://www.openssl.org">OpenSSL</a> certificates</li>
<li><a href="https://docs.factorcode.org/content/vocab-chrome-tools.html">chrome-tools</a>: for copying curl/fetch commands from <a href="https://developer.chrome.com/docs/devtools/">Chrome DevTools</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-checksums.wyhash.html">checksums.wyhash</a>: implement <a href="https://github.com/wangyi-fudan/wyhash">wyhash</a> hash function</li>
<li><a href="https://docs.factorcode.org/content/vocab-cocoa.statusbar.html">cocoa.statusbar</a>: support for system-wide menu bars on macOS</li>
<li><a href="https://docs.factorcode.org/content/vocab-codebase-analyzer.html">codebase-analyzer</a>: tools for generating codebase statistics</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.contrast.html">colors.contrast</a>: implement <a href="https://www.w3.org/TR/WCAG21/#contrast-minimum">WCAG color contrast criteria</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.hwb.html">colors.hwb</a>: support the <a href="https://en.wikipedia.org/wiki/HWB_color_model">HWB color model</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-color-picker-game.html">color-picker-game</a>: demo game where the user tries to match a color with sliders</li>
<li><a href="https://docs.factorcode.org/content/vocab-command-loop.html">command-loop</a>: generic line-oriented command interpreters</li>
<li><a href="https://docs.factorcode.org/content/vocab-compression.bzip3.html">compression.bzip3</a>: support <a href="https://github.com/kspalaiologos/bzip3">BZip3</a>, the “<em>better and stronger spiritual successor to BZip2</em>”</li>
<li><a href="https://docs.factorcode.org/content/vocab-compression.gzip.html">compression.gzip</a>: implementation of <a href="https://en.wikipedia.org/wiki/Gzip">gzip</a> algorithms</li>
<li><a href="https://docs.factorcode.org/content/vocab-compression.zstd.html">compression.zstd</a>: support <a href="https://www.zstd.net">Zstandard</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-countries.html">countries</a>: various country codes and abbrevations</li>
<li><a href="https://docs.factorcode.org/content/vocab-cpu.arm.html">cpu.arm</a>: assembler backend for ARM 32-bit and 64-bit</li>
<li><a href="https://docs.factorcode.org/content/vocab-crypto.jwt.html">crypto.jwt</a>: encode/decode of <a href="https://jwt.io">JSON Web Tokens</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-csexp.html">csexp</a>: reading/writing from <a href="https://en.wikipedia.org/wiki/Canonical_S-expressions">Canonical S-expressions</a>s</li>
<li><a href="https://docs.factorcode.org/content/vocab-db.mysql.html">db.mysql</a>: prototype of a <a href="https://www.mysql.com">MySQL</a> database backend</li>
<li><a href="https://docs.factorcode.org/content/vocab-did-you-mean.html">did-you-mean</a>: prototype of a “<em>did you mean?</em>” restarts when tokens aren’t found</li>
<li><a href="https://docs.factorcode.org/content/vocab-discord.html">discord</a>: implement the <a href="https://discord.com/developers/docs/intro">Discord API</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-discord.chatgpt-bot.html">discord.chatgpt-bot</a>: a <a href="https://chat.openai.com">ChatGPT</a> bot for Discord</li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.10x.html">editors.10x</a>: support <a href="https://10xeditor.com">10x Editor</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.acme.html">editors.acme</a>: support <a href="https://acme.cat-v.org">Acme text editor</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.aquamacs.html">editors.aquamacs</a>: support <a href="https://aquamacs.org">Aquamacs</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.bluefish.html">editors.bluefish</a>: support <a href="https://bluefish.openoffice.nl/index.html">Bluefish editor</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.cudatext.html">editors.cudatext</a>: support <a href="https://cudatext.github.io">CudaText code editor</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.espresso.html">editors.espresso</a>: support <a href="https://espressoapp.com">Espresso web editor</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.kakoune.html">editors.kakoune</a>: support <a href="https://kakoune.org">Kakoune code editor</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.kate.html">editors.kate</a>: support <a href="https://kate-editor.org">Kate text editor</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.lapce.html">editors.lapce</a>: support <a href="https://lapce.dev">Lapce text editor</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.lite-xl.html">editors.lite-xl</a>: support <a href="https://lite-xl.com">Lite XL editor</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.nova.html">editors.nova</a>: support <a href="https://nova.app">Nova code editor</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.pulsar.html">editors.pulsar</a>: support <a href="https://pulsar-edit.dev">Pulsar text editor</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.smultron.html">editors.smultron</a>: support <a href="https://www.peterborgapps.com/smultron/">Smultron text editor</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.subethaedit.html">editors.subethaedit</a>: support <a href="https://subethaedit.net">SubEthaEdit</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.visual-studio-code-exploration.html">editors.visual-studio-code-exploration</a>: support Visual Studio Code Exploration builds</li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.visual-studio-code-insiders.html">editors.visual-studio-code-insiders</a>: support <a href="https://code.visualstudio.com/insiders/">Visual Studio Code Insiders</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.visual-studio-codium.html">editors.visual-studio-codium</a>: support <a href="https://vscodium.com">VSCodium</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.zed.html">editors.zed</a>: support <a href="https://zed.dev">Zed text editor</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-elevate.html">elevate</a>: cross-platform privilege escalation</li>
<li><a href="https://docs.factorcode.org/content/vocab-escape-strings.ui.html">escape-strings.ui</a>: demo user interface for <a href="https://docs.factorcode.org/content/vocab-escape-strings.html">escape-strings</a> vocabulary</li>
<li><a href="https://docs.factorcode.org/content/vocab-fixups.html">fixups</a>: help provide restarts for word renamings</li>
<li><a href="https://docs.factorcode.org/content/vocab-format-using.html">format-using</a>: experimental tool for formatting <code>USING:</code> blocks differently</li>
<li><a href="https://docs.factorcode.org/content/vocab-gamelib.html">gamelib</a>: generic game library with some fluent demos including <a href="https://docs.factorcode.org/content/vocab-gamelib.demos.sokoban.html">sokoban</a> and <a href="https://docs.factorcode.org/content/vocab-gamelib.demos.tic-tac-toe.html">tic-tac-toe</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-gemini.html">gemini</a>: support for <a href="https://gemini.circumlunar.space/">Project Gemini</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-gemini.cli.html">gemini.cli</a>: Project Gemini command-line interface</li>
<li><a href="https://docs.factorcode.org/content/vocab-gemini.server.html">gemini.server</a>: Project Gemini file server</li>
<li><a href="https://docs.factorcode.org/content/vocab-gemini.ui.html">gemini.ui</a>: Project Gemini user interface</li>
<li><a href="https://docs.factorcode.org/content/vocab-generators.html">generators</a>: prototype of <a href="https://en.wikipedia.org/wiki/Generator_%28computer_programming%29">generator routines</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-geohash.html">geohash</a>: implement <a href="https://en.wikipedia.org/wiki/Geohash">Geohash</a> geocode system</li>
<li><a href="https://docs.factorcode.org/content/vocab-gir.html">gir</a>: parser for <a href="https://github.com/gircore/gir-files">GIR files</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-github.html">github</a>: wrapper for the <a href="https://docs.github.com/en/rest">GitHub API</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-glfw.html">glfw</a>: wrapper for <a href="https://www.glfw.org">GLFW</a> library</li>
<li><a href="https://docs.factorcode.org/content/vocab-gravatar.html">gravatar</a>: support <a href="https://en.gravatar.com/site/implement">Gravatar API</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-help.tour.html">help.tour</a>: added a <a href="https://re.factorcode.org/2023/01/guided-tour-of-factor.html">Guided Tour of Factor</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-hetzner.html">hetzner</a>: wrapper for the <a href="https://www.hetzner.com">Hetzner Cloud API</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-hipku.html">hipku</a>: implement the <a href="https://re.factorcode.org/2023/02/hipku.html">Hipku algorithm</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-html5.html">html5</a>: beginning of <a href="https://www.w3.org/TR/2011/WD-html5-20110405/">HTML5</a> parser</li>
<li><a href="https://docs.factorcode.org/content/vocab-http.websockets.html">http.websockets</a>: support for <a href="https://en.wikipedia.org/wiki/WebSocket">HTTP WebSockets</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-http2.html">http2</a>: beginning of <a href="https://datatracker.ietf.org/doc/html/rfc7540">HTTP/2</a> implementation</li>
<li><a href="https://docs.factorcode.org/content/vocab-images.jpeg.html">images.jpeg</a>: brought back from <a href="https://github.com/factor/factor-unmaintained">unmaintained</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-images.processing.html">images.processing</a>: brought back from <a href="https://github.com/factor/factor-unmaintained">unmaintained</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.counting.html">io.streams.counting</a>: implementation of <a href="https://docs.factorcode.org/content/article-stream-protocol.html">stream protocol</a> that counts read/write of elements</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.escape-codes.html">io.streams.escape-codes</a>: support for <code>faint</code>, <code>underline</code>, and <code>blink</code> styles</li>
<li><a href="https://docs.factorcode.org/content/vocab-iso-codes.html">iso-codes</a>: support for getting Debian ISO files</li>
<li><a href="https://docs.factorcode.org/content/vocab-itunes.html">itunes</a>: implements <a href="https://www.apple.com/itunes/">iTunes</a> API</li>
<li><a href="https://docs.factorcode.org/content/vocab-json.http.html">json.http</a>: utilities for working with JSON web services</li>
<li><a href="https://docs.factorcode.org/content/vocab-libclang.html">libclang</a>: wrapper for <a href="https://clang.llvm.org/doxygen/group__CINDEX.html">libclang</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-linux.input-events.html">linux.input-events</a>: support for <code>/dev/input/event*</code> devices for reading mouse and keyboard events</li>
<li><a href="https://docs.factorcode.org/content/vocab-lint.vocabs.html">lint.vocabs</a>: a vocabulary lint tool for detecting unused imports, etc</li>
<li><a href="https://docs.factorcode.org/content/vocab-lists.circular.html">lists.circular</a>: wrapper to allow circular objects to be used in lists</li>
<li><a href="https://docs.factorcode.org/content/vocab-logic.html">logic</a>: implementation of <a href="https://en.wikipedia.org/wiki/Logic_programming">logic programming</a> techniques</li>
<li><a href="https://docs.factorcode.org/content/vocab-long-urls.html">long-urls</a>: expand <a href="https://en.wikipedia.org/wiki/URL_shortening">short URLs</a> to their long versions</li>
<li><a href="https://docs.factorcode.org/content/vocab-markov-chains.html">markov-chains</a>: support for <a href="https://en.wikipedia.org/wiki/Markov_chain">Markov chains</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.matrices.extras.html">math.matrices.extras</a>:</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.primes.brute-force.html">math.primes.brute-force</a>: original “brute force” primality factoring</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.primes.pollard-rho-brent.html">math.primes.pollard-rho-brent</a>: faster Pollard-Rho-Brent primality factoring</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.runge-kutta.html">math.runge-kutta</a>: support for <a href="https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods">Runge-Kutta methods</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-mediawiki.api.html">mediawiki.api</a>: support for the MediaWiki API</li>
<li><a href="https://docs.factorcode.org/content/vocab-modern.html.html">modern.html</a>: prototype html parser</li>
<li><a href="https://docs.factorcode.org/content/vocab-multisets.html">multisets</a>: beginning to support multisets</li>
<li><a href="https://docs.factorcode.org/content/vocab-notifications.html">notifications</a>: support cross-platform notification APIs</li>
<li><a href="https://docs.factorcode.org/content/vocab-npm.html">npm</a>: support for working with the <a href="https://www.npmjs.com">npm package manager</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-openai.html">openai</a>: implement the <a href="https://platform.openai.com/docs/introduction">OpenAI API</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-papier.html">papier</a>: demo sprite-based game</li>
<li><a href="https://docs.factorcode.org/content/vocab-periodic-table.html">periodic-table</a>: demo periodic table UI gadget</li>
<li><a href="https://docs.factorcode.org/content/vocab-pcre2.html">pcre2</a>: bindings to libpcre2</li>
<li><a href="https://docs.factorcode.org/content/vocab-proquint.html">proquint</a>: implements <a href="https://arxiv.org/html/0901.4016">Proquint encoding</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-process-autopsy.html">process-autopsy</a>: rename from <code>ci.run-process</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-punycode.html">punycode</a>: support for international domain names</li>
<li><a href="https://docs.factorcode.org/content/vocab-quiz.html">quiz</a>: flashcard-style quiz game</li>
<li><a href="https://docs.factorcode.org/content/vocab-random.passwords.html">random.passwords</a>: random password generator</li>
<li><a href="https://docs.factorcode.org/content/vocab-random.pcg.html">random.pcg</a>: support for <a href="https://www.pcg-random.org/index.html">PCG random number generators</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-random.xoshiro.html">random.xoshiro</a>: support for <a href="https://prng.di.unimi.it">xoroshiro random number generators</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-raygui.html">raygui</a>: support for <a href="https://github.com/raysan5/raygui">raygui</a> user interface library</li>
<li><a href="https://docs.factorcode.org/content/vocab-raylib.html">raylib</a>: support for <a href="https://raylib.com">raylib</a> game programming library</li>
<li><a href="https://docs.factorcode.org/content/vocab-reservoir-sampling.html">reservoir-sampling</a>: implements <a href="https://en.wikipedia.org/wiki/Reservoir_sampling">reservoir sampling algorithm</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-retries.html">retries</a>: vocab for trying code blocks multiple times</li>
<li><a href="https://docs.factorcode.org/content/vocab-rocksdb.html">rocksdb</a>: support for <a href="https://rocksdb.org">RocksDB</a> key-value store</li>
<li><a href="https://docs.factorcode.org/content/vocab-rosetta-code.multisplit.html">rosetta-code.multisplit</a>: solution for <a href="https://rosettacode.org/wiki/Multisplit">Multisplit</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-ryu.html">ryu</a>: implementation of <a href="https://dl.acm.org/citation.cfm?id=3192369">Ryū: fast float-to-string conversion</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-semver.html">semver</a>: support for <a href="https://semver.org">Semantic Versioning</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.padded.html">sequences.padded</a>: virtual “padded” sequences</li>
<li><a href="https://docs.factorcode.org/content/vocab-solr.html">solr</a>: beginnings of <a href="https://solr.apache.org">Apache Solr</a> support</li>
<li><a href="https://docs.factorcode.org/content/vocab-sorting.specification.html">sorting.specification</a>: rename from <code>sorting.slots</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-stack-as-data.html">stack-as-data</a>: combinators for manipulating the data stack</li>
<li><a href="https://docs.factorcode.org/content/vocab-string-server.html">string-server</a>: server for stress testing large network payloads</li>
<li><a href="https://docs.factorcode.org/content/vocab-syslog.html">syslog</a>: client for the <a href="https://www.rfc-editor.org/rfc/rfc5424">Syslog Protocol</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-syntax.terse.html">syntax.terse</a>: prototype some terse syntax words</li>
<li><a href="https://docs.factorcode.org/content/vocab-tensors.html">tensors</a>: prototype of typed-array programming library</li>
<li><a href="https://docs.factorcode.org/content/vocab-tftp.html">tftp</a>: support for the <a href="https://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol">Trivial File Transfer Protocol</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-tinyvg.html">tinyvg</a>: support for the <a href="https://tinyvg.tech">TinyVG</a> binary encoded vector graphics format</li>
<li><a href="https://docs.factorcode.org/content/vocab-tldr.html">tldr</a>: implementation of the <a href="https://tldr.sh">tldr pages</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-tokencase.html">tokencase</a>: convert between various string tokenization methods</li>
<li><a href="https://docs.factorcode.org/content/vocab-toml.html">toml</a>: readers and writers for <a href="https://toml.io/en/">Tom’s Obvious Markup Language</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.disassembler.capstone.html">tools.disassembler.capstone</a>: backend for <a href="https://www.capstone-engine.org">Capstone</a> disassembler</li>
<li><a href="https://docs.factorcode.org/content/vocab-totp.html">totp</a>: support <a href="https://en.wikipedia.org/wiki/Time-based_one-time_password">Time-based one-time passwords</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.backend.cocoa.input-methods.html">ui.backend.cocoa.input-methods</a>: support for typing letters in non-English languages</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.gadgets.flex-borders.html">ui.gadgets.flex-borders</a>: a border gadget that doesn’t dictate the size of the contained gadget</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.theme.base16.html">ui.theme.base16</a>: support all <a href="https://github.com/chriskempson/base16">base16 themes</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.theme.wombat.html">ui.theme.wombat</a>: support the “wombat” theme</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.button-list.html">ui.tools.button-list</a>: a gadget with a list of active buttons</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.listener.log.html">ui.tools.listener.log</a>: helper words to log to a <a href="https://docs.factorcode.org/content/article-ui-listener.html">UI listener</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.windows.drop-target.html">ui.windows.drop-target</a>: support dropping files on Windows</li>
<li><a href="https://docs.factorcode.org/content/vocab-ulid.html">ulid</a>: <a href="https://github.com/ulid/spec">Universally Unique Lexicographically Sortable Identifier</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-unicode.control-pictures.flags.html">unicode.control-pictures</a>: convert ASCII control characters to their Unicode picture equivalents</li>
<li><a href="https://docs.factorcode.org/content/vocab-unicode.flags.html">unicode.flags</a>: conversion to-and-from unicode flag emojis</li>
<li><a href="https://docs.factorcode.org/content/vocab-unix.scheduler.html">unix.scheduler</a>: some words from <code>sched.h</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-unix.sysctl.html">unix.sysctl</a>: support for <code>sysctl</code> primitives</li>
<li><a href="https://docs.factorcode.org/content/vocab-unix.xattrs.html">unix.xattrs</a>: support for <a href="https://en.wikipedia.org/wiki/Extended_file_attributes">extended file attributes</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-verbal-expressions.html">verbal-expressions</a>: more readable regular expressions</li>
<li><a href="https://docs.factorcode.org/content/vocab-vin.html">vin</a>: parsing <a href="https://en.wikipedia.org/wiki/Vehicle_identification_number">VIN numbers</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-visionect.html">visionect</a>: support for <a href="https://visionect.com">Visionect</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-vulkan.html">vulkan</a>: support for <a href="https://www.vulkan.org">Vulkan</a> library</li>
<li><a href="https://docs.factorcode.org/content/vocab-wasm.html">wasm</a>: beginning to support WASM opcodes</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.drive-strings.html">windows.drive-strings</a>: support for <code>GetLogicalDriveStrings</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.hardware.html">windows.hardware</a>: support for enumerating display monitors</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.powrprof.html">windows.powrprof</a>: support for power management policies</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.processes.html">windows.processes</a>: support for snapshot APIs</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.shcore.html">windows.shcore</a>: support for per-monitor DPI scaling</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.version.html">windows.version</a>: support for file version info</li>
<li><a href="https://docs.factorcode.org/content/vocab-wipe.html">wipe</a>: utility to overwrite a file with random data</li>
<li><a href="https://docs.factorcode.org/content/vocab-wipe.ui.html">wipe.ui</a>: windows user interface for wipe</li>
<li><a href="https://docs.factorcode.org/content/vocab-wordlet.html">wordlet</a>: implements a game similar to <a href="https://www.nytimes.com/games/wordle/index.html">wordle</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-yenc.html">yenc</a>: support <a href="https://en.wikipedia.org/wiki/YEnc">yEnc</a> binary-to-text encoding format</li>
<li><a href="https://docs.factorcode.org/content/vocab-zealot.help-lint.html">zealot.help-lint</a>: adding help-lint to the zealot nightly builder</li>
<li><a href="https://docs.factorcode.org/content/vocab-zim.html">zim</a>: support for <a href="https://re.factorcode.org/2023/05/offline-wikipedia.html">zim files</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-zim.builder.html">zim.builder</a>: support to <a href="https://re.factorcode.org/2023/05/zim-builder.html">build zim files</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-zim.server.html">zim.server</a>: serve existing zim files from a web server</li>
<li><a href="https://docs.factorcode.org/content/vocab-zim.tools.html">zim.tools</a>: command-line tools for working with zim files</li>
<li><a href="https://docs.factorcode.org/content/vocab-zoneinfo.update.html">zoneinfo.update</a>: make updating the zoneinfo files easier</li>
</ul>
<h3 id="improved-libraries">Improved Libraries:</h3>
<ul>
<li><a href="https://docs.factorcode.org/content/vocab-alien.c-types.html">alien.c-types</a>: defined <code>u8</code>, <code>u16</code>, <code>u32</code>, <code>u64</code>, <code>s8</code>, <code>s16</code>, <code>s32</code>, <code>s64</code>, <code>f32</code>, <code>f64</code>, <code>isize</code>, <code>usize</code> types</li>
<li><a href="https://docs.factorcode.org/content/vocab-alien.libraries.finder.linux.html">alien.libraries.finder.linux</a>: more robust implementation using <code>ld</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-alien.libraries.finder.windows.html">alien.libraries.finder.windows</a>: more robust implementation using <code>GetModuleFileName</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-assocs.html">assocs</a>: adding <code>zip-with</code>, <code>collect-by</code>, <code>?value-at</code>, <code>?change-at</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-assocs.extras.html">assocs.extras</a>: more words and tests</li>
<li><a href="https://docs.factorcode.org/content/vocab-base64.html">base64</a>: performance improvements and added urlsafe versions</li>
<li><a href="https://docs.factorcode.org/content/vocab-base85.html">base85</a>: performance improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-boolean-expr.html">boolean-expr</a>: add some more expression simplification rules</li>
<li><a href="https://docs.factorcode.org/content/vocab-bootstrap.image.html">bootstrap.image</a>: add a <code>MAIN:</code> for <code>-run=bootstrap.image [platform...]</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-bootstrap.image.upload.html">bootstrap.image.upload</a>: more robust, use IPV4, use scp.exe on windows</li>
<li><a href="https://docs.factorcode.org/content/vocab-bson.html">bson</a>: unify reader/writer vocabulary</li>
<li><a href="https://docs.factorcode.org/content/vocab-cache.html">cache</a>: possibly workaround a UI issue using <code>assoc-filter</code> instead of <code>assoc-filter!</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-calendar.html">calendar</a>: improve docs, add words, improve consistency in cloning/modifying timestamps</li>
<li><a href="https://docs.factorcode.org/content/vocab-calendar.holidays.us.html">calendar.holidays.us</a>: support more holidays</li>
<li><a href="https://docs.factorcode.org/content/vocab-classes.struct.html">classes.struct</a>: improved <code>new</code> and <code>boa</code> on struct classes</li>
<li><a href="https://docs.factorcode.org/content/vocab-classes.union.html">classes.union</a>: faster predicate checks</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.html">colors</a>: merged in colors.constant and colors.hex</li>
<li><a href="https://docs.factorcode.org/content/vocabs-color-picker.html">color-picker</a>: support picking more colors</li>
<li><a href="https://docs.factorcode.org/content/vocab-combinators.extras.html">combinators.extras</a>: more words and tests</li>
<li><a href="https://docs.factorcode.org/content/vocab-combinators.smart.html">combinators.smart</a>: adding <code>smart-loop</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-command-line.html">command-line</a>: run scripts and eval with auto-use on, allow non-empty data stack</li>
<li><a href="https://docs.factorcode.org/content/vocab-compiler.cfg.builder.alien.boxing.html">compiler.cfg.builder.alien.boxing</a>: improved System V AMD64 API compliance</li>
<li><a href="https://docs.factorcode.org/content/vocab-compression.huffman.html">compression.huffman</a>: support for <a href="https://en.wikipedia.org/wiki/Huffman_coding">Huffman codes</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-compression.inflate.html">compression.inflate</a>: adding gzip-inflate</li>
<li><a href="https://docs.factorcode.org/content/vocab-contributors.html">contributors</a>: additional Factor developers</li>
<li><a href="https://docs.factorcode.org/content/vocab-core-foundation.fsevents.html">core-foundation.fsevents</a>: adding <code>kFSEventStream</code> flags</li>
<li><a href="https://docs.factorcode.org/content/vocab-core-text.html">core-text</a>: improve retina text layout</li>
<li><a href="https://docs.factorcode.org/content/vocab-core-text.fonts.html">core-text.fonts</a>: switch default monospace font from “Monaco” to “Menlo”</li>
<li><a href="https://docs.factorcode.org/content/vocab-crontab.html">crontab</a>: fix some leap day issues and non-standard day-of-week fractions</li>
<li><a href="https://docs.factorcode.org/content/vocab-db.postgresql.html">db.postgresql</a>: improve unit testing when sharing a single database instance</li>
<li><a href="https://docs.factorcode.org/content/vocab-db.tuples.html">db.tuples</a>: adding <code>reject-tuples</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-debugger.html">debugger</a>: integrate fixups</li>
<li><a href="https://docs.factorcode.org/content/vocab-decimals.html">decimals</a>: better <code>DECIMAL:</code> prettyprinting</li>
<li><a href="https://docs.factorcode.org/content/vocab-delegate.html">delegate</a>: learned how to consult on <code>HOOK:</code> generics</li>
<li><a href="https://docs.factorcode.org/content/vocab-documents.elements.html">documents.elements</a>: adding paragraph-elt for paragraph level cursor movement</li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.html">editors</a>: allow loading all editors after selecting the one you want to use, add <code>EDITOR:</code> syntax</li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.ultraedit.html">editors.ultraedit</a>: support macOS version</li>
<li><a href="https://docs.factorcode.org/content/vocab-endian.html">endian</a>: merge io.binary.fast versions</li>
<li><a href="https://docs.factorcode.org/content/vocab-escape-strings.html">escape-strings</a>: support a lot of wild string escaping techniques</li>
<li><a href="https://docs.factorcode.org/content/vocab-eval.html">eval</a>: adding <code>eval-with-stack</code> words</li>
<li><a href="https://docs.factorcode.org/content/vocab-farkup.html">farkup</a>: only <code>nofollow</code> absolute urls</li>
<li><a href="https://docs.factorcode.org/content/vocab-functors.html">functors</a>: fix to use <code>with-words</code> for more robust parsing</li>
<li><a href="https://docs.factorcode.org/content/vocab-furnace.actions.html">furnace.actions</a>: support <code>PUT</code> and <code>PATCH</code> methods</li>
<li><a href="https://docs.factorcode.org/content/vocab-furnace.recaptcha.html">furnace.recaptcha</a>: update to reCAPTCHA2</li>
<li><a href="https://docs.factorcode.org/content/vocab-game.input.gtk.html">game.input.gtk</a>: support mouse and keyboard events on linux</li>
<li><a href="https://docs.factorcode.org/content/vocab-gdbm.html">gdbm</a>: prefix words with <code>gdbm-</code>…</li>
<li><a href="https://docs.factorcode.org/content/vocab-git.html">git</a>: more features for working with git and GitHub repositories</li>
<li><a href="https://docs.factorcode.org/content/vocab-gopher.server.html">gopher.server</a>: adding <code>MAIN:</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-gopher.ui.html">gopher.ui</a>: adding <code>MAIN:</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-grouping.extras.html">grouping.extras</a>: more words and tests</li>
<li><a href="https://docs.factorcode.org/content/vocab-hacker-news.html">hacker-news</a>: more pages and better color support</li>
<li><a href="https://docs.factorcode.org/content/vocab-hashcash.html">hashcash</a>: improved <a href="https://en.wikipedia.org/wiki/Hashcash">Hashcash</a> cryptographic proof-of-work system</li>
<li><a href="https://docs.factorcode.org/content/vocab-heaps.html">heaps</a>: fix for <code>heap-delete</code> bug</li>
<li><a href="https://docs.factorcode.org/content/vocab-help.html.html">help.html</a>: support dark mode, some aesthetic improvements, qualified searching</li>
<li><a href="https://docs.factorcode.org/content/vocab-help.syntax.html">help.syntax</a>: adding <a href="https://re.factorcode.org/2023/06/easy-help.html">easy help syntax</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-html.templates.chloe.html">html.templates.chloe</a>: adding <code><script></code> and <code><meta></code> tag templates</li>
<li><a href="https://docs.factorcode.org/content/vocab-http.client.html">http.client</a>: support <code>PATCH</code> requests</li>
<li><a href="https://docs.factorcode.org/content/vocab-http.parsers.html">http.parsers</a>: support more characters in cookie keys</li>
<li><a href="https://docs.factorcode.org/content/vocab-images.loader.cocoa.html">images.loader.cocoa</a>: support more image types on macOS 11+</li>
<li><a href="https://docs.factorcode.org/content/vocab-images.loader.gdiplus.html">images.loader.gdiplus</a>: support RGBA pixel format</li>
<li><a href="https://docs.factorcode.org/content/vocab-init.html">init</a>: add <code>STARTUP-HOOK:</code> syntax</li>
<li><a href="https://docs.factorcode.org/content/vocab-inverse.html">inverse</a>: fix swapped inverse of <code>+</code> and <code>/</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-io.crlf.html">io.crlf</a>: adding <code>stream-read-crlf</code>, some <code>ignoring-crlf</code> words, and a <code>crlf-stream</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-io.directories.html">io.directories</a>: merge in words from io.directories.search and io.directories.hierarchy</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.files.info.html">io.files.info</a>: support mount points</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.monitors.macosx.html">io.monitors.macosx</a>: support add/remove/rename file system events</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.pathnames.html">io.pathnames</a>: adding <code>canonicalize-path</code>, <code>canonicalize-path-full</code>, <code>>windows-path</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-io.sockets.secure.openssl.html">io.sockets.secure.openssl</a>:</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.ansi.html">io.streams.ansi</a>: faster by caching styles, support blink</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.byte-array.fast.html">io.streams.byte-array.fast</a>: faster when writing a <code>byte-array</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.256color.html">io.streams.256color</a>: faster by caching styles, support blink</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.styles.html">io.styles</a>: fix nested streams</li>
<li><a href="https://docs.factorcode.org/content/vocab-ip-parser.html">ip-parser</a>: adding <code>ipv6-ntoa</code> and <code>ipv6-aton</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-json.html">json</a>: unify reader/writer vocabulary</li>
<li><a href="https://docs.factorcode.org/content/vocab-listener.html">listener</a>: adding <code>-q</code> quiet startup</li>
<li><a href="https://docs.factorcode.org/content/vocab-lists.html">lists</a>: adding list literals <code>L{</code>, <code>2leach</code>, <code>lreduce</code>, <code>2lreduce</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-lru-cache.html">lru-cache</a>: adding <code>fifo-cache</code>, <code>lifo-cache</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-machine-learning.data-sets.html">machine-learning.data-sets</a>: adding mnist data</li>
<li><a href="https://docs.factorcode.org/content/vocab-mason.html">mason</a>: improvements for nightly builders, code signing, notarization</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.html">math</a>: adding <code>until-zero</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.bitwise.html">math.bitwise</a>: adding <code>d>w/w</code>, <code>w>h/h</code>, <code>h>b/b</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.combinatorics.html">math.combinatorics</a>: adding <code>unique-permutations</code>, <code>combination-with-replacement</code>, <code>selection</code> combinators</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.extras.html">math.extras</a>: more words and docs</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.floats.half.html">math.floats.half</a>: improve roundtrip of subnormal float16</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.functions.html">math.functions</a>: adding <code>e^-1</code>, <code>logit</code>, <code>lgamma</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.intervals.html">math.intervals</a>: cleanup and bug fixes</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.matrices.html">math.matrices</a>: cleanup and interface improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.parser.html">math.parser</a>: support underscore number separators, e.g. <code>1_000_000</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.primes.factors.html">math.primes.factors</a>: switch to pollard-rho-brent-factors</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.statistics.html">math.statistics</a>: new words: <code>dcg</code>, <code>ndcg</code>, <code>quartiles</code>, <code>sum-of-squares</code>, <code>sum-of-cubes</code>, <code>sum-of-quads</code>, <code>spearman-corr</code>, <code>rank-by-avg</code>, <code>rank-by-min</code>, <code>rank-by-max</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.text.english.html">math.text.english</a>: support larger numbers, AP style</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.vectors.html">math.vectors</a>: adding <code>l1-norm</code>, <code>cross</code>, <code>normal</code>, <code>proj</code>, <code>perp</code>, <code>angle-between</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-maze.html">maze</a>: support mouse clicks</li>
<li><a href="https://docs.factorcode.org/content/vocab-metar.html">metar</a>: improvements to wind and visibility parsing, adding <code>MAIN:</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-mime.multipart.html">mime.multipart</a>: improve error when decoding runs out of bytes</li>
<li><a href="https://docs.factorcode.org/content/vocab-modern.html">modern</a>: improved experimental lexer</li>
<li><a href="https://docs.factorcode.org/content/vocab-multiline.html">multiline</a>: remove <code>HEREDOC:</code>, adding lua-style strings <code>[=[</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-numbers-game.html">numbers-game</a>: simplify for readability</li>
<li><a href="https://docs.factorcode.org/content/vocab-peg.html">peg</a>: improve compile performance</li>
<li><a href="https://docs.factorcode.org/content/vocab-peg.ebnf.html">peg.ebnf</a>: adding <code>EBNF-PARSER:</code>, <code>PARTIAL-EBNF:</code> syntax</li>
<li><a href="https://docs.factorcode.org/content/vocab-peg.javascript.html">peg.javascript</a>: support more javascript features</li>
<li><a href="https://docs.factorcode.org/content/vocab-peg.parsers.html">peg.parsers</a>: make <code>range-pattern</code> more efficient for single characters</li>
<li><a href="https://docs.factorcode.org/content/vocab-project-euler.html">project-euler</a>: solutions for problems 64, 87, 463, and 508</li>
<li><a href="https://docs.factorcode.org/content/vocab-prettyprint.backend.html">prettyprint.backend</a>: print some special floats differently, like -0.0, 0.0, 1/0., -1/0., etc.</li>
<li><a href="https://docs.factorcode.org/content/vocab-python.html">python</a>: update to python 3+</li>
<li><a href="https://docs.factorcode.org/content/vocab-random.html">random</a>: implement <code>binomial-random</code> distribution, faster <code>randoms</code> using <code>random*</code> generic</li>
<li><a href="https://docs.factorcode.org/content/vocab-random.mersenne-twister.html">random.mersenne-twister</a>: slightly faster <code>random-32*</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-readline-listener.html">readline-listener</a>: support pathname completion</li>
<li><a href="https://docs.factorcode.org/content/vocab-redis.html">redis</a>: support <code>SCRIPT</code> commands</li>
<li><a href="https://docs.factorcode.org/content/vocab-regexp.html">regexp</a>: fix case-insensitive lookahead and lookbehind</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.deep.html">sequences.deep</a>: adding <code>flatten1</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.extras.html">sequences.extras</a>: more words and tests</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.generalizations.html">sequences.generalizations</a>: adding <code>?firstn</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.product.html">sequences.product</a>: adding <code>product-find</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.repeating.html">sequences.repeating</a>: support repeating elements and repeating sequence</li>
<li><a href="https://docs.factorcode.org/content/vocab-shuffle.html">shuffle</a>: adding more shufflers like <code>2pick</code>, <code>5roll</code>, <code>6roll</code>, <code>7roll</code>, <code>8roll</code>, <code>2reach</code>, <code>nipdd</code>…</li>
<li><a href="https://docs.factorcode.org/content/vocab-smtp.html">smtp</a>: fix issues sending email to some SMTP servers requiring HELO after TLS auth</li>
<li><a href="https://docs.factorcode.org/content/vocab-sodium.html">sodium</a>: support more crypto techniques</li>
<li><a href="https://docs.factorcode.org/content/vocab-sorting.html">sorting</a>: renamed <code>natural-sort</code> to <code>sort</code>, <code>sort</code> to <code>sort-with</code>, <code>sort-with</code> to <code>sort-by</code>, etc.</li>
<li><a href="https://docs.factorcode.org/content/vocab-splitting.extras.html">splitting.extras</a>: adding <code>split-head</code>, <code>split-tail</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-splitting.monotonic.html">splitting.monotonic</a>: performance improvements to <code>monotonic-split</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-stack-checker.dependencies.html">stack-checker.dependencies</a>: fix depends on <code>struct-class</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-stack-checker.transforms.html">stack-checker.transforms</a>: implement <code>boa</code> on <code>struct-class</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-strings.parser.html">strings.parser</a>: support octal escapes</li>
<li><a href="https://docs.factorcode.org/content/vocab-strings.tables.html">strings.tables</a>: implement box format</li>
<li><a href="https://docs.factorcode.org/content/vocab-system-info.html">system-info</a>: adding <code>username</code> word</li>
<li><a href="https://docs.factorcode.org/content/vocab-system-info.linux.html">system-info.linux</a>: implement <code>username</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-system-info.macosx.html">system-info.macosx</a>: new OS codenames, implement <code>username</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-terminal.html">terminal</a>: fix typo in name of <code>terminal-height</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-tetris.html">tetris</a>: some cleanup, background color showing running or paused</li>
<li><a href="https://docs.factorcode.org/content/vocab-timers.html">timers</a>: improve timers to re-use threads when restarted</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.annotations.html">tools.annotations</a>: optionally re-annotate silently, add <code>reset-all</code> convenience word</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.completions.html">tools.completions</a>: add <code>pathname</code> completions, improve qualified word name completions</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.dns.html">tools.dns</a>: handle dns aliases</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.dns.public.html">tools.dns.public</a>: add more known nameservers</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.hexdump.html">tools.hexdump</a>: adding <code>M\ string hexdump</code>.</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.scaffold.html">tools.scaffold</a>: improve scaffolding of unit tests</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.test.html">tools.test</a>: adding <code>long-unit-test</code>, <code>must-not-fail</code>, <code>test-root</code>, <code>refresh-and-test</code>, <code>MAIN:</code> command-line</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.wc.html">tools.wc</a>: better support for files not found</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.backend.cocoa.html">ui.backend.cocoa</a>: supporting dynamic light/dark theme switching</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.backend.cocoa.views.html">ui.backend.cocoa.views</a>: supporting preedit input methods for language support</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.backend.windows.html">ui.backend.windows</a>: some bug fixes and cleanup</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.backend.editors.html">ui.gadgets.editors</a>: adding readline-bindings, preedit support</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.gadgets.glass.html">ui.gadgets.glass</a>: fix popups off screen</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.gadgets.panes.html">ui.gadgets.panes</a>: better nested stream style support</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.gadgets.tabbed.html">ui.gadgets.tabbed</a>: modernized the tab style</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.inspector.html">ui.tools.inspector</a>: improve non-printable string inspection</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.listener.html">ui.tools.listener</a>: improve font-size adjustments</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.operations.html">ui.tools.operations</a>: adding copy-object support</li>
<li><a href="https://docs.factorcode.org/content/vocab-unicode.html">unicode</a>: support Unicode 15.0.0</li>
<li><a href="https://docs.factorcode.org/content/vocab-units.html">units</a>: adding <code>d^</code> and <code>d-cube</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-units.si.html">units.si</a>: more units (quetta, ronna, ronto, quecto)</li>
<li><a href="https://docs.factorcode.org/content/vocab-unix.ffi.macosx.html">unix.ffi.macosx</a>: adding xattr support</li>
<li><a href="https://docs.factorcode.org/content/vocab-unix.process.html">unix.process</a>: adding <code>posix_spawn</code> support</li>
<li><a href="https://docs.factorcode.org/content/vocab-urls.html">urls</a>: adding <code>redacted-url</code> for log files, IPV6 support, lots of test cases</li>
<li><a href="https://docs.factorcode.org/content/vocab-urls.encoding.html">urls.encoding</a>: removing “;” query string separators, adding <code>encode-uri</code> and <code>decode-uri</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-uuid.html">uuid</a>: adding support for versions 6, 7, and 8.</li>
<li><a href="https://docs.factorcode.org/content/vocab-vocabs.cache.html">vocabs.cache</a>: fix to not reset disk cache when forgetting a single vocab</li>
<li><a href="https://docs.factorcode.org/content/vocab-vocabs.loader.html">vocabs.loader</a>: fix a restarts issue</li>
<li><a href="https://docs.factorcode.org/content/vocab-vocabs.metadata.html">vocabs.metadata</a>: adding <code>vocab-metadata-paths</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-vocabs.platforms.html">vocabs.platforms</a>: adding some experimental syntax</li>
<li><a href="https://docs.factorcode.org/content/vocab-webapps.html">webapps</a>: some style improvements, light/dark mode support</li>
<li><a href="https://docs.factorcode.org/content/vocab-webbrowser.html">webbrowser</a>: adding <code>MAIN:</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-websites.concatenative.html">websites.concatenative</a>: support <a href="https://cgit.factorcode.org">cgit.factorcode.org</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-wikipedia.html">wikipedia</a>: adding <code>MAIN:</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.com.html">windows.com</a>: more COM interfaces</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.errors.html">windows.errors</a>: utility words for error handling</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.kernel32.html">windows.kernel32</a>: adding <code>GetLogicalDriveStrings</code> and <code>GetDynamicTimeZoneInformation</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.ole32.html">windows.ole32</a>: more constants</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.psapi.html">windows.psapi</a>: adding <code>GetProcessImageFileNameA</code> and <code>GetModuleFileNameExW</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.registry.html">windows.registry</a>: adding <code>query-registry</code> word</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.shell32.html">windows.shell32</a>: add <code>shell32.dll</code> functions</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.uniscribe.html">windows.uniscribe</a>: transparency improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.user32.html">windows.user32</a>: adding DPI awareness words</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.winsock.html">windows.winsock</a>: more socket constants</li>
<li><a href="https://docs.factorcode.org/content/vocab-xdg.html">xdg</a>: adding support for <code>XDG_STATE_HOME</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-xml.data.html">xml.data</a>: better support CDATA tags</li>
<li><a href="https://docs.factorcode.org/content/vocab-xmode.html">xmode</a>: update XMODE files, support newer syntax</li>
<li><a href="https://docs.factorcode.org/content/vocab-zoneinfo.html">zoneinfo</a>: update to 2023c, improved rule parsing</li>
</ul>
<h3 id="vm-improvements">VM Improvements:</h3>
<ul>
<li>Increase codeheap default to 96 MB</li>
<li>Set <code>current-directory</code> when launching Factor.app</li>
<li>More work on ARM backend</li>
<li>Faster <code>file-exists?</code> on Windows</li>
<li>Some work on FreeBSD support</li>
</ul>
Vigenère Cipher
https://re.factorcode.org/2023/08/vigenere.html
Mon, 21 Aug 2023 08:00:00 -0700https://re.factorcode.org/2023/08/vigenere.html<p>The <a href="https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher">Vigenère cipher</a> is a
historically interesting method of encrypting a piece of text using a <a href="https://en.wikipedia.org/wiki/Caesar_cipher">Caesar
cipher</a> where each letter is
encoded based on a different letter from a repeating input “key” text. If the
recipient knows the key, they can recover the plaintext by reversing the
encoding process.</p>
<blockquote>
<p>For example, suppose that the plaintext to be encrypted is</p>
<p><code>attackatdawn</code></p>
<p>The person sending the message chooses a keyword and repeats it until it
matches the length of the plaintext, for example, the keyword “LEMON”:</p>
<p><code>LEMONLEMONLE</code></p>
</blockquote>
<p>We start by defining the available alphabet:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">LETTERS</span> <span class="s">"ABCDEFGHIJKLMNOPQRSTUVWXYZ"</span>
</span></span></code></pre></div><p>We can implement this using an inner word and a flag for whether we are
encrypting or decrypting a message. For convenience, we will use <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a> and make
sure both the key and the message are
<a href="https://docs.factorcode.org/content/word-__gt__upper,unicode.html">uppercase</a>
and drop all non-letters from the input:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">viginere</span> <span class="nf">( </span><span class="nv">msg</span> <span class="nv">key</span> <span class="nv">encrypt?</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> msg >upper :> MSG
</span></span><span class="line"><span class="cl"> key >upper :> KEY
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>MSG [| ch |
</span></span><span class="line"><span class="cl"> ch LETTERS <span class="nb">index </span>[| i |
</span></span><span class="line"><span class="cl"> [ <span class="m">1 </span><span class="nb">+ </span>KEY <span class="nb">length mod </span>i ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> KEY <span class="nb">nth </span>LETTERS <span class="nb">index
</span></span></span><span class="line"><span class="cl"> encrypt? [ <span class="nb">+ </span>] [ <span class="nb">- </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> LETTERS <span class="nb">length </span>[ <span class="nb">+ </span>] [ <span class="nb">mod </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> LETTERS <span class="nb">nth </span>,
</span></span><span class="line"><span class="cl"> ] <span class="nb">when*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each drop
</span></span></span><span class="line"><span class="cl"> ] <span class="s">""</span> make <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>vigenere</span> <span class="nf">( </span><span class="nv">msg</span> <span class="nv">key</span> <span class="nf">-- </span><span class="nv">encrypted</span> <span class="nf">) </span><span class="no">t </span>viginere <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">vigenere></span> <span class="nf">( </span><span class="nv">msg</span> <span class="nv">key</span> <span class="nf">-- </span><span class="nv">decrypted</span> <span class="nf">) </span><span class="no">f </span>viginere <span class="k">;
</span></span></span></code></pre></div><p>We can make some test cases showing that it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="s">"VBVTQBYOLCPB"</span> } [
</span></span><span class="line"><span class="cl"> <span class="s">"ATTACKATDAWN"</span> <span class="s">"VICTORY"</span> >vigenere
</span></span><span class="line"><span class="cl">] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="s">"ATTACKATDAWN"</span> } [
</span></span><span class="line"><span class="cl"> <span class="s">"VBVTQBYOLCPB"</span> <span class="s">"VICTORY"</span> vigenere>
</span></span><span class="line"><span class="cl">] unit-test
</span></span></code></pre></div><p>It gained a reputation for being exceptionally strong – even being described as
“<em>unbreakable</em>” and “<em>impossible of translation</em>”. It turns out there is a big
difference between difficult and impossible, and a
<a href="https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher#Cryptanalysis">cryptanalysis</a>
ultimately found various weaknesses that enabled the cipher to be broken or
attacked in certain ways.</p>
<p>While not inventing the cipher that holds his name, Vigenère actually invented
a stronger version called an <a href="https://en.wikipedia.org/wiki/Autokey_cipher">autokey
cipher</a> which modifies the key
during the encoding and decoding process avoiding one of the weak points when
the key is shorter than the message and the letters are reused.</p>
<p>It is a smidge more complex to implement but shares the same structure as the
version above:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">autokey</span> <span class="nf">( </span><span class="nv">msg</span> <span class="nv">key</span> <span class="nv">encrypt?</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> msg >upper :> MSG
</span></span><span class="line"><span class="cl"> key >upper >sbuf :> KEY
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>MSG [| ch |
</span></span><span class="line"><span class="cl"> ch LETTERS <span class="nb">index </span>[| i |
</span></span><span class="line"><span class="cl"> [ <span class="m">1 </span><span class="nb">+ </span>i ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> KEY encrypt? [ ch <span class="nb">suffix! </span>] <span class="nb">when nth
</span></span></span><span class="line"><span class="cl"> LETTERS <span class="nb">index
</span></span></span><span class="line"><span class="cl"> encrypt? [ <span class="nb">+ </span>] [ <span class="nb">- </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> LETTERS <span class="nb">length </span>[ <span class="nb">+ </span>] [ <span class="nb">mod </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> LETTERS <span class="nb">nth
</span></span></span><span class="line"><span class="cl"> encrypt? [ KEY <span class="nb">over suffix! drop </span>] <span class="nb">unless </span>,
</span></span><span class="line"><span class="cl"> ] <span class="nb">when*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each drop
</span></span></span><span class="line"><span class="cl"> ] <span class="s">""</span> make <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>autokey</span> <span class="nf">( </span><span class="nv">msg</span> <span class="nv">key</span> <span class="nf">-- </span><span class="nv">encrypted</span> <span class="nf">) </span><span class="no">t </span>autokey <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">autokey></span> <span class="nf">( </span><span class="nv">msg</span> <span class="nv">key</span> <span class="nf">-- </span><span class="nv">decrypted</span> <span class="nf">) </span><span class="no">f </span>autokey <span class="k">;
</span></span></span></code></pre></div><p>And some more test cases:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="s">"FWGLCDEJIKG"</span> } [
</span></span><span class="line"><span class="cl"> <span class="s">"AWESOMENESS"</span> <span class="s">"FACTOR"</span> >autokey
</span></span><span class="line"><span class="cl">] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="s">"AWESOMENESS"</span> } [
</span></span><span class="line"><span class="cl"> <span class="s">"FWGLCDEJIKG"</span> <span class="s">"FACTOR"</span> autokey>
</span></span><span class="line"><span class="cl">] unit-test
</span></span></code></pre></div>Easy Help
https://re.factorcode.org/2023/06/easy-help.html
Tue, 13 Jun 2023 09:00:00 -0700https://re.factorcode.org/2023/06/easy-help.html<p>One of the challenges with any software platform, and particularly for
<a href="https://en.wikipedia.org/wiki/Programming_language">programming languages</a>,
is having enough <a href="https://technicalwriterhq.com/documentation/software-documentation/how-to-write-software-documentation/">well-written
documentation</a>
to onboard new users as well as providing assistance to those that have been
using the platform for longer.</p>
<p>In previous versions of <a href="https://factorcode.org">Factor</a>, the <a href="https://docs.factorcode.org/content/article-help.html">help
system</a> – which
provides for writing documentation and browsing the documentation in the
command-line as well as in the <a href="https://docs.factorcode.org/content/article-ui-browser.html">UI
browser</a> –
required somewhat verbose syntax needing, for example, all text to be
defined as literal
<a href="https://docs.factorcode.org/content/article-strings.html">strings</a>. This
became both hard to write and hard to read as source code.</p>
<p>In the upcoming release of Factor 0.99, we have a kind of “easy help” syntax
that allows some smart interpretation of documentation syntax so that the
following example is now possible:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">HELP:</span> <span class="nf">do-the-thing</span>
</span></span><span class="line"><span class="cl">{ $values
</span></span><span class="line"><span class="cl"> word: word
</span></span><span class="line"><span class="cl"> n: <span class="nb">number
</span></span></span><span class="line"><span class="cl"> quot: { $quotation <span class="nf">( </span><span class="nv">x</span> <span class="nf">-- </span><span class="nv">y</span> <span class="nf">) </span>}
</span></span><span class="line"><span class="cl"> seq: { $sequence <span class="s">"things"</span> }
</span></span><span class="line"><span class="cl"> seq: { $sequence <span class="nb">fixnum </span>}
</span></span><span class="line"><span class="cl"> obj/f: { $maybe <span class="nb">object </span>}
</span></span><span class="line"><span class="cl"> this: <span class="s">"that other thing"</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">{ $description
</span></span><span class="line"><span class="cl"> Does the thing. Words which <span class="no">\ execute</span> an input
</span></span><span class="line"><span class="cl"> parameter must be declared <span class="no">\ inline</span> so
</span></span><span class="line"><span class="cl"> that a caller which passes in a literal word can have a
</span></span><span class="line"><span class="cl"> static stack effect.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> A <span class="nb">second </span>paragraph should work also. And we should be
</span></span><span class="line"><span class="cl"> able to refer to markup { $snippet <span class="s">"text"</span> } <span class="m">.
</span></span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">{ $notes
</span></span><span class="line"><span class="cl"> To <span class="nb">execute </span>a non-literal word, you can use
</span></span><span class="line"><span class="cl"> <span class="no">\ execute(</span> to check the stack effect before
</span></span><span class="line"><span class="cl"> calling <span class="nb">at </span>runtime.
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">{ $examples
</span></span><span class="line"><span class="cl"> [=[
</span></span><span class="line"><span class="cl"> <span class="kn">USING:</span> <span class="nn">kernel</span> <span class="nn">io</span> <span class="nn">words</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"> <span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="k">:</span> <span class="nf">twice</span> <span class="nf">( </span><span class="nv">word</span> <span class="nf">-- ) </span><span class="nb">dup execute execute </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl"> <span class="k">:</span> <span class="nf">hello</span> <span class="nf">( -- ) </span><span class="s">"Hello"</span> <span class="nb">print </span><span class="k">;
</span></span></span><span class="line"><span class="cl"> <span class="no">\ hello</span> twice
</span></span><span class="line"><span class="cl"> <span class="s">"Hello\nHello"</span>
</span></span><span class="line"><span class="cl"> ]=]
</span></span><span class="line"><span class="cl">} <span class="k">;
</span></span></span></code></pre></div><p>This also applies to articles, which can now be written much more clearly:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">ARTICLE: <span class="s">"roman"</span> <span class="s">"Roman numerals"</span>
</span></span><span class="line"><span class="cl">The { $vocab-link <span class="s">"roman"</span> } vocabulary can convert numbers to <span class="nb">and </span>from the
</span></span><span class="line"><span class="cl">Roman numeral system <span class="nb">and </span>can perform arithmetic given Roman numerals as input.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">A parsing word for literal Roman numerals:
</span></span><span class="line"><span class="cl">{ $subsections <span class="kn">POSTPONE:</span> <span class="nf">ROMAN:</span> }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Converting to Roman numerals:
</span></span><span class="line"><span class="cl">{ $subsections
</span></span><span class="line"><span class="cl"> >roman
</span></span><span class="line"><span class="cl"> >ROMAN
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Converting Roman numerals to integers:
</span></span><span class="line"><span class="cl">{ $subsections roman> }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Roman numeral arithmetic:
</span></span><span class="line"><span class="cl">{ $subsections
</span></span><span class="line"><span class="cl"> roman+
</span></span><span class="line"><span class="cl"> roman-
</span></span><span class="line"><span class="cl"> roman*
</span></span><span class="line"><span class="cl"> roman/i
</span></span><span class="line"><span class="cl"> roman/mod
</span></span><span class="line"><span class="cl">} <span class="k">;
</span></span></span></code></pre></div><p>We have periodically found some edge cases that don’t quite format as
expected, but likely most of these cases have been resolved.</p>
<p>Give it a try and let us know what you think!</p>
Rainbows
https://re.factorcode.org/2023/06/rainbows.html
Fri, 02 Jun 2023 08:00:00 -0700https://re.factorcode.org/2023/06/rainbows.html<p><a href="https://en.wikipedia.org/wiki/Rainbow">Rainbows</a> are awesome, especially the
ones that are <a href="https://www.metoffice.gov.uk/weather/learn-about/weather/optical-effects/rainbows/double-rainbows">double
rainbows</a>
which can be quite intense when they are a <a href="https://www.youtube.com/watch?v=OQSNhk5ICTI">double rainbow all the way across
the sky</a>. They can also be awesome
when they show up as <a href="https://en.wikipedia.org/wiki/Rainbow_flag_(LGBT)">rainbow
flags</a> which are used to
indicate that a place is welcome, accepting, and safe for people.</p>
<p>Given that this is <a href="https://en.wikipedia.org/wiki/Pride_Month">Pride Month</a>, it
might be fun to make some rainbows today using
<a href="https://factorcode.org">Factor</a>.</p>
<p>I bumped into this nice tutorial on <a href="https://krazydad.com/tutorials/makecolors.php">making annoying rainbows in
javascript</a> that has some
background on color theory including links to <a href="https://blog.asmartbear.com/color-wheels.html">how color vision actually
works</a> as well as detailed
science on <a href="http://www.handprint.com/HP/WCL/color1.html">light and the eye</a> and
a “better rainbow method” called the
<a href="http://basecase.org/env/on-rainbows">sinebow</a>.</p>
<p>After implementing a lot of <a href="https://re.factorcode.org/2013/10/color-support.html">color support</a> for
Factor, I get <a href="https://xkcd.com/356/">nerd sniped</a> sometimes when it comes to
colors. Instead of using <a href="https://en.wikipedia.org/wiki/HSL_and_HSV">HSL
colors</a>, lets just use the more
common <a href="https://en.wikipedia.org/wiki/RGB_color_model">RGB color model</a> to make
some rainbows!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">rainbow-phase.</span> <span class="nf">( </span><span class="nv">str</span> <span class="nv">phase</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> str >graphemes :> chars
</span></span><span class="line"><span class="cl"> 2pi chars <span class="nb">length / </span>:> frequency
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> chars [| s i |
</span></span><span class="line"><span class="cl"> frequency i <span class="nb">* </span><span class="m">2 </span><span class="nb">+ </span>phase <span class="nb">+ </span>sin <span class="m">0.5 </span><span class="nb">+
</span></span></span><span class="line"><span class="cl"> frequency i <span class="nb">* </span><span class="m">0 </span><span class="nb">+ </span>phase <span class="nb">+ </span>sin <span class="m">0.5 </span><span class="nb">+
</span></span></span><span class="line"><span class="cl"> frequency i <span class="nb">* </span><span class="m">4 </span><span class="nb">+ </span>phase <span class="nb">+ </span>sin <span class="m">0.5 </span><span class="nb">+
</span></span></span><span class="line"><span class="cl"> <span class="m">1.0 </span><rgba> :> color
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> s <span class="s">""</span> <span class="nb">like </span>H{ { foreground color } } format
</span></span><span class="line"><span class="cl"> ] <span class="nb">each-index nl </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">rainbow.</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- ) </span><span class="m">0 </span>rainbow-phase. <span class="k">;
</span></span></span></code></pre></div><p>This supports <a href="https://home.unicode.org">Unicode</a> by calling
<a href="https://docs.factorcode.org/content/word-__gt__graphemes,unicode.html">>graphemes</a>
to split a word by grouping on visual characters.</p>
<p>And, it looks like this:</p>
<p>
<img src="https://re.factorcode.org/images/2023-06-02-rainbows.png" alt="" width="515" height="128" />
</p>
<p>Happy Pride Month!</p>
ZIM Builder
https://re.factorcode.org/2023/05/zim-builder.html
Wed, 17 May 2023 07:30:00 -0700https://re.factorcode.org/2023/05/zim-builder.html<p>Apparently, it is just too much fun building tools to make an <a href="https://re.factorcode.org/2023/05/offline-wikipedia.html">offline
Wikipedia</a> and the next thing we needed to
build was a way to make offline <a href="https://docs.factorcode.org">Factor
documentation</a>. This documentation is available
inside each <a href="https://factorcode.org">Factor</a> instance and generated by the
<a href="https://docs.factorcode.org/content/article-help.html">Factor help system</a>.</p>
<p>Since we implemented the <a href="https://github.com/factor/factor/blob/master/extra/zim/zim.factor">zim
vocabulary</a>
with support for reading the <a href="https://wiki.openzim.org/wiki/ZIM_file_format">ZIM file
format</a> and the <a href="https://github.com/factor/factor/blob/master/extra/zim/server/server.factor">zim.server
vocabulary</a>
with support for serving those files out as websites, the natural follow up
is the <a href="https://github.com/factor/factor/blob/master/extra/zim/builder/builder.factor">zim.builder
vocabulary</a>
to make the ZIM files in the first place!</p>
<p>Yesterday, I wrote the <code>build-zim</code> word that can archive all of the files
in the
<a href="https://docs.factorcode.org/content/word-current-directory,io.pathnames.html">current-directory</a>
into a ZIM file at the specified output path. Just now, I generated some
“offline Factor documentation” by running this command on a <code>docs</code>
directory holding all the HTML files uploaded by a recent nightly build:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">zim.builder</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"resource:docs"</span> [ <span class="s">"resource:docs.zim"</span> build-zim ] with-directory
</span></span></code></pre></div><p>You can then run a local Factor documentation server like so:</p>
<pre tabindex="0"><code>$ ./factor -run=zim.server docs.zim
</code></pre><p>It’s interesting that our Factor documentation is 1.4 GB of HTML files, 52 MB
as a <code>docs.tar.gz</code>, and 47 MB as a <code>docs.zim</code> file using <a href="http://facebook.github.io/zstd/">Zstandard
compression</a>. It’s a cool file format for
serving this type of content.</p>
<p>I posted a <a href="https://downloads.factorcode.org/misc/docs.zim">ZIM snapshot of the Factor
documentation</a> if you’d like
to download it and give this a try with a recent nightly build.</p>
Offline Wikipedia
https://re.factorcode.org/2023/05/offline-wikipedia.html
Tue, 16 May 2023 07:30:00 -0700https://re.factorcode.org/2023/05/offline-wikipedia.html<p>Pretty much everyone agrees that <a href="https://wikipedia.org">Wikipedia</a> is awesome
(except maybe during one of their <a href="https://slate.com/technology/2022/12/wikipedia-wikimedia-foundation-donate.html">controversial fundraising
campaigns</a>).
In addition to Wikipedia, the <a href="https://www.wikimedia.org">Wikimedia Foundation</a>
operates:</p>
<p>
<img src="https://re.factorcode.org/images/2023-05-16-wikimedia.png" alt="" width="527" height="530" />
</p>
<p>Even though the official <a href="https://apps.apple.com/us/app/wikipedia/id324715238">Wikipedia iOS
app</a> and <a href="https://play.google.com/store/apps/details?id=org.wikipedia&hl=en_US&gl=US&pli=1">Wikipedia
Android
app</a>
are both great, they still require access to <a href="https://en.wikipedia.org/wiki/Internet">the
internet</a> to be useful. I am not alone
when wondering how to <a href="https://diff.wikimedia.org/2018/06/14/build-your-own-hitchhikers-guide-with-wikipedia/">build your own Hitchhiker’s Guide with
Wikipedia</a>
and looking through the options to <a href="https://en.wikipedia.org/wiki/Wikipedia:Database_download">download a Wikipedia
database</a>.</p>
<p>One way you can do this is to implement support for the <a href="https://wiki.openzim.org/wiki/ZIM_file_format">ZIM file
format</a>, for example using the
<a href="https://www.openzim.org/wiki/Libzim">libzim project</a>. There are many archives
available to <a href="https://library.kiwix.org/">download as a ZIM file</a> for Wikipedia
and various popular websites like <a href="https://stackoverflow.com">StackOverflow</a>,
<a href="https://www.gutenberg.org">Project Gutenberg</a>, and even some open source
projects. You can also <a href="https://openzim.org/wiki/Build_your_ZIM_file">build your own ZIM
file</a> if you want to archive
custom content.</p>
<blockquote>
<p>ZIM stands for “Zeno IMproved”, as it replaces the earlier Zeno file format.
Its file compression uses <a href="https://en.wikipedia.org/wiki/LZMA2">LZMA2</a>, as
implemented by the <a href="https://en.wikipedia.org/wiki/XZ_Utils">xz-utils</a>
library, and, more recently,
<a href="https://en.wikipedia.org/wiki/Zstandard">Zstandard</a>. The openZIM project is
sponsored by <a href="https://en.wikipedia.org/wiki/Wikimedia_CH">Wikimedia CH</a>, and
supported by the <a href="https://en.wikipedia.org/wiki/Wikimedia_Foundation">Wikimedia
Foundation</a>.</p>
</blockquote>
<p>Let’s implement this using <a href="https://factorcode.org">Factor</a>!</p>
<h3 id="header">Header</h3>
<p>Each ZIM file starts with a header in <a href="https://en.wikipedia.org/wiki/Endianness">little endian
format</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">PACKED-STRUCT: zim-header
</span></span><span class="line"><span class="cl"> { magic-number uint32_t }
</span></span><span class="line"><span class="cl"> { major-version uint16_t }
</span></span><span class="line"><span class="cl"> { minor-version uint16_t }
</span></span><span class="line"><span class="cl"> { uuid uint64_t[2] }
</span></span><span class="line"><span class="cl"> { entry-count uint32_t }
</span></span><span class="line"><span class="cl"> { cluster-count uint32_t }
</span></span><span class="line"><span class="cl"> { url-ptr-pos uint64_t }
</span></span><span class="line"><span class="cl"> { title-ptr-pos uint64_t }
</span></span><span class="line"><span class="cl"> { cluster-ptr-pos uint64_t }
</span></span><span class="line"><span class="cl"> { mime-list-ptr-pos uint64_t }
</span></span><span class="line"><span class="cl"> { main-page uint32_t }
</span></span><span class="line"><span class="cl"> { layout-page uint32_t }
</span></span><span class="line"><span class="cl"> { checksum-pos uint64_t } <span class="k">;
</span></span></span></code></pre></div><p>In addition to 16-bit, 32-bit, and 64-bit little-endian numbers, we need to be
able to read null-terminated strings typically stored as UTF-8. For example,
when reading the mime-type list:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-string</span> <span class="nf">( -- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { <span class="m">0 </span>} <span class="nb">read-until </span><span class="m">0 </span><span class="nb">assert= </span>utf8 decode <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-mime-types</span> <span class="nf">( -- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ read-string <span class="nb">dup empty? not </span>] [ ] <span class="nb">produce nip </span><span class="k">;
</span></span></span></code></pre></div><p>That’s enough to parse the header file, the list of mime-types, and the lists
of pointers to urls, titles, and clusters used for indexing into the ZIM file.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">zim</span> <span class="nv">path</span> <span class="nv">header</span> <span class="nv">mime-types</span> <span class="nv">urls</span> <span class="nv">titles</span> <span class="nv">clusters</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-zim</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">zim</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>binary [
</span></span><span class="line"><span class="cl"> zim-header read-struct <span class="nb">dup </span>{
</span></span><span class="line"><span class="cl"> [ magic-number>> <span class="m">0x44D495A </span><span class="nb">assert= </span>]
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> mime-list-ptr-pos>> <span class="nb">seek-absolute seek-input
</span></span></span><span class="line"><span class="cl"> read-mime-types
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>url-ptr-pos>> <span class="nb">seek-absolute seek-input
</span></span></span><span class="line"><span class="cl"> entry-count>> [ <span class="m">8 </span><span class="nb">read </span>le> ] <span class="nb">replicate
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>title-ptr-pos>> <span class="nb">seek-absolute seek-input
</span></span></span><span class="line"><span class="cl"> entry-count>> [ <span class="m">4 </span><span class="nb">read </span>le> ] <span class="nb">replicate
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>cluster-ptr-pos>> <span class="nb">seek-absolute seek-input
</span></span></span><span class="line"><span class="cl"> cluster-count>> [ <span class="m">8 </span><span class="nb">read </span>le> ] <span class="nb">replicate
</span></span></span><span class="line"><span class="cl"> ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span>zim <span class="nb">boa
</span></span></span><span class="line"><span class="cl"> ] with-file-reader <span class="k">;
</span></span></span></code></pre></div><h3 id="entries">Entries</h3>
<p>There are two types of <em>directory entries</em>:</p>
<ol>
<li>content entries</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">content-entry</span> <span class="nv">mime-type</span> <span class="nv">parameter-len</span> <span class="nv">namespace</span>
</span></span><span class="line"><span class="cl"> <span class="nv">revision</span> <span class="nv">cluster-number</span> <span class="nv">blob-number</span> <span class="nv">url</span> <span class="nv">title</span> <span class="nv">parameter</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-content-entry</span> <span class="nf">( </span><span class="nv">mime-type</span> <span class="nf">-- </span><span class="nv">content-entry</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">read1
</span></span></span><span class="line"><span class="cl"> <span class="nb">read1
</span></span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>le>
</span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>le>
</span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>le>
</span></span><span class="line"><span class="cl"> read-string
</span></span><span class="line"><span class="cl"> read-string
</span></span><span class="line"><span class="cl"> <span class="no">f
</span></span></span><span class="line"><span class="cl"> content-entry <span class="nb">boa
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>parameter-len>> <span class="nb">read </span>>>parameter <span class="k">;
</span></span></span></code></pre></div><ol start="2">
<li>redirect entries</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">redirect-entry</span> <span class="nv">mime-type</span> <span class="nv">parameter-len</span> <span class="nv">namespace</span> <span class="nv">revision</span>
</span></span><span class="line"><span class="cl"> <span class="nv">redirect-index</span> <span class="nv">url</span> <span class="nv">title</span> <span class="nv">parameter</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-redirect-entry</span> <span class="nf">( </span><span class="nv">mime-type</span> <span class="nf">-- </span><span class="nv">redirect-entry</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">read1
</span></span></span><span class="line"><span class="cl"> <span class="nb">read1
</span></span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>le>
</span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>le>
</span></span><span class="line"><span class="cl"> read-string
</span></span><span class="line"><span class="cl"> read-string
</span></span><span class="line"><span class="cl"> <span class="no">f
</span></span></span><span class="line"><span class="cl"> redirect-entry <span class="nb">boa
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>parameter-len>> <span class="nb">read </span>>>parameter <span class="k">;
</span></span></span></code></pre></div><p>The mime-type indicates which type of entry we are reading:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-entry</span> <span class="nf">( -- </span><span class="nv">entry</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">2 </span><span class="nb">read </span>le> <span class="nb">dup </span><span class="m">0xffff </span><span class="nb">=
</span></span></span><span class="line"><span class="cl"> [ read-redirect-entry ] [ read-content-entry ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Now we can read the entry at index <code>n</code> in a ZIM file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-entry-index</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">zim</span> <span class="nf">-- </span><span class="nv">entry/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> urls>> <span class="nb">nth seek-absolute seek-input </span>read-entry <span class="k">;
</span></span></span></code></pre></div><h3 id="clusters">Clusters</h3>
<p>Content is stored as <em>clusters</em> of data, where each cluster is a sequence of
binary <em>blobs</em> contained at an offset into the cluster. And the cluster is
stored either uncompressed or with optional compression (typically LZMA or
ZStandard).</p>
<p>We can read the “no compression” version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-cluster-none</span> <span class="nf">( -- </span><span class="nv">offsets</span> <span class="nv">blobs</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>le>
</span></span><span class="line"><span class="cl"> [ <span class="m">4 </span><span class="nb">/i </span><span class="m">1 </span><span class="nb">- </span>[ <span class="m">4 </span><span class="nb">read </span>le> ] <span class="nb">replicate </span>] [ <span class="nb">prefix </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ <span class="nb">last </span>] [ <span class="nb">first </span>] <span class="nb">bi - read </span><span class="k">;
</span></span></span></code></pre></div><p>And then read the “ZStandard compression” version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-cluster-zstd</span> <span class="nf">( -- </span><span class="nv">offsets</span> <span class="nv">blobs</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> zstd-uncompress-stream-frame <span class="nb">dup </span>uint32_t deref
</span></span><span class="line"><span class="cl"> [ <span class="m">4 </span><span class="nb">/i </span>uint32_t <c-direct-array> ] [ <span class="nb">tail-slice </span>] <span class="nb">2bi
</span></span></span><span class="line"><span class="cl"> <span class="nb">2dup </span>[ [ <span class="nb">last </span>] [ <span class="nb">first </span>] <span class="nb">bi - </span>] [ <span class="nb">length assert= </span>] <span class="nb">bi* </span><span class="k">;
</span></span></span></code></pre></div><p>The cluster can then be read by checking the compression type in use:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-cluster</span> <span class="nf">( -- </span><span class="nv">offsets</span> <span class="nv">blobs</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">read1 </span>[ <span class="m">5 </span><span class="nb">bit? </span><span class="no">f </span><span class="nb">assert= </span>] [ <span class="m">4 </span>bits ] <span class="nb">bi </span>{
</span></span><span class="line"><span class="cl"> { <span class="m">1 </span>[ read-cluster-none ] }
</span></span><span class="line"><span class="cl"> { <span class="m">2 </span>[ <span class="s">"zlib not supported"</span> <span class="nb">throw </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">3 </span>[ <span class="s">"bzip2 not supported"</span> <span class="nb">throw </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">4 </span>[ <span class="s">"lzma not supported"</span> <span class="nb">throw </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">5 </span>[ read-cluster-zstd ] }
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div><p>To read the <em>blob</em> at index <code>n</code>, we read the entire cluster, then offset into
the <em>blobs</em> data:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">read-cluster-blob</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">blob</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> read-cluster :> <span class="nf">( </span><span class="nv">offsets</span> <span class="nv">blobs</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>offsets <span class="nb">nth </span>:> zero
</span></span><span class="line"><span class="cl"> n offsets <span class="nb">nth </span>:> from
</span></span><span class="line"><span class="cl"> n <span class="m">1 </span><span class="nb">+ </span>offsets <span class="nb">nth </span>:> to
</span></span><span class="line"><span class="cl"> from to [ zero <span class="nb">- </span>] <span class="nb">bi@ </span>blobs <span class="nb">subseq </span><span class="k">;
</span></span></span></code></pre></div><p>Now we can read the <em>blob</em> by index into a given cluster in a ZIM file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-blob-index</span> <span class="nf">( </span><span class="nv">blob-number</span> <span class="nv">cluster-number</span> <span class="nv">zim</span> <span class="nf">-- </span><span class="nv">blob</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> clusters>> <span class="nb">nth seek-absolute seek-input </span>read-cluster-blob <span class="k">;
</span></span></span></code></pre></div><p>And we can read the entry content from each entry type or index:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">GENERIC#: read-entry-content <span class="m">1 </span><span class="nf">( </span><span class="nv">entry</span> <span class="nv">zim</span> <span class="nf">-- </span><span class="nv">blob</span> <span class="nv">mime-type</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M::</span> <span class="nc">content-entry</span> <span class="nf">read-entry-content</span> <span class="nf">( </span><span class="nv">entry</span> <span class="nv">zim</span> <span class="nf">-- </span><span class="nv">blob</span> <span class="nv">mime-type</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> entry blob-number>>
</span></span><span class="line"><span class="cl"> entry cluster-number>>
</span></span><span class="line"><span class="cl"> zim read-blob-index
</span></span><span class="line"><span class="cl"> entry mime-type>>
</span></span><span class="line"><span class="cl"> zim mime-types>> <span class="nb">nth </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">redirect-entry</span> <span class="nf">read-entry-content</span>
</span></span><span class="line"><span class="cl"> [ redirect-index>> ] [ read-entry-content ] <span class="nb">bi* </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">integer</span> <span class="nf">read-entry-content</span>
</span></span><span class="line"><span class="cl"> [ read-entry-index ] <span class="nb">keep </span>'[ _ read-entry-content ] [ <span class="no">f f </span>] <span class="nb">if* </span><span class="k">;
</span></span></span></code></pre></div><p>Reading the “main page” content is simple using the index stored in the ZIM
header:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-main-page</span> <span class="nf">( </span><span class="nv">zim</span> <span class="nf">-- </span><span class="nv">blob/f</span> <span class="nv">mime-type/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ header>> main-page>> ] [ read-entry-content ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>We can find an entry by searching using a <code>namespace</code> and <code>url</code>, taking
advantage of the fact the entries are sorted by <code><namespace><url></code> to perform
a <a href="https://docs.factorcode.org/content/article-binary-search.html">binary
search</a>. Some
common namespaces include:</p>
<ul>
<li><code>A</code> - Article</li>
<li><code>C</code> - User Content</li>
<li><code>M</code> - ZIM metadata</li>
<li><code>W</code> - Well known entries</li>
<li><code>X</code> - Search indexes</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">find-entry-url</span> <span class="nf">( </span><span class="nv">namespace</span> <span class="nv">url</span> <span class="nv">zim</span> <span class="nf">-- </span><span class="nv">entry/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span>zim header>> entry-count>> <iota> [
</span></span><span class="line"><span class="cl"> <span class="nb">nip </span>zim read-entry-index
</span></span><span class="line"><span class="cl"> <span class="nb">namespace over </span>namespace>> <=>
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>+eq+ <span class="nb">= </span>[ <span class="nb">drop </span>url <span class="nb">over </span>url>> <=> ] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> ] search <span class="nb">2drop dup </span>{
</span></span><span class="line"><span class="cl"> [ ] [ namespace>> <span class="nb">namespace = </span>] [ url>> url <span class="nb">= </span>]
</span></span><span class="line"><span class="cl"> } 1&& [ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">unless </span><span class="k">;
</span></span></span></code></pre></div><p>If we find the entry after searching, we can read it’s content:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-entry-url</span> <span class="nf">( </span><span class="nv">namespace</span> <span class="nv">url</span> <span class="nv">zim</span> <span class="nf">-- </span><span class="nv">blob/f</span> <span class="nv">mime-type/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ find-entry-url ] <span class="nb">keep </span>'[ _ read-entry-content ] [ <span class="no">f f </span>] <span class="nb">if* </span><span class="k">;
</span></span></span></code></pre></div><h3 id="web-server">Web Server</h3>
<p>This is all kinda awesome, but basically these ZIM files hold HTML data for an
offline instance of the various wiki-type servers. So, wouldn’t it be awesome
to make a <a href="https://docs.factorcode.org/content/article-http.server.responders.html">HTTP server
responder</a>
that loads a ZIM file and then returns data from it on a local Factor <a href="https://docs.factorcode.org/content/article-http.server.html">HTTP
server</a>?</p>
<p>Yes!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">zim-responder</span> <span class="nv">zim</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><zim-responder></span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">zim-responder</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> read-zim zim-responder <span class="nb">boa </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">zim-responder</span> <span class="nf">call-responder*</span>
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>{ [ <span class="nb">length </span><span class="m">1 </span><span class="nb">> </span>] [ <span class="nb">first length </span><span class="m">1 </span><span class="nb">= </span>] } 1&&
</span></span><span class="line"><span class="cl"> [ <span class="nb">unclip-slice first </span>] [ <span class="sc">CHAR: A </span>] <span class="nb">if swap </span><span class="s">"/"</span> <span class="nb">join
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">dip </span>[
</span></span><span class="line"><span class="cl"> zim>> <span class="nb">dup </span>path>> binary [
</span></span><span class="line"><span class="cl"> <span class="nb">over empty? </span>[ read-entry-url ] [ <span class="nb">2nip </span>read-main-page ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] with-file-reader
</span></span><span class="line"><span class="cl"> ] <span class="nb">bi* 2dup and </span>[
</span></span><span class="line"><span class="cl"> <content> binary >>content-encoding
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="nb">2drop </span><404>
</span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>We use that to make a little entry point that creates a <code>zim-responder</code> and
then sets it as the
<a href="https://docs.factorcode.org/content/word-main-responder,http.server.html">main-responder</a>
and calls
<a href="https://docs.factorcode.org/content/word-httpd,http.server.html">httpd</a> to
start a web server. Using the latest <a href="https://github.com/factor/factor">development
version</a>, we can run it like so:</p>
<pre tabindex="0"><code>$ ./factor -run=zim.server /path/to/wiki.zim [port]
</code></pre><p>There are few features that would be nice to add – like searching URLs, titles,
and content, or dealing with split ZIM files (when over 4GB on file systems
like FAT32) – but this is a pretty sweet neat new tool we have available now in
a nightly build and will be released soon in Factor 0.99.</p>
Calendar Ranges
https://re.factorcode.org/2023/05/calendar-ranges.html
Tue, 09 May 2023 09:00:00 -0700https://re.factorcode.org/2023/05/calendar-ranges.html<p>A post recently titled <a href="https://martinheinz.dev/blog/96">Python’s Missing Batteries: Essential Libraries
You’re Missing Out On</a> caught my eye. One
of my favorite parts about <a href="https://factorcode.org">Factor</a> is the <a href="https://docs.factorcode.org/content/article-vocab-index.html">large
standard
library</a> that
we ship with. Looking at blogs like these sometimes helps me notice
functionality that we are missing.</p>
<p>One of the provided examples from the
<a href="https://boltons.readthedocs.io/en/latest/timeutils.html">timeutils</a> module
is the <code>daterange</code> word that provides an iterator between a <code>start</code> and
<code>stop</code> date:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">start_date</span> <span class="o">=</span> <span class="n">date</span><span class="p">(</span><span class="n">year</span><span class="o">=</span><span class="mi">2023</span><span class="p">,</span> <span class="n">month</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">day</span><span class="o">=</span><span class="mi">9</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">end_date</span> <span class="o">=</span> <span class="n">date</span><span class="p">(</span><span class="n">year</span><span class="o">=</span><span class="mi">2023</span><span class="p">,</span> <span class="n">month</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">day</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">day</span> <span class="ow">in</span> <span class="n">timeutils</span><span class="o">.</span><span class="n">daterange</span><span class="p">(</span><span class="n">start_date</span><span class="p">,</span> <span class="n">end_date</span><span class="p">,</span> <span class="n">step</span><span class="o">=</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">)):</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="nb">repr</span><span class="p">(</span><span class="n">day</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># datetime.date(2023, 4, 9)</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># datetime.date(2023, 4, 11)</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># datetime.date(2023, 4, 13)</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># ...</span>
</span></span></code></pre></div><p>I realize that although we have <a href="https://docs.factorcode.org/content/article-ranges.html">numeric
ranges</a>, the
current support for
<a href="https://docs.factorcode.org/content/article-numbers.html">numbers</a> doesn’t
allow extending them so that <a href="https://docs.factorcode.org/content/article-timestamp-arithmetic.html">timestamp
arithmetic</a>
is implicitly supported. Some future version of Factor might fix this when
we finish merging support for <a href="https://en.wikipedia.org/wiki/Multiple_dispatch">multiple
dispatch</a>, but in the
meantime I added a <code>timestamp-range</code> object that works identically to
<code>range</code> but with
<a href="https://docs.factorcode.org/content/article-calendar.html">calendar</a>
objects.</p>
<p>The above <a href="https://python.org">Python</a> example would look something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">calendar.ranges</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2023 4 9 </span><date-utc>
</span></span><span class="line"><span class="cl"> <span class="m">2023 4 30 </span><date-utc>
</span></span><span class="line"><span class="cl"> <span class="m">2 </span>days <timestamp-range> [ <span class="m">. </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl">T{ timestamp { year <span class="m">2023 </span>} { month <span class="m">4 </span>} { day <span class="m">9 </span>} }
</span></span><span class="line"><span class="cl">T{ timestamp { year <span class="m">2023 </span>} { month <span class="m">4 </span>} { day <span class="m">11 </span>} }
</span></span><span class="line"><span class="cl">T{ timestamp { year <span class="m">2023 </span>} { month <span class="m">4 </span>} { day <span class="m">13 </span>} }
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>The <a href="https://github.com/factor/factor/blob/master/extra/calendar/ranges/ranges.factor">current
implementation</a>
has <code><timestamp-range></code> work the same way as <code><range></code> as it assumes an
inclusive range <code>[from,to]</code>. Give it a try!</p>
Case Conversion
https://re.factorcode.org/2023/05/case-conversion.html
Fri, 05 May 2023 06:00:00 -0700https://re.factorcode.org/2023/05/case-conversion.html<p>One aspect of exposure to different programming languages and programmers is
differing opinions on proper case conventions for class names, variable
names, and other attribute names. Sometimes you want to convert between them
for various reasons.</p>
<p>
<img src="https://re.factorcode.org/images/2023-05-05-aybabtu.png" alt="" width="480" height="320" />
</p>
<p>Looking around at other programming languages, you can find modules such as
<a href="https://www.npmjs.com/package/change-case">Change Case</a> for Javascript,
<a href="https://pypi.org/project/case-converter/">case-converter</a> for Python, a <a href="https://codegolf.stackexchange.com/questions/216396/a-snake-a-camel-and-a-kebab">code
golf
challenge</a>,
a regular expression approach to <a href="https://plainenglish.io/blog/convert-string-to-different-case-styles-snake-kebab-camel-and-pascal-case-in-javascript-da724b7220d7">convert string to different case
styles</a>,
and even a PHP module written by <a href="https://jawira.com">Jawira Portugal</a> called
<a href="https://jawira.github.io/case-converter/">Case Converter</a> that handles quite a
few, <em>ahem</em>, cases:</p>
<blockquote>
<p>Convert strings between 13 naming conventions: Snake case, Camel case,
Kebab case, Pascal case, Ada case, Train case, Cobol case, Macro case,
Upper case, Lower case, Title case, Sentence case and Dot notation.</p>
</blockquote>
<p>Examples of which might look something like:</p>
<ul>
<li><code>snake_case</code></li>
<li><code>camelCase</code></li>
<li><code>kebab-case</code></li>
<li><code>PascalCase</code></li>
<li><code>Ada_Case</code></li>
<li><code>Train-Case</code></li>
<li><code>COBOL-CASE</code></li>
<li><code>MACRO_CASE</code></li>
<li><code>UPPER CASE</code></li>
<li><code>lower case</code></li>
<li><code>Title Case</code></li>
<li><code>Sentence case</code></li>
<li><code>dot.case</code></li>
</ul>
<p>I thought it would be an interesting example, to make a
<a href="https://re.factorcode.org/2023/05/unicode.html">Unicode-aware</a> case conversion library for
<a href="https://factorcode.org">Factor</a> that handles all of those same cases in a
small amount of code (less than 35 lines of code!).</p>
<p>The first word looks for a <a href="https://docs.factorcode.org/content/word-lower__que__,unicode.html">lowercase
grapheme</a>,
then finds the next one that is not lowercase:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">case-index</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">i/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ lower? ] <span class="nb">find </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span>[ lower? <span class="nb">not </span>] <span class="nb">find-from drop
</span></span></span><span class="line"><span class="cl"> ] [ <span class="nb">nip </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>We can then use that method to split the
<a href="https://docs.factorcode.org/content/word-__gt__graphemes,unicode.html">graphemes</a>
at these case boundaries:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">split-case</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">words</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> >graphemes [ <span class="nb">dup empty? not </span>] [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ case-index ] [ <span class="nb">length or </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> <span class="nb">cut-slice swap concat
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">produce nip </span><span class="k">;
</span></span></span></code></pre></div><p>Splitting tokens, first on the common token separators, and then on the case
boundaries.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">split-tokens</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">words</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">" -_."</span> split [ split-case ] <span class="nb">map concat </span><span class="k">;
</span></span></span></code></pre></div><p>And now the core of the algorithm that splits an input string into tokens, with
two variants (one that applies a
<a href="https://docs.factorcode.org/content/word-quotation,quotations.html">quotation</a>
to each token and another that handles the first token differently than the
rest) before
<a href="https://docs.factorcode.org/content/word-join,sequences.html">joining</a> the
tokens using a provided <code>glue</code> character.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">case1</span> <span class="nf">( </span><span class="nv">str</span> <span class="nv">quot</span> <span class="nv">glue</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ split-tokens ] [ <span class="nb">map </span>] [ <span class="nb">join </span>] <span class="nb">tri* </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">case2</span> <span class="nf">( </span><span class="nv">str</span> <span class="nv">first-quot</span> <span class="nv">rest-quot</span> <span class="nv">glue</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ split-tokens <span class="m">0 </span><span class="nb">over </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">change-nth dup rest-slice </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">map! drop </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">join </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">spread </span><span class="k">; inline
</span></span></span></code></pre></div><p>Now that’s everything we need to implement all the case conversions!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>camelcase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">) </span>[ >lower ] [ >title ] <span class="s">""</span> case2 <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>pascalcase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">) </span>[ >title ] <span class="s">""</span> case1 <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>snakecase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">) </span>[ >lower ] <span class="s">"_"</span> case1 <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>adacase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">) </span>[ >title ] <span class="s">"_"</span> case1 <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>macrocase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">) </span>[ >upper ] <span class="s">"_"</span> case1 <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>kebabcase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">) </span>[ >lower ] <span class="s">"-"</span> case1 <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>traincase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">) </span>[ >title ] <span class="s">"-"</span> case1 <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>cobolcase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">) </span>[ >upper ] <span class="s">"-"</span> case1 <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>lowercase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">) </span>[ >lower ] <span class="s">" "</span> case1 <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>uppercase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">) </span>[ >upper ] <span class="s">" "</span> case1 <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>titlecase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">) </span>[ >title ] <span class="s">" "</span> case1 <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>sentencecase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">) </span>[ >title ] [ >lower ] <span class="s">" "</span> case2 <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>dotcase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">) </span>[ >lower ] <span class="s">"."</span> case1 <span class="k">;
</span></span></span></code></pre></div><p>These are available in the <a href="https://github.com/factor/factor/blob/master/extra/tokencase/tokencase.factor">tokencase
vocabulary</a>
and is included in the latest nightly builds.</p>
Unicode
https://re.factorcode.org/2023/05/unicode.html
Thu, 04 May 2023 09:00:00 -0700https://re.factorcode.org/2023/05/unicode.html<p>The <a href="https://rust-lang.org">Rust programming language</a> is pretty cool. I’ve
enjoyed many aspects of the <a href="https://transitiontech.ca/random/RIIR">Rewrite It In
Rust</a> meme that appears as a part of
the <a href="https://twitter.com/rustevangelism">Rust Evangelism Strike Force</a>. The
<a href="https://www.rust-lang.org/learn">Rust documentation</a> includes a pretty
awesome <a href="https://doc.rust-lang.org/book/title-page.html">Rust book</a> that is
probably a <a href="https://en.wikipedia.org/wiki/Gold_standard">gold standard</a> for
programming language documentation.</p>
<p>In the Rust book, there is a section on <a href="https://doc.rust-lang.org/book/ch08-02-strings.html">Storing UTF-8 Encoded Text with
Strings</a>. It contains a
neat example that I would like to use to show how
<a href="https://factorcode.org">Factor</a> string objects work, how we handle
<a href="https://home.unicode.org">Unicode</a> and other <a href="https://en.wikipedia.org/wiki/Character_encoding">character
encodings</a>, and show how
we probably can make some improvements in the future. At the time of this
blog post, we support Unicode 15.0.0 which was <a href="http://blog.unicode.org/2022/09/announcing-unicode-standard-version-150.html">released in September
2022</a>.</p>
<p>Factor <a href="https://docs.factorcode.org/content/article-strings.html">strings</a>
are a sequence of Unicode <a href="https://en.wikipedia.org/wiki/Code_point">code
points</a> which we explore to see
how they work.</p>
<h3 id="bytes">Bytes</h3>
<blockquote>
<p>If we look at the Hindi word “नमस्ते” written in the
Devanagari script, it is stored as a vector of <code>u8</code> values that looks
like this:</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"नमस्ते"</span> utf8 encode <span class="m">.
</span></span></span><span class="line"><span class="cl">B{
</span></span><span class="line"><span class="cl"> <span class="m">224 164 168 224 164 174 224 164 184 224 165 141 224 164 164
</span></span></span><span class="line"><span class="cl"> <span class="m">224 165 135
</span></span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Or, as a series of hex values:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"नमस्ते"</span> utf8 encode .h
</span></span><span class="line"><span class="cl">B{
</span></span><span class="line"><span class="cl"> <span class="m">0xe0 0xa4 0xa8 0xe0 0xa4 0xae 0xe0 0xa4 0xb8 0xe0 0xa5 0x8d
</span></span></span><span class="line"><span class="cl"> <span class="m">0xe0 0xa4 0xa4 0xe0 0xa5 0x87
</span></span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>You could instead print them as
<a href="https://docs.factorcode.org/content/word-.o%2Cprettyprint.html">octal</a> or
<a href="https://docs.factorcode.org/content/word-.b%2Cprettyprint.html">binary</a> quite
easily.</p>
<h3 id="code-points">Code Points</h3>
<blockquote>
<p>That’s 18 bytes and is how computers ultimately store this data. If we
look at them as Unicode scalar values, which are what Rust’s <code>char</code>
type is, those bytes look like this:</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"नमस्ते"</span> [ <span class="nb">1string </span>] { } <span class="nb">map-as </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"न"</span> <span class="s">"म"</span> <span class="s">"स"</span> <span class="s">"्"</span> <span class="s">"त"</span> <span class="s">"े"</span> }
</span></span></code></pre></div><p>You can see what the code point numeric values are:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"नमस्ते"</span> <span class="nb">>array </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">2344 2350 2360 2381 2340 2375 </span>}
</span></span></code></pre></div><p>Or even see what the code point names are:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"नमस्ते"</span> [ char>name ] { } <span class="nb">map-as </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> <span class="s">"devanagari-letter-na"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"devanagari-letter-ma"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"devanagari-letter-sa"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"devanagari-sign-virama"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"devanagari-letter-ta"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"devanagari-vowel-sign-e"</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><h3 id="characters">Characters</h3>
<blockquote>
<p>There are six char values here, but the fourth and sixth are not letters:
they’re diacritics that don’t make sense on their own. Finally, if we
look at them as grapheme clusters, we’d get what a person would call the
four letters that make up the Hindi word:</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"नमस्ते"</span> >graphemes [ <span class="nb">>string </span>] <span class="nb">map </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"न"</span> <span class="s">"म"</span> <span class="s">"स्"</span> <span class="s">"ते"</span> }
</span></span></code></pre></div><p>These graphemes are code points grouped like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"नमस्ते"</span> >graphemes [ <span class="nb">>array </span>] <span class="nb">map </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ { <span class="m">2344 </span>} { <span class="m">2350 </span>} { <span class="m">2360 2381 </span>} { <span class="m">2340 2375 </span>} }
</span></span></code></pre></div><h3 id="encodings">Encodings</h3>
<blockquote>
<p>Rust provides different ways of interpreting the raw string data that
computers store so that each program can choose the interpretation it
needs, no matter what human language the data is in.</p>
</blockquote>
<p>Factor supports many
<a href="https://docs.factorcode.org/content/article-encodings-descriptors.html">encodings</a>
which can be used for interacting with other computer systems. These include
ASCII, many <a href="https://docs.factorcode.org/content/article-io.encodings.8-bit.html">legacy 8-bit
encodings</a>
(including MacRoman, EBCDIC, and others), other Unicode variants (such as
UTF-7, UTF-16, and UTF-32), ISO-2022, and several others.</p>
<p>There are a couple of space optimizations to save memory when only small
code points are used, which is common in English as well as formats such as
<a href="https://en.wikipedia.org/wiki/Base64">Base64</a>. Looking at the Rust standard
library, the improvements made to the <a href="https://docs.python.org/3/howto/unicode.html">Python unicode
support</a>, or other languages
such as <a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/stringsandcharacters/">Strings and Characters in
Swift</a>,
there are likely improvements we can make when working with text in
<a href="https://factorcode.org">Factor</a>.</p>
Color Picker Game
https://re.factorcode.org/2023/05/color-picker-game.html
Wed, 03 May 2023 09:00:00 -0700https://re.factorcode.org/2023/05/color-picker-game.html<p>In the <a href="https://www.kodeco.com/books/swiftui-by-tutorials">SwiftUI by
Tutorials</a> book by Ray
Wenderlich, there is a <a href="https://www.kodeco.com/books/swiftui-by-tutorials/v2.0/chapters/2-getting-started">tutorial on building
RGBullsEye</a>,
which is a game for adjusting <a href="https://www.w3schools.com/colors/colors_rgb.asp">RGB
Colors</a> using sliders to
match a provided random color and providing a “color score” to the user
showing how well they matched it. Some users have even <a href="https://github.com/search?q=rgbullseye&type=repositories">posted their
solutions</a> on
GitHub.</p>
<p>I thought it would be fun to build a version of this example using
<a href="https://factorcode.org">Factor</a>.</p>
<p>
<img src="https://re.factorcode.org/images/2023-05-03-color-picker-game.png" alt="" width="633" height="547" />
</p>
<p>We could generate a
<a href="https://docs.factorcode.org/content/article-colors.protocol.html">color</a> by
using
<a href="https://docs.factorcode.org/content/word-random-unit,random.html">random-unit</a>
to make three random values for the <code>red</code>, <code>green</code>, and <code>blue</code> slots. Instead, we can pick randomly from the <a href="https://docs.factorcode.org/content/article-colors.constants.html">standard color database</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-color</span> <span class="nf">( -- </span><span class="nv">color</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> named-colors random named-color <span class="k">;
</span></span></span></code></pre></div><p>Comparing two colors can use the
<a href="https://docs.factorcode.org/content/word-rgba-distance%2Ccolors.distances.html">rgba-distance</a>
word from the <a href="https://docs.factorcode.org/content/vocab-colors.distances.html">colors.distances
vocabulary</a>,
returning an integer score out of 100 points:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">color-score</span> <span class="nf">( </span><span class="nv">color1</span> <span class="nv">color2</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> rgba-distance <span class="m">1.0 </span><span class="nb">swap - </span><span class="m">100.0 </span><span class="nb">* </span>round <span class="nb">>integer </span><span class="k">;
</span></span></span></code></pre></div><p>We can define a <a href="https://docs.factorcode.org/content/word-gadget,ui.gadgets.html">gadget
type</a> that
can be used to find our object in a <a href="https://docs.factorcode.org/content/article-ui-layouts.html">gadget
hierarchy</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">color-picker-game</span> < <span class="nc">track</span> <span class="k">;
</span></span></span></code></pre></div><p>Given a child of the <code>color-picker-game</code> instance, we can pull out the
<code>color-preview</code> gadgets in a slightly fragile way by knowing where they
are in the layout:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-color-previews</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- </span><span class="nv">preview1</span> <span class="nv">preview2</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ color-picker-game? ] find-parent
</span></span><span class="line"><span class="cl"> children>> <span class="nb">first </span>children>> <span class="nb">first2 </span><span class="k">;
</span></span></span></code></pre></div><p>Using that, we can make a button that, when clicked:</p>
<ol>
<li>finds the two <code>color-preview</code> objects</li>
<li>grabs the latest color value from their
<a href="https://docs.factorcode.org/content/article-models.html">models</a></li>
<li>calculates the “color score”</li>
<li>displays it by modifying the button text</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><match-button></span> <span class="nf">( -- </span><span class="nv">button</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"Match Color"</span> [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>find-color-previews
</span></span><span class="line"><span class="cl"> [ model>> compute-model ] <span class="nb">bi@
</span></span></span><span class="line"><span class="cl"> color-score <span class="s">"Your score: %d"</span> sprintf
</span></span><span class="line"><span class="cl"> <span class="nb">over </span>children>> <span class="nb">first </span>text<< relayout
</span></span><span class="line"><span class="cl"> ] <border-button> <span class="k">;
</span></span></span></code></pre></div><p>Another button can be used to reset the color we are trying to match against
to a new random color, setting it on the model used by the left
<code>color-preview</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><reset-button></span> <span class="nf">( -- </span><span class="nv">button</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"Random"</span> [
</span></span><span class="line"><span class="cl"> find-color-previews <span class="nb">drop </span>model>>
</span></span><span class="line"><span class="cl"> random-color <span class="nb">swap </span>set-model
</span></span><span class="line"><span class="cl"> ] <border-button> <span class="k">;
</span></span></span></code></pre></div><p>Using these two buttons, and some gadgets from the <a href="https://docs.factorcode.org/content/vocab-color-picker.html">color picker
vocabulary</a>, we
can build up our interface, choosing a random color to start, and then
laying out the other components we need:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf"><color-picker-game></span> <span class="nf">( -- </span><span class="nv">gadget</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> vertical color-picker-game new-track { <span class="m">5 5 </span>} >>gap
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> random-color <model> :> left-model
</span></span><span class="line"><span class="cl"> <span class="no">\ <rgba></span> <color-sliders> :> <span class="nf">( </span><span class="nv">sliders</span> <span class="nv">right-model</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> horizontal <track>
</span></span><span class="line"><span class="cl"> left-model <color-preview> 1/2 track-add
</span></span><span class="line"><span class="cl"> right-model <color-preview> 1/2 track-add
</span></span><span class="line"><span class="cl"> <span class="m">1 </span>track-add
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> sliders <span class="no">f </span>track-add
</span></span><span class="line"><span class="cl"> right-model <color-status> <span class="no">f </span>track-add
</span></span><span class="line"><span class="cl"> <match-button> <span class="no">f </span>track-add
</span></span><span class="line"><span class="cl"> <reset-button> <span class="no">f </span>track-add <span class="k">;
</span></span></span></code></pre></div><p>We can make a <a href="https://docs.factorcode.org/content/word-MAIN-WINDOW__colon__,ui.html">main entry
point</a>,
constructing the game and providing it as the main gadget:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">MAIN-WINDOW: color-picker-game-window
</span></span><span class="line"><span class="cl"> { { title <span class="s">"Color Picker Game"</span> } }
</span></span><span class="line"><span class="cl"> <color-picker-game> >>gadgets <span class="k">;
</span></span></span></code></pre></div><p>This is available in the <a href="https://github.com/factor/factor/blob/master/extra/color-picker-game/color-picker-game.factor">development
version</a>
and includes some additional features such as support for additional <a href="https://en.wikipedia.org/wiki/Color_space">color
spaces</a> along with some
improvements to our <a href="https://docs.factorcode.org/content/vocab-ui.gadgets.tabbed.html">tabbed
gadgets</a>.
Give it a try!</p>
OpenAI
https://re.factorcode.org/2023/04/openai.html
Tue, 25 Apr 2023 00:15:00 -0800https://re.factorcode.org/2023/04/openai.html<p>It’s been pretty hard to avoid all of the incredible stories about
<a href="https://en.wikipedia.org/wiki/Artificial_intelligence">artificial
intelligence</a> over
the past few months. There seem to be incredible applications to the area of
<a href="https://www.mckinsey.com/featured-insights/mckinsey-explainers/what-is-generative-ai">generative
AI</a>
occurring on a daily basis. Image generation with
<a href="http://midjourney.com">Midjourney</a> is pretty next-level. Code generation
using <a href="https://github.com/features/preview/copilot-x">GitHub Copilot</a> seems
pretty amazing. Interacting with large language models like
<a href="https://openai.com/product/gpt-4">GPT-4</a> or <a href="https://bard.google.com">Bard</a>
or <a href="https://www.bing.com/new">Bing Chat</a> or <a href="https://ai.facebook.com/blog/large-language-model-llama-meta-ai/">Facebook
LLaMA</a> or
<a href="https://stability.ai/blog/stability-ai-launches-the-first-of-its-stablelm-suite-of-language-models">StableLM</a>
and so many others seems like science fiction. Audio models like
<a href="https://openai.com/research/whisper">Whisper</a> used for audio transcription
even make popular audio assistants look pretty dated.</p>
<p>With all of the hype, it seemed inevitable that
<a href="https://factorcode.org">Factor</a> would gain some kind of AI functionality.</p>
<p>Despite the <a href="https://hackernoon.com/how-openai-transitioned-from-a-nonprofit-to-a-$29b-for-profit-company#">non-profit vs for-profit controversy of
OpenAI</a>,
they do seem to have a momentary lead in the race to make tools that others
can build upon. One of those tools is the <a href="https://platform.openai.com/docs/introduction">OpenAI
API</a>, which is made available
using <a href="https://restfulapi.net/introduction-to-json/">JSON</a> and
<a href="https://www.cloudflare.com/learning/ddos/glossary/hypertext-transfer-protocol-http">HTTP</a>.
Besides the <a href="https://platform.openai.com/docs/api-reference">OpenAI API
Reference</a>, there is an
<a href="https://github.com/openai/openai-cookbook/">OpenAI Cookbook</a> and popular
libraries such as <a href="https://github.com/openai/openai-python">OpenAI Python</a>
for building systems using it.</p>
<p>Recently, I contributed the <a href="https://github.com/factor/factor/blob/master/extra/openai/openai.factor">openai
vocabulary</a>
which allows using all the methods currently made available by
<a href="https://openai.com">OpenAI</a>. You will need an <a href="https://platform.openai.com/account/api-keys">OpenAI API
Key</a>.</p>
<p>Once you obtain that, you can set it in <a href="https://docs.factorcode.org/content/article-listener.html">the
listener</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">openai</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"sk-....................."</span> openai-api-key <span class="nb">set-global
</span></span></span></code></pre></div><p>And now you have enough to try it out…</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"what is the factor programming language"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"text-davinci-003"</span> <completion>
</span></span><span class="line"><span class="cl"> <span class="m">100 </span>>>max_tokens create-completion
</span></span><span class="line"><span class="cl"> <span class="s">"choices"</span> <span class="nb">of first </span><span class="s">"text"</span> <span class="nb">of print
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Factor is a stack-oriented programming language, designed for creating
</span></span><span class="line"><span class="cl">flexible, reusable software components. It combines elements from both
</span></span><span class="line"><span class="cl">object-oriented <span class="nb">and </span>functional programming, <span class="nb">and </span>provides powerful features,
</span></span><span class="line"><span class="cl">including static typing <span class="nb">and </span>static type checking, an interactive program
</span></span><span class="line"><span class="cl">development environment, built-in automated testing, <span class="nb">and </span>a wide range <span class="nb">of
</span></span></span><span class="line"><span class="cl">built-in data types. The language is designed to be easy to use, yet provide
</span></span><span class="line"><span class="cl">a high degree <span class="nb">of </span>flexibility.
</span></span></code></pre></div><p>Cool!</p>
<p>We even have a <a href="https://github.com/factor/factor/blob/master/extra/discord/chatgpt-bot/chatgpt-bot.factor">Discord bot using
OpenAI</a>
that answers on the <a href="https://discord.gg/QxJYZx3QDf">Factor Discord server</a>.</p>
ASCII Table PDF
https://re.factorcode.org/2023/03/ascii-table-pdf.html
Tue, 07 Mar 2023 09:00:00 -0700https://re.factorcode.org/2023/03/ascii-table-pdf.html<p><a href="https://jugad2.blogspot.com/p/about-vasudev-ram.html">Vasudev Ram</a> has a
blog with many different posts about various programming topics including
Python, Linux, SQL, and PDFs. On the topic of PDF generation, they have a
blog post about making an <a href="https://jugad2.blogspot.com/2015/03/ascii-table-to-pdf-with-xtopdf.html">ASCII Table to PDF with
xtopdf</a>.</p>
<blockquote>
<p>Recently, I had the need for an ASCII table lookup, which I searched for and
found, thanks to the folks here:</p>
<p><a href="https://www.ascii-code.com">www.ascii-code.com</a></p>
<p>That gave me the idea of writing a simple program to generate an ASCII table
in PDF. Here is the code for a part of that table - the first 32 (0 to 31)
ASCII characters, which are the control characters:</p>
</blockquote>
<p>It might not be widely known, but <a href="https://factorcode.org">Factor</a> has built-in
support for writing to <a href="https://docs.factorcode.org/content/vocab-pdf.streams.html">PDF
Streams</a> using the
<a href="https://docs.factorcode.org/content/article-io.styles.html">formatted output</a>
protocol. This supports <a href="https://docs.factorcode.org/content/article-styles.html">text
styles</a> including
changing font names, bold and italic styles, foreground and background colors,
etc.</p>
<p>We start by defining the symbols and descriptions of the first 32 ASCII
characters. These are all non-printable <a href="https://en.wikipedia.org/wiki/Control_character">control
character</a>, which is why we
use this array of strings to render them in a table.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">ASCII</span> {
</span></span><span class="line"><span class="cl"> <span class="s">"NUL Null char"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"SOH Start of Heading"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"STX Start of Text"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"ETX End of Text"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"EOT End of Transmission"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"ENQ Enquiry"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"ACK Acknowledgment"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"BEL Bell"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"BS Back Space"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"HT Horizontal Tab"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"LF Line Feed"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"VT Vertical Tab"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"FF Form Feed"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"CR Carriage Return"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"SO Shift Out / X-On"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"SI Shift In / X-Off"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"DLE Data Line Escape"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"DC1 Device Control 1 (oft. XON)"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"DC2 Device Control 2"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"DC3 Device Control 3 (oft. XOFF)"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"DC4 Device Control 4"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"NAK Negative Acknowledgement"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"SYN Synchronous Idle"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"ETB End of Transmit Block"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"CAN Cancel"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"EM End of Medium"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"SUB Substitute"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"ESC Escape"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"FS File Separator"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"GS Group Separator"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"RS Record Separator"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"US Unit Separator"</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>The core <em>printing</em> logic is a header, followed by rows for each character,
formatted into a table of decimal, octal, hexadecimal, and binary values
along with their symbol and description from the array above:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">ascii.</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"ASCII Control Characters - 0 to 31"</span> <span class="nb">print nl
</span></span></span><span class="line"><span class="cl"> ASCII [
</span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="nb">+ swap </span>[
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ >dec ]
</span></span><span class="line"><span class="cl"> [ >oct <span class="m">3 </span><span class="sc">CHAR: 0 </span><span class="nb">pad-head </span>]
</span></span><span class="line"><span class="cl"> [ >hex <span class="m">2 </span><span class="sc">CHAR: 0 </span><span class="nb">pad-head </span>]
</span></span><span class="line"><span class="cl"> [ >bin <span class="m">8 </span><span class="sc">CHAR: 0 </span><span class="nb">pad-head </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">dip </span><span class="s">" "</span> split1 <span class="m">6 </span>narray
</span></span><span class="line"><span class="cl"> ] <span class="nb">map-index </span>{
</span></span><span class="line"><span class="cl"> <span class="s">"DEC"</span> <span class="s">"OCT"</span> <span class="s">"HEX"</span> <span class="s">"BIN"</span> <span class="s">"Symbol"</span> <span class="s">"Description"</span>
</span></span><span class="line"><span class="cl"> } <span class="nb">prefix </span>format-table <span class="nb">unclip
</span></span></span><span class="line"><span class="cl"> H{ { font-style bold } } format <span class="nb">nl
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">print </span>] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>Since the <a href="https://docs.factorcode.org/content/article-ui-listener.html">UI
listener</a>
supports formatted streams, you can see it from the listener:</p>
<p>
<img src="https://re.factorcode.org/images/2023-03-07-ascii-table-pdf.png" alt="" width="614" height="655" />
</p>
<p>Outputting this to a PDF file is now easy. We make sure to set the font to
<code>monospace</code> and then run <code>ascii.</code> with our <a href="https://docs.factorcode.org/content/word-with-pdf-writer,pdf.streams.html">PDF
writer</a>,
saving the generated PDF output into a file.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">ascii-pdf</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> H{ { font-name <span class="s">"monospace"</span> } } [ ascii. ] with-style
</span></span><span class="line"><span class="cl"> ] with-pdf-writer pdf>string <span class="nb">swap </span>utf8 set-file-contents <span class="k">;
</span></span></span></code></pre></div><p>We also support writing to <a href="https://docs.factorcode.org/content/article-html.streams.html">HTML
streams</a> in a
similar manner, so it would be pretty easy to create an <code>ascii-html</code> word to
output an HTML file with the same printing logic above but instead using our
<a href="https://docs.factorcode.org/content/word-with-html-writer,html.streams.html">HTML
writer</a>.</p>
Short UUID
https://re.factorcode.org/2023/03/shortuuid.html
Fri, 03 Mar 2023 09:00:00 -0800https://re.factorcode.org/2023/03/shortuuid.html<p>The <a href="https://github.com/skorokithakis/shortuuid">shortuuid</a> project is a
“<em>simple python library that generates concise, unambiguous, URL-safe UUIDs</em>”.
I thought it would be a fun exercise to implement this in
<a href="https://factorcode.org">Factor</a>.</p>
<p>What is a “short UUID”?</p>
<p>You can read the <a href="https://web.archive.org/web/20130525172507/https://blog.stochastictechnologies.com/new-python-module-shortuuid">original
announcement</a>,
but basically it is a string representation of a number using a reduced
alphabet that can be used in places like URLs where conciseness is
desirable. The author mentions that it provides security by “<em>not divulging
information (such as how many rows there are in that particular table, the
time difference between one item and the next, etc.)</em>”. However, I think it
is more <a href="https://en.wikipedia.org/wiki/Security_through_obscurity">security through
obscurity</a> than
real security.</p>
<p>In any event, the alphabet used are these 57 characters:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">alphabet</span>
</span></span><span class="line"><span class="cl"><span class="s">"23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"</span>
</span></span></code></pre></div><p>We encode a numeric input by repeatedly “divmod”, indexing into an alphabet,
until exhausted.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">encode-uuid</span> <span class="nf">( </span><span class="nv">uuid</span> <span class="nf">-- </span><span class="nv">shortuuid</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span><span class="m">0 </span><span class="nb">> </span>] [
</span></span><span class="line"><span class="cl"> alphabet [ <span class="nb">length /mod </span>] [ <span class="nb">nth </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] <span class="s">""</span> <span class="nb">produce-as nip reverse </span><span class="k">;
</span></span></span></code></pre></div><p>We decode using a reverse process, looking up the position of each character
in the alphabet, re-assembling the numeric input for each character in the
<em>shortuuid</em>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">decode-uuid</span> <span class="nf">( </span><span class="nv">shortuuid</span> <span class="nf">-- </span><span class="nv">uuid</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>[
</span></span><span class="line"><span class="cl"> alphabet <span class="nb">index </span>[ alphabet <span class="nb">length * </span>] <span class="nb">dip +
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">reduce </span><span class="k">;
</span></span></span></code></pre></div><p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/shortuuid/shortuuid.factor">GitHub</a>,
including features to deal with <em>legacy</em> values generated before version
1.0.0 as well as supporting different alphabets being used.</p>
Geo Timezones
https://re.factorcode.org/2023/03/geo-timezones.html
Wed, 01 Mar 2023 09:00:00 -0700https://re.factorcode.org/2023/03/geo-timezones.html<p><a href="https://bradfitz.com">Brad Fitzpatrick</a> wrote a <a href="https://github.com/bradfitz/latlong">Go package called
latlong</a> which efficiently maps a
latitude/longitude to a timezone. The <a href="https://plus.google.com/u/0/+BradFitzpatrick/posts/XVyy1bAzkZd">original
post</a>
describing it was on <a href="https://plus.google.com">Google+</a> and is likely lost
forever – unless it made it into the <a href="https://wiki.archiveteam.org/index.php/Google%2B">Google+
archive</a> before Google+
joined the <a href="https://killedbygoogle.com">Google Graveyard</a>.</p>
<blockquote>
<p>It tries to have a small binary size (~360 KB), low memory footprint
(~1 MB), and incredibly fast lookups (~0.5 microseconds). It does not
try to be perfectly accurate when very close to borders.</p>
</blockquote>
<p>It’s available in other languages, too!</p>
<p><a href="https://huonw.github.io">Huon Wilson</a> ported the library to the <a href="https://www.rust-lang.org">Rust
Programming Language</a>, making the code <a href="https://github.com/huonw/tz-search">available
on GitHub</a> and <a href="https://crates.io/crates/tz-search">installable via
Cargo</a>. There is even a wrapper made for
<a href="https://nodejs.org/">NodeJs</a> that is <a href="https://www.npmjs.com/package/node-latlong">installable via
NPM</a> that uses a <a href="https://github.com/Ecube-Labs/latlong-cli">command-line
executable written in Go</a>.</p>
<p>When it was announced in 2015, I had ported the library to
<a href="https://factorcode.org">Factor</a>, but missed the opportunity to blog about
it. Below we discuss some details about the implementation, starting with
its use of a <a href="http://efele.net/maps/tz/world/">shapefile of the TZ timezones of the
world</a> to divide the world into zones that
are assigned timezone values – looking something like this:</p>
<p>
<img src="https://re.factorcode.org/images/2023-03-01-tzsearch-Rt8bLSD.png" alt="" width="472" height="378" />
</p>
<p>The world is divided into 6 <em>zoom levels</em> of tiles (represented by a key and
an index value) that allow us to search from a very large area first, then
down to the more specific geographic area. Note: we represent the struct as
a <a href="https://en.wikipedia.org/wiki/Endianness">big endian</a> struct with
<a href="http://www.catb.org/esr/structure-packing/">structure packing</a> to minimize
wasted space in the files.</p>
<p>The <em>zoom levels</em> are then cached using <a href="https://docs.factorcode.org/content/article-literals.html">literal
syntax</a> into a
<code>zoom-levels</code> constant.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">BE-PACKED-STRUCT: tile
</span></span><span class="line"><span class="cl"> { key uint }
</span></span><span class="line"><span class="cl"> { idx ushort } <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">SPECIALIZED-ARRAY: tile
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">zoom-levels</span> $[
</span></span><span class="line"><span class="cl"> <span class="m">6 </span><iota> [
</span></span><span class="line"><span class="cl"> number>string
</span></span><span class="line"><span class="cl"> <span class="s">"vocab:geo-tz/zoom"</span> <span class="s">".dat"</span> <span class="nb">surround
</span></span></span><span class="line"><span class="cl"> binary file-contents tile cast-array
</span></span><span class="line"><span class="cl"> ] <span class="nb">map
</span></span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><p>Each of the <em>zoom levels</em> reference indexes into a <em>leaves</em> data structure
that contains 14,110 items – each represented by one of three data types:</p>
<ol>
<li>Type <code>S</code> is a <em>string</em>.</li>
<li>Type <code>2</code> is a <em>one bit tile</em>.</li>
<li>Type <code>P</code> is a <em>pixmap</em> thats 128 bytes long.</li>
</ol>
<p>These we load and cache into a <code>unique-leaves</code> constant.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">#leaves</span> <span class="m">14110
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">BE-PACKED-STRUCT: one-bit-tile
</span></span><span class="line"><span class="cl"> { idx0 ushort }
</span></span><span class="line"><span class="cl"> { idx1 ushort }
</span></span><span class="line"><span class="cl"> { bits ulonglong } <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">unique-leaves</span> $[
</span></span><span class="line"><span class="cl"> <span class="s">"vocab:geo-tz/leaves.dat"</span> binary [
</span></span><span class="line"><span class="cl"> #leaves [
</span></span><span class="line"><span class="cl"> <span class="nb">read1 </span>{
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: S </span>[ { <span class="m">0 </span>} <span class="nb">read-until drop </span>utf8 decode ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: 2 </span>[ one-bit-tile read-struct ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: P </span>[ <span class="m">128 </span><span class="nb">read </span>] }
</span></span><span class="line"><span class="cl"> } <span class="nb">case
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">replicate
</span></span></span><span class="line"><span class="cl"> ] with-file-reader
</span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><p>The core logic involves looking up a leaf (which is one of three types,
loaded above), given an <code>(x, y)</code> coordinate. If it is a <code>string</code> type,
we are done. If it is a <code>one-bit-tile</code>, we defer to the appropriate leaf
specified by <code>idx0</code> or <code>idx1</code>. And if it is pixmap, we have a smidge
more logic to detect oceans or defer again to a different leaf.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">ocean-index</span> <span class="m">0xffff
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">GENERIC#: lookup-leaf <span class="m">2 </span><span class="nf">( </span><span class="nv">leaf</span> <span class="nv">x</span> <span class="nv">y</span> <span class="nf">-- </span><span class="nv">zone/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">string</span> <span class="nf">lookup-leaf</span> <span class="nb">2drop </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M::</span> <span class="nc">one-bit-tile</span> <span class="nf">lookup-leaf</span> <span class="nf">( </span><span class="nv">leaf</span> <span class="nv">x</span> <span class="nv">y</span> <span class="nf">-- </span><span class="nv">zone/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> leaf bits>> y <span class="m">3 </span>bits <span class="m">3 </span><span class="nb">shift </span>x <span class="m">3 </span>bits <span class="nb">bitor bit?
</span></span></span><span class="line"><span class="cl"> [ leaf idx1>> ] [ leaf idx0>> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> unique-leaves <span class="nb">nth </span>x y lookup-leaf <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M::</span> <span class="nc">byte-array</span> <span class="nf">lookup-leaf</span> <span class="nf">( </span><span class="nv">leaf</span> <span class="nv">x</span> <span class="nv">y</span> <span class="nf">-- </span><span class="nv">zone/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> y <span class="m">3 </span>bits <span class="m">3 </span><span class="nb">shift </span>x <span class="m">3 </span>bits <span class="nb">bitor </span><span class="m">2 </span><span class="nb">* </span>:> i
</span></span><span class="line"><span class="cl"> i leaf <span class="nb">nth </span><span class="m">8 </span><span class="nb">shift </span>i <span class="m">1 </span><span class="nb">+ </span>leaf <span class="nb">nth +
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>ocean-index <span class="nb">= </span>[ <span class="nb">drop </span><span class="no">f </span>] [
</span></span><span class="line"><span class="cl"> unique-leaves <span class="nb">nth </span>x y lookup-leaf
</span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>We’re almost done! Given a zoom level, a <code>tile-key</code> helps us find a
specific tile that we then can lookup the leaf for, hopefully finding the
<code>timezone</code> associated with the coordinate.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">lookup-zoom-level</span> <span class="nf">( </span><span class="nv">zoom-level</span> <span class="nv">x</span> <span class="nv">y</span> <span class="nv">tile-key</span> <span class="nf">-- </span><span class="nv">zone/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> zoom-level [ key>> tile-key >=< ] search <span class="nb">swap </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>key>> tile-key <span class="nb">= </span>[
</span></span><span class="line"><span class="cl"> idx>> unique-leaves <span class="nb">nth </span>x y lookup-leaf
</span></span><span class="line"><span class="cl"> ] [ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] [ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Each coordinate is effectively a
<a href="https://en.wikipedia.org/wiki/Pixel">pixel</a> in the image, so our logic
searches from the outermost <em>zoom level</em> to the innermost, trying to lookup
a timezone in each one using the coordinate and level as a <code>tile-key</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">tile-key</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nv">level</span> <span class="nf">-- </span><span class="nv">tile-key</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> level <span class="nb">dup </span><span class="m">3 </span><span class="nb">+ neg </span>:> n
</span></span><span class="line"><span class="cl"> y x [ n <span class="nb">shift </span><span class="m">14 </span>bits ] <span class="nb">bi@
</span></span></span><span class="line"><span class="cl"> { <span class="m">0 14 28 </span>} bitfield <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">lookup-pixel</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nf">-- </span><span class="nv">zone</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">6 </span><iota> [| level |
</span></span><span class="line"><span class="cl"> level zoom-levels <span class="nb">nth
</span></span></span><span class="line"><span class="cl"> x y <span class="nb">2dup </span>level tile-key
</span></span><span class="line"><span class="cl"> lookup-zoom-level
</span></span><span class="line"><span class="cl"> ] <span class="nb">map-find-last drop </span><span class="k">;
</span></span></span></code></pre></div><p>Finally, we have enough to implement our public API, converting a given
latitude/longitude coordinate to a <em>pixel</em> value, deferring to the word we
just defined above to do the work.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">deg-pixels</span> <span class="m">32
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">lookup-zone</span> <span class="nf">( </span><span class="nv">lat</span> <span class="nv">lon</span> <span class="nf">-- </span><span class="nv">zone</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> lon <span class="m">180 </span><span class="nb">+ </span>deg-pixels <span class="nb">* </span><span class="m">0 360 </span>deg-pixels <span class="nb">* </span><span class="m">1 </span><span class="nb">- </span>clamp
</span></span><span class="line"><span class="cl"> <span class="m">90 </span>lat <span class="nb">- </span>deg-pixels <span class="nb">* </span><span class="m">0 180 </span>deg-pixels <span class="nb">* </span><span class="m">1 </span><span class="nb">- </span>clamp
</span></span><span class="line"><span class="cl"> [ <span class="nb">>integer </span>] <span class="nb">bi@ </span>lookup-pixel <span class="k">;
</span></span></span></code></pre></div><p>And then a couple of test cases to show it’s working:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="s">"America/Los_Angeles"</span> } [ <span class="m">37.7833 -122.4167 </span>lookup-zone ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="s">"Australia/Sydney"</span> } [ <span class="m">-33.8885 151.1908 </span>lookup-zone ] unit-test
</span></span></code></pre></div><p>Performance is pretty good, we can generate over 3 million lookups per
second, putting our cost per lookup around 0.33 microseconds. And all of
that in less than 70 lines of code.</p>
<p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/tree/master/geo-tz">GitHub</a>.</p>
Reference Server
https://re.factorcode.org/2023/02/reference-server.html
Tue, 28 Feb 2023 07:00:00 -0700https://re.factorcode.org/2023/02/reference-server.html<p><a href="https://eatonphil.com">Phil Eaton</a> made a repository of <a href="https://github.com/eatonphil/referenceserver">Barebones UNIX
socket servers</a> with this
description:</p>
<blockquote>
<p>I find myself writing this server in some language every few months. Each
time I have to scour the web for a good reference. Use this as a reference
to write your own bare server in C or other languages with a UNIX API
(Python, OCaml, etc).</p>
</blockquote>
<p>Many developers learning network programming will encounter <a href="https://beej.us/guide/bgnet/">Beej’s Guide to
Network Programming</a> which uses the sockets
API, has been ported to many platforms, and explains the intricacies of
making computers talk to each other in this manner.</p>
<h2 id="c">C</h2>
<p>We can take a look at his <a href="https://github.com/eatonphil/referenceserver/blob/master/c/server.c">C
implementation</a>
of a server that listens on port 15000, accepts client connections, reads up
to 1024 bytes which are printed to the screen, then writes <code>hello world</code>
back to the client and disconnects them:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf"><netinet/in.h></span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf"><stdio.h></span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf"><stdlib.h></span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf"><sys/socket.h></span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf"><unistd.h></span><span class="cp">
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">server</span><span class="p">,</span> <span class="n">client</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kt">socklen_t</span> <span class="n">addrlen</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">bufsize</span> <span class="o">=</span> <span class="mi">1024</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="o">*</span><span class="n">buffer</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="n">bufsize</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">sockaddr_in</span> <span class="n">address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">server</span> <span class="o">=</span> <span class="nf">socket</span><span class="p">(</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">SOCK_STREAM</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">address</span><span class="p">.</span><span class="n">sin_family</span> <span class="o">=</span> <span class="n">AF_INET</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">address</span><span class="p">.</span><span class="n">sin_addr</span><span class="p">.</span><span class="n">s_addr</span> <span class="o">=</span> <span class="n">INADDR_ANY</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">address</span><span class="p">.</span><span class="n">sin_port</span> <span class="o">=</span> <span class="nf">htons</span><span class="p">(</span><span class="mi">15000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nf">bind</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="p">(</span><span class="k">struct</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="p">)</span> <span class="o">&</span><span class="n">address</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">address</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">listen</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="n">client</span> <span class="o">=</span> <span class="nf">accept</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="p">(</span><span class="k">struct</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="p">)</span> <span class="o">&</span><span class="n">address</span><span class="p">,</span> <span class="o">&</span><span class="n">addrlen</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nf">recv</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">buffer</span><span class="p">,</span> <span class="n">bufsize</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">"%s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">buffer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nf">write</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="s">"hello world</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="mi">12</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nf">close</span><span class="p">(</span><span class="n">client</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nf">close</span><span class="p">(</span><span class="n">server</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="factor">Factor</h2>
<p>A direct <a href="https://factorcode.org">Factor</a> translation – without any error
checking, like in the original example – using the <a href="https://docs.factorcode.org/content/article-alien.html">C library
interface</a> might
look something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">alien.c-types</span> <span class="nn">alien.data</span> <span class="nn">byte-arrays</span>
</span></span><span class="line"><span class="cl"><span class="nn">classes.struct</span> <span class="nn">io</span> <span class="nn">io.encodings.string</span> <span class="nn">io.encodings.utf8</span> <span class="nn">kernel</span>
</span></span><span class="line"><span class="cl"><span class="nn">sequences</span> <span class="nn">unix.ffi</span> <span class="nn">unix.types</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">reference-server</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="m">1024 </span><byte-array> :> buffer
</span></span><span class="line"><span class="cl"> AF_INET SOCK_STREAM <span class="m">0 </span>socket :> server
</span></span><span class="line"><span class="cl"> sockaddr-in malloc-struct
</span></span><span class="line"><span class="cl"> AF_INET >>family
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>>>addr
</span></span><span class="line"><span class="cl"> <span class="m">15000 </span>htons >>port :> address
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> server address sockaddr-in heap-size bind <span class="nb">drop
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> server <span class="m">10 </span>listen <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> server address <span class="m">0 </span>socklen_t <ref> accept :> client
</span></span><span class="line"><span class="cl"> client buffer <span class="m">1024 0 </span>recv
</span></span><span class="line"><span class="cl"> buffer <span class="nb">swap head-slice </span>utf8 decode <span class="nb">print flush
</span></span></span><span class="line"><span class="cl"> client $[ <span class="s">"hello world\n"</span> >byte-array ]
</span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>unix.ffi:write <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> client close <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> <span class="no">t
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> server close <span class="nb">drop </span><span class="k">;
</span></span></span></code></pre></div><p>I noticed that some of his examples are more idiomatic to the language, so
we could rewrite this using <a href="https://docs.factorcode.org/content/article-io.servers.html">threaded
servers</a> –
gaining the benefit of working on Windows as well as error handling and
logging – using a handler
<a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a> to
implement the read/print/write/disconnect logic.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">io</span> <span class="nn">io.encodings.binary</span> <span class="nn">io.encodings.string</span>
</span></span><span class="line"><span class="cl"><span class="nn">io.encodings.utf8</span> <span class="nn">io.servers</span> <span class="nn">kernel</span> <span class="nn">namespaces</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">reference-server</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> binary <threaded-server>
</span></span><span class="line"><span class="cl"> <span class="m">15000 </span>>>insecure
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="m">1024 </span><span class="nb">read-partial </span>[
</span></span><span class="line"><span class="cl"> [ utf8 decode <span class="nb">print flush </span>] <span class="nb">with-global
</span></span></span><span class="line"><span class="cl"> $[ <span class="s">"hello world\n"</span> >byte-array ] <span class="nb">write flush
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">when*
</span></span></span><span class="line"><span class="cl"> ] >>handler
</span></span><span class="line"><span class="cl"> start-server wait-for-server <span class="k">;
</span></span></span></code></pre></div><p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/reference-server/reference-server.factor">GitHub</a>.</p>
Weighted Random
https://re.factorcode.org/2023/02/weighted-random.html
Sun, 26 Feb 2023 09:00:00 -0700https://re.factorcode.org/2023/02/weighted-random.html<p>Some time ago, I <a href="https://github.com/factor/factor/commit/5f4bf4513bad8bf6d6b71fa97bfaf29aae3a014d">implemented a way to generate weighted random
values</a>
from a discrete distribution in <a href="https://factorcode.org">Factor</a>. It ended
up being a pretty satisfyingly simple word that builds a cumulative probability table, generates a random probability, then searches the table to find which value to return:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">weighted-random</span> <span class="nf">( </span><span class="nv">histogram</span> <span class="nf">-- </span><span class="nv">obj</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">unzip </span>cum-sum [ <span class="nb">last >float </span>random ] <span class="nb">keep </span>bisect-left <span class="nb">swap nth </span><span class="k">;
</span></span></span></code></pre></div><h2 id="is-it-fast">Is It Fast?</h2>
<p>We can define a simple discrete distribution of values:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">dist</span> H{ { <span class="s">"A"</span> <span class="m">1 </span>} { <span class="s">"B"</span> <span class="m">2 </span>} { <span class="s">"C"</span> <span class="m">3 </span>} { <span class="s">"D"</span> <span class="m">4 </span>} }
</span></span></code></pre></div><p>And it seems to work – we can make a few random values from it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> dist weighted-random <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"C"</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> dist weighted-random <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"C"</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> dist weighted-random <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"D"</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> dist weighted-random <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"B"</span>
</span></span></code></pre></div><p>After generating a lot of random values, we can see the
<a href="https://docs.factorcode.org/content/word-histogram,math.statistics.html">histogram</a>
matches our distribution:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10,000,000 </span>[ dist weighted-random ] <span class="nb">replicate </span>histogram <span class="m">.
</span></span></span><span class="line"><span class="cl">H{
</span></span><span class="line"><span class="cl"> { <span class="s">"A"</span> <span class="m">998403 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"B"</span> <span class="m">2000400 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"C"</span> <span class="m">3001528 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"D"</span> <span class="m">3999669 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>But, how fast is it?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">10,000,000 </span>[ dist weighted-random ] <span class="nb">replicate </span>] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">3.02998325 </span>seconds
</span></span></code></pre></div><p>Okay, so it’s not <em>that</em> fast… generating around <strong>3.3 million</strong> per
second on one of my computers.</p>
<h2 id="improvements">Improvements</h2>
<p>We can make two quick improvements to this:</p>
<ol>
<li>First, we can factor out the initial step from the random number
generation.</li>
<li>Second, we can take advantage of a <a href="https://github.com/factor/factor/commit/930bc25ad350e83806e482d130de6a589cee886a">recent improvement to the random
vocabulary</a>,
mainly to change the <code>random</code> word that was previously implemented for
different types to instead get the current
<a href="https://docs.factorcode.org/content/word-random-generator%2Crandom.html">random-generator</a>
and then pass it to the <code>random*</code> implementation instead. This allows a
few speedups where we can lookup the <a href="https://docs.factorcode.org/content/article-namespaces.html">dynamic
variable</a> once
and then use it many times.</li>
</ol>
<p>That results in this definition:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">weighted-randoms</span> <span class="nf">( </span><span class="nv">length</span> <span class="nv">histogram</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">unzip </span>cum-sum <span class="nb">swap
</span></span></span><span class="line"><span class="cl"> [ [ <span class="nb">last >float </span>random-generator <span class="nb">get </span>] <span class="nb">keep </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> '[ _ _ random* _ bisect-left _ <span class="nb">nth </span>] <span class="nb">replicate </span><span class="k">;
</span></span></span></code></pre></div><p>That gives us a nice speedup, just over <strong>10 million</strong> per second:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">10,000,000 </span>dist weighted-randoms ] time histogram <span class="m">.
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">0.989039625 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">H{
</span></span><span class="line"><span class="cl"> { <span class="s">"A"</span> <span class="m">1000088 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"B"</span> <span class="m">1999445 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"C"</span> <span class="m">3000688 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"D"</span> <span class="m">3999779 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>That’s pretty nice, but it turns out that we can do better.</p>
<h2 id="vose-alias-method">Vose Alias Method</h2>
<p><a href="https://www.keithschwarz.com/me/">Keith Schwarz</a> wrote a fascinating blog
post about some better algorithms for <a href="https://www.keithschwarz.com/darts-dice-coins/">sampling from a discrete
distribution</a>. One of those
algorithms is the <a href="https://en.wikipedia.org/wiki/Alias_method">Vose Alias
Method</a> which creates a data
structure of items, probabilities, and an alias table that is used to return
an alternate choice:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">vose</span>
</span></span><span class="line"><span class="cl"> { <span class="nv">n</span> integer }
</span></span><span class="line"><span class="cl"> { <span class="nv">items</span> array }
</span></span><span class="line"><span class="cl"> { <span class="nv">probs</span> array }
</span></span><span class="line"><span class="cl"> { <span class="nv">alias</span> array } <span class="k">;
</span></span></span></code></pre></div><p>We construct a <code>vose</code> tuple by splitting the distribution into items and
their probabilities, and then processing the probabilities into lists of
<code>small</code> (less than 1) or <code>large</code> (greater than or equal to 1),
iteratively aliasing the index of smaller items to larger items.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf"><vose></span> <span class="nf">( </span><span class="nv">dist</span> <span class="nf">-- </span><span class="nv">vose</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> V{ } <span class="nb">clone </span>:> small
</span></span><span class="line"><span class="cl"> V{ } <span class="nb">clone </span>:> large
</span></span><span class="line"><span class="cl"> dist <span class="nb">assoc-size </span>:> n
</span></span><span class="line"><span class="cl"> n <span class="no">f </span><span class="nb"><array> </span>:> alias
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> dist <span class="nb">unzip dup </span>[ <span class="nb">length </span>] [ <span class="nb">sum </span>] <span class="nb">bi / </span>v*n :> <span class="nf">( </span><span class="nv">items</span> <span class="nv">probs</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> probs [ <span class="nb">swap </span><span class="m">1 </span><span class="nb">< </span>small large <span class="nb">? push </span>] <span class="nb">each-index
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ small <span class="nb">empty? not </span>large <span class="nb">empty? not and </span>] [
</span></span><span class="line"><span class="cl"> small <span class="nb">pop </span>:> s
</span></span><span class="line"><span class="cl"> large <span class="nb">pop </span>:> l
</span></span><span class="line"><span class="cl"> l s alias <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> l <span class="nb">dup </span>probs [ s probs <span class="nb">nth + </span><span class="m">1 </span><span class="nb">- dup </span>] <span class="nb">change-nth
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="nb">< </span>small large <span class="nb">? push
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="m">1 </span>large [ probs <span class="nb">set-nth </span>] <span class="nb">with each
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span>small [ probs <span class="nb">set-nth </span>] <span class="nb">with each
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> n items probs alias vose <span class="nb">boa </span><span class="k">;
</span></span></span></code></pre></div><p>We can implement the <code>random*</code> generic to select a random item from the
<code>vose</code> tuple – choosing a random item index, check it’s probability
against a random number between 0.0 and 1.0, and if it is over a threshold
we return the aliased item instead:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M::</span> <span class="nc">vose</span> <span class="nf">random*</span> <span class="nf">( </span><span class="nv">obj</span> <span class="nv">rnd</span> <span class="nf">-- </span><span class="nv">elt</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> obj n>> rnd random*
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>obj probs>> <span class="nb">nth </span>rnd (random-unit) <span class="nb">>=
</span></span></span><span class="line"><span class="cl"> [ obj alias>> <span class="nb">nth </span>] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> obj items>> <span class="nb">nth </span><span class="k">;
</span></span></span></code></pre></div><p>It’s much faster, over <strong>14.4 million</strong> per second:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">10,000,000 </span>dist <vose> randoms ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.693588458 </span>seconds
</span></span></code></pre></div><p>This is available now in the
<a href="https://docs.factorcode.org/content/vocab-math.extras.html">math.extras</a>
vocabulary in the current development version, along with a few tweaks that
brings the performance over <strong>21.7 million</strong> per second.</p>
DuckDuckGo
https://re.factorcode.org/2023/02/duckduckgo.html
Wed, 15 Feb 2023 09:00:00 -0700https://re.factorcode.org/2023/02/duckduckgo.html<p>The conversation around the current quality of web search engines, the
doomsday prediction about various incumbents, and the <a href="https://dkb.blog/p/bing-ai-cant-be-trusted">equal parts inspiring
and challenging rollout</a> of
large language models to improve search has been fascinating to watch. There
are many challengers in the search engine space including companies like
<a href="https://kagi.com/">Kagi</a> and <a href="https://neeva.com/">Neeva</a> among many <a href="https://www.crunchbase.com/hub/search-engine-startups">search
engine startups</a>.
One privacy-focused startup that has been fun to follow for awhile has been
<a href="https://duckduckgo.com/">DuckDuckGo</a>.</p>
<p>You can see an <a href="http://api.duckduckgo.com/?q=DuckDuckGo&format=json&pretty=1">example of the DuckDuckGo
API</a> that is
available on <code>api.duckduckgo.com</code>. This does not provide access to their
full search results, but instead provides access to their instant answers.
Regardless, I thought it would be neat if we could use this from
<a href="https://factorcode.org/">Factor</a>.</p>
<p>We can take a search query and turn it into a <a href="https://docs.factorcode.org/content/article-urls.html">URL
object</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">duckduckgo-url</span> <span class="nf">( </span><span class="nv">query</span> <span class="nf">-- </span><span class="nv">url</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">URL" http://api.duckduckgo.com"</span>
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span><span class="s">"q"</span> set-query-param
</span></span><span class="line"><span class="cl"> <span class="s">"json"</span> <span class="s">"format"</span> set-query-param
</span></span><span class="line"><span class="cl"> <span class="s">"1"</span> <span class="s">"pretty"</span> set-query-param
</span></span><span class="line"><span class="cl"> <span class="s">"1"</span> <span class="s">"no_redirect"</span> set-query-param
</span></span><span class="line"><span class="cl"> <span class="s">"1"</span> <span class="s">"no_html"</span> set-query-param
</span></span><span class="line"><span class="cl"> <span class="s">"1"</span> <span class="s">"skip_disambig"</span> set-query-param <span class="k">;
</span></span></span></code></pre></div><p>Using the
<a href="https://docs.factorcode.org/content/article-http.client.html">http.client</a>
vocabulary and the
<a href="https://docs.factorcode.org/content/article-json.html">json</a> vocabulary we
can retrieve a result set:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">duckduckgo</span> <span class="nf">( </span><span class="nv">query</span> <span class="nf">-- </span><span class="nv">results</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> duckduckgo-url http-get <span class="nb">nip </span>utf8 decode json> <span class="k">;
</span></span></span></code></pre></div><p>We can make a word that prints out the <em>abstract</em> response with clickable links:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">abstract.</span> <span class="nf">( </span><span class="nv">results</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"Heading"</span> <span class="nb">of </span>[ <span class="nb">drop </span>] [
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span>{
</span></span><span class="line"><span class="cl"> [ <span class="s">"AbstractURL"</span> <span class="nb">of </span>>url write-object <span class="nb">nl </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"AbstractText"</span> <span class="nb">of print </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"AbstractSource"</span> <span class="nb">of </span><span class="s">"- "</span> <span class="nb">write print </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave nl
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-empty </span><span class="k">;
</span></span></span></code></pre></div><p>And then a word that prints out a <em>result</em> response, parsing the
<a href="https://en.wikipedia.org/wiki/HTML">HTML</a> using the
<a href="https://docs.factorcode.org/content/vocab-html.parser.html">html.parser</a>
vocabulary and output as text using the
<a href="https://docs.factorcode.org/content/vocab-html.parser.printer.html">html.parser.printer</a>
vocabulary:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">result.</span> <span class="nf">( </span><span class="nv">result</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"Result"</span> <span class="nb">of </span>[
</span></span><span class="line"><span class="cl"> <span class="s">"<a href=\""</span> ?head <span class="nb">drop </span><span class="s">"\">"</span> split1 <span class="s">"</a>"</span> split1
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap </span>>url write-object ]
</span></span><span class="line"><span class="cl"> [ parse-html html-text. <span class="nb">nl </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">when* </span><span class="k">;
</span></span></span></code></pre></div><p>There are more aspects to the response from the API, but we can initially
print out the abstract, the results, and the related topics:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">duckduckgo.</span> <span class="nf">( </span><span class="nv">query</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> duckduckgo {
</span></span><span class="line"><span class="cl"> [ abstract. ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"Results"</span> <span class="nb">of </span>[ result. ] <span class="nb">each </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"RelatedTopics"</span> <span class="nb">of </span>[ result. ] <span class="nb">each </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span><span class="k">;
</span></span></span></code></pre></div><p>We can try it out on a topic that this particular blog likes to discuss:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"factorcode"</span> duckduckgo.
</span></span><span class="line"><span class="cl">Factor (programming language)
</span></span><span class="line"><span class="cl">Factor is a stack-oriented programming language created by Slava
</span></span><span class="line"><span class="cl">Pestov. Factor is dynamically typed <span class="nb">and </span>has automatic memory
</span></span><span class="line"><span class="cl">management, as well as powerful metaprogramming features. The
</span></span><span class="line"><span class="cl">language has a single implementation featuring a self-hosted
</span></span><span class="line"><span class="cl">optimizing compiler <span class="nb">and </span>an interactive development environment.
</span></span><span class="line"><span class="cl">The Factor distribution includes a large standard library.
</span></span><span class="line"><span class="cl"><span class="nb">- </span>Wikipedia
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Official site <span class="nb">- </span>Factor (programming language)
</span></span><span class="line"><span class="cl">Concatenative programming languages
</span></span><span class="line"><span class="cl">Stack-oriented programming languages
</span></span><span class="line"><span class="cl">Extensible syntax programming languages
</span></span><span class="line"><span class="cl">Function-level languages
</span></span><span class="line"><span class="cl">High-level programming languages
</span></span><span class="line"><span class="cl">Programming languages
</span></span><span class="line"><span class="cl">Software using the BSD license
</span></span></code></pre></div><p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/duckduckgo/duckduckgo.factor">GitHub</a>.</p>
Magic
https://re.factorcode.org/2023/02/magic.html
Sun, 12 Feb 2023 09:00:00 -0800https://re.factorcode.org/2023/02/magic.html<p>Ever wonder what the type of a particular binary file is? Or wonder how a
program knows that a particular binary file is in a compatible <a href="https://en.wikipedia.org/wiki/File_format">file
format</a>? One way is to look at
the <a href="https://en.wikipedia.org/wiki/Magic_number_(programming)">magic number</a>
used by the file format in question. You can see some examples in a <a href="https://en.wikipedia.org/wiki/List_of_file_signatures">list
of file signatures</a>.</p>
<p>The <a href="http://www.darwinsys.com/file/">libmagic</a> library commonly supports the
<a href="http://linux.die.net/man/1/file">file</a> command on Unix systems, other than
<a href="https://www.apple.com/macos/ventura/">Apple macOS</a> which has its own
implementation, and uses magic numbers and other techniques to identify file
types. You can see how it works through a few examples:</p>
<pre tabindex="0"><code>$ file vm/factor.hpp
vm/factor.hpp: C++ source text, ASCII text
$ file Factor.app/Contents/Info.plist
Factor.app/Contents/Info.plist: XML document text
$ file factor
factor: Mach-O 64-bit executable x86_64
$ file factor.image
factor.image: data
</code></pre><h3 id="wrapping-the-c-library">Wrapping the C library</h3>
<p>I am going to show how to wrap a C library using the <a href="https://docs.factorcode.org/content/article-alien.html">alien
vocabulary</a> which
provides an <a href="https://en.wikipedia.org/wiki/Foreign_function_interface">FFI
capability</a> in
<a href="https://factorcode.org/">Factor</a>. The <a href="https://manpages.ubuntu.com/manpages/trusty/man3/libmagic.3.html">man pages for
libmagic</a>
show us some of the functions available in <code>magic.h</code>.</p>
<p>The <code>libmagic</code> library needs to be made available to the Factor instance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="s">"magic"</span> {
</span></span><span class="line"><span class="cl"> { [ os macosx? ] [ <span class="s">"libmagic.dylib"</span> ] }
</span></span><span class="line"><span class="cl"> { [ os unix? ] [ <span class="s">"libmagic.so"</span> ] }
</span></span><span class="line"><span class="cl">} <span class="nb">cond </span>cdecl add-library
</span></span></code></pre></div><p>We start by defining an opaque type for <code>magic_t</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">TYPEDEF:</span> <span class="nf">void*</span> <span class="nf">magic_t</span>
</span></span></code></pre></div><p>Some functions are available for opening, loading, and then closing the
<code>magic_t</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> magic_t <span class="nf">magic_open</span> ( int flags )
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> int <span class="nf">magic_load</span> ( magic_t magic, c-string path )
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> void <span class="nf">magic_close</span> ( magic_t magic )
</span></span></code></pre></div><p>It is convenient to wrap the close function as a
<a href="https://docs.factorcode.org/content/word-DESTRUCTOR__colon__%2Calien.destructors.html">destructor</a>
for use in a
<a href="https://docs.factorcode.org/content/word-with-destructors,destructors.html">with-destructors</a>
form.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">DESTRUCTOR: magic_close
</span></span></code></pre></div><p>A function that <em>“returns a textual description of the contents of the
filename argument”</em>, which gives us the file command ability above:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> c-string <span class="nf">magic_file</span> ( magic_t magic, c-string path )
</span></span></code></pre></div><p>That should be everything we need to continue…</p>
<h3 id="using-the-c-library">Using the C library</h3>
<p>Now that we have the raw C library made available as Factor words, we can
create a simpler interface by wrapping some of the words into a simple word
that guesses the type of a file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">guess-file</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">result</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> normalize-path
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>magic_open &magic_close
</span></span><span class="line"><span class="cl"> [ <span class="no">f </span>magic_load <span class="nb">drop </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap </span>magic_file ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] with-destructors <span class="k">;
</span></span></span></code></pre></div><p>And we can then try it on a few files:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"vm/factor.hpp"</span> guess-file <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"C++ source, ASCII text"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Factor.app/Contents/Info.plist"</span> guess-file <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"XML 1.0 document, Unicode text, UTF-8 text"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"factor"</span> guess-file <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"symbolic link to Factor.app/Contents/MacOS/factor"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"factor.image"</span> guess-file <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"data"</span>
</span></span></code></pre></div><p>This has been available for awhile in the <a href="https://docs.factorcode.org/content/vocab-magic.html">magic
vocabulary</a> with
improved error checking and some options to guess the
<a href="https://en.wikipedia.org/wiki/Media_type">MIME type</a> of files.</p>
Hipku
https://re.factorcode.org/2023/02/hipku.html
Wed, 08 Feb 2023 09:00:00 -0800https://re.factorcode.org/2023/02/hipku.html<p>Once upon a time, there was a Javascript project called
<a href="https://github.com/gabemart/hipku">Hipku</a>. The original post that described
it was lost somewhere in the <a href="https://en.wikipedia.org/wiki/Series_of_tubes">series of
tubes</a>, but thankfully the
“full documentation and a working demo” was <a href="https://web.archive.org/web/20211204171141/https://gabrielbrady.com/projects/hipku/">saved by the Wayback
Machine</a>.
It is also still <a href="https://www.npmjs.com/package/hipku">available on npm</a> for
installation.</p>
<blockquote>
<p>Hipku is a small javascript library to encode IP addresses as haiku. It
can express any IPv4 or IPv6 address as a Western-style 5/7/5 syllable
haiku.</p>
</blockquote>
<p>An implementation in <a href="https://python.org">Python</a> was created called
<a href="https://github.com/lord63/pyhipku">PyHipku</a>. It is still <a href="https://pypi.org/project/pyhipku/">available on
PyPi</a> for installation, but the website
associated with it was also lost to history and not even the Great Wayback
Machine seems able to recover it. I think of programming as aspiring to a kind
of poetic result – and wonder what kind of a language could run <a href="https://spot.colorado.edu/~sniderc/poetry/wakawaka.html">Waka Waka Bang
Splat</a> – well, the
<a href="https://en.wikipedia.org/wiki/Haiku">haiku</a> style caught my interest, so I
ported the <em>hipku</em> algorithm to the <a href="https://factorcode.org">Factor programming
language</a>.</p>
<p>At it’s core, we encode an <a href="https://en.wikipedia.org/wiki/Internet_Protocol_version_4">IPv4
address</a> or <a href="https://en.wikipedia.org/wiki/IPv6">IPv6
address</a> into a series of numerical values
and then make a poem by looking up each word from a word list. Some symbols are
defined to help us know to start a sentence with an uppercase letter or end a
sentence with a period:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYMBOLS: </span><span class="nf">Octet</span> <span class="nf">octet</span> <span class="nf">octet.</span> <span class="k">;
</span></span></span></code></pre></div><p>For example, an IPv4 key specifies the word lists to use for each octet and an
IPv4 schema specify how the octets form into a <em>hipku</em> – an <code>f</code> indicates a
newline:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">ipv4-key</span> ${
</span></span><span class="line"><span class="cl"> animal-adjectives animal-colors animal-nouns animal-verbs
</span></span><span class="line"><span class="cl"> nature-adjectives nature-nouns plant-nouns plant-verbs
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">ipv4-schema</span> ${
</span></span><span class="line"><span class="cl"> <span class="s">"The"</span> octet octet octet <span class="no">f
</span></span></span><span class="line"><span class="cl"> octet <span class="s">"in the"</span> octet octet. <span class="no">f
</span></span></span><span class="line"><span class="cl"> Octet octet.
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>To create the <em>hipku</em>, we iterate across the key, choosing words numerically by
looking up the octet value, and then composing them into the ordering specified
by the schema.</p>
<p>You can see a couple examples below:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"127.0.0.1"</span> >hipku <span class="nb">print
</span></span></span><span class="line"><span class="cl">The hungry white ape
</span></span><span class="line"><span class="cl">aches in the ancient canyon.
</span></span><span class="line"><span class="cl">Autumn colors crunch.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"2001:db8:3333:4444:5555:6666:7777:8888"</span> >hipku <span class="nb">print
</span></span></span><span class="line"><span class="cl">Chilled apes <span class="nb">and </span>blunt seas
</span></span><span class="line"><span class="cl">clap dear firm firm grim grim gnomes.
</span></span><span class="line"><span class="cl">Large holes grasp pained mares.
</span></span></code></pre></div><p>We support both encoding into a <em>hipku</em> as well as decoding back into an
IPv4/IPv6 address. This is available as the <a href="https://github.com/factor/factor/blob/master/extra/hipku/hipku.factor">hipku
vocabulary</a>
in a recent nightly build.</p>Proquint
https://re.factorcode.org/2023/02/proquint.html
Tue, 07 Feb 2023 08:00:00 -0800https://re.factorcode.org/2023/02/proquint.html<p>A few days ago, Ciprian Dorin Craciun wrote a <a href="https://notes.volution.ro/v1/2023/02/notes/0a9aaa3a/">binary to text encoding blog
post</a> about the “state
of the art and missed opportunities” in various encoding schemes. In that
post, I was introduced to the <a href="https://arxiv.org/html/0901.4016">Proquint
encoding</a> which stands for “PRO-nouncable
QUINT-uplets”.</p>
<p>In the <a href="https://factorcode.org/">Factor programming language</a>, we have
enjoyed implementing many encoding/decoding methods including:
<a href="https://docs.factorcode.org/content/vocab-base16.html">base16</a>,
<a href="https://docs.factorcode.org/content/vocab-base24.html">base24</a>,
<a href="https://docs.factorcode.org/content/vocab-base32.html">base32</a>,
<a href="https://docs.factorcode.org/content/vocab-base32hex.html">base32hex</a>,
<a href="https://docs.factorcode.org/content/vocab-base32-crockford.html">base32-crockford</a>,
<a href="https://docs.factorcode.org/content/vocab-base36.html">base36</a>,
<a href="https://docs.factorcode.org/content/vocab-base58.html">base58</a>,
<a href="https://docs.factorcode.org/content/vocab-base62.html">base62</a>,
<a href="https://docs.factorcode.org/content/vocab-base64.html">base64</a>,
<a href="https://docs.factorcode.org/content/vocab-base85.html">base85</a>,
<a href="https://docs.factorcode.org/content/vocab-base91.html">base91</a>,
<a href="https://docs.factorcode.org/content/vocab-uu.html">uu</a>, and many others. I
thought it would be fun to add a quick implementation of Proquint.</p>
<p>Like other encodings, it makes use of an alphabet – grouped as consonants
and vowels:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">consonant</span> <span class="s">"bdfghjklmnprstvz"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">vowel</span> <span class="s">"aiou"</span>
</span></span></code></pre></div><p>Numbers are grouped into 5-character blocks representing a 16-bit number,
with alternating consonants representing 4 bits and vowels representing 2
bits:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>quint16</span> <span class="nf">( </span><span class="nv">m</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">5 </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">even? </span>[
</span></span><span class="line"><span class="cl"> [ <span class="m">-4 </span><span class="nb">shift </span>] [ <span class="m">4 </span>bits consonant <span class="nb">nth </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> [ <span class="m">-2 </span><span class="nb">shift </span>] [ <span class="m">2 </span>bits vowel <span class="nb">nth </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="s">""</span> map-integers-as <span class="nb">reverse nip </span><span class="k">;
</span></span></span></code></pre></div><p>Encoding a 32-bit number is made by joining two 16-bit blocks:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>quint32</span> <span class="nf">( </span><span class="nv">m</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">-16 </span><span class="nb">shift </span>] <span class="nb">keep </span>[ <span class="m">16 </span>bits >quint16 ] <span class="nb">bi@ </span><span class="s">"-"</span> <span class="nb">glue </span><span class="k">;
</span></span></span></code></pre></div><p>Decoding numbers looks up each consonant or vowel, skipping separators:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">quint></span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">m</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>$[ consonant alphabet-inverse ] <span class="nb">nth </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">nip </span>[ <span class="m">4 </span><span class="nb">shift </span>] [ <span class="nb">+ </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>$[ vowel alphabet-inverse ] <span class="nb">nth </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">nip </span>[ <span class="m">2 </span><span class="nb">shift </span>] [ <span class="nb">+ </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="sc">CHAR: - </span><span class="nb">assert=
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">reduce </span><span class="k">;
</span></span></span></code></pre></div><p>We can use this to make a random password that might be more memorable – but
perhaps more secure if using more
<a href="https://docs.factorcode.org/content/word-random-bits,random.html">random-bits</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">quint-password</span> <span class="nf">( -- </span><span class="nv">quint</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">32 </span>random-bits >quint32 <span class="k">;
</span></span></span></code></pre></div><p>And we could use our <a href="https://docs.factorcode.org/content/vocab-ip-parser.html">ip-parser
vocabulary</a> to
make <a href="https://en.wikipedia.org/wiki/Internet_Protocol_version_4">IPv4
addresses</a> more
memorable:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">ipv4>quint</span> <span class="nf">( </span><span class="nv">ipv4</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> ipv4-aton >quint32 <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">quint>ipv4</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">ipv4</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> quint> ipv4-ntoa <span class="k">;
</span></span></span></code></pre></div><p>You can see how this might work by building a test suite to show roundtrips
work:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="no">t </span>} [
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { <span class="s">"127.0.0.1"</span> <span class="s">"lusab-babad"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"63.84.220.193"</span> <span class="s">"gutih-tugad"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"63.118.7.35"</span> <span class="s">"gutuk-bisog"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"140.98.193.141"</span> <span class="s">"mudof-sakat"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"64.255.6.200"</span> <span class="s">"haguz-biram"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"128.30.52.45"</span> <span class="s">"mabiv-gibot"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"147.67.119.2"</span> <span class="s">"natag-lisaf"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"212.58.253.68"</span> <span class="s">"tibup-zujah"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"216.35.68.215"</span> <span class="s">"tobog-higil"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"216.68.232.21"</span> <span class="s">"todah-vobij"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"198.81.129.136"</span> <span class="s">"sinid-makam"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"12.110.110.204"</span> <span class="s">"budov-kuras"</span> }
</span></span><span class="line"><span class="cl"> } [
</span></span><span class="line"><span class="cl"> [ quint>ipv4 <span class="nb">= </span>] [ <span class="nb">swap </span>ipv4>quint <span class="nb">= </span>] <span class="nb">2bi and
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">assoc-all?
</span></span></span><span class="line"><span class="cl">] unit-test
</span></span></code></pre></div><p>This is now available as the <a href="https://github.com/factor/factor/blob/master/extra/proquint/proquint.factor">proquint
vocabulary</a>
in a recent nightly build.</p>Semantic Versioning
https://re.factorcode.org/2023/01/semantic-versioning.html
Tue, 24 Jan 2023 11:00:00 -0800https://re.factorcode.org/2023/01/semantic-versioning.html<p><a href="https://semver.org/">Semantic Versioning</a> (or “semver” for short) is a
specification for handling version numbers, and providing a way to sort and
specify compatibility using a <code>MAJOR.MINOR.PATCH</code> structure with optional
“pre-release” and “build” information.</p>
<p>Some examples of semantic version numbers:</p>
<ul>
<li>1.0.0-alpha</li>
<li>1.0.0-beta+win32</li>
<li>1.0.0-rc.1</li>
<li>1.0.0</li>
</ul>
<p>For a long time, I thought it might be funny to follow the upcoming release
of <a href="https://factorcode.org">Factor</a> version <code>0.99</code> with version <code>0.100</code>.
Well, if we wanted to be consistent with “semver”, it might instead have to
be something like <code>0.100.0-joke+haha</code>.</p>
<p>There is now a <a href="https://docs.factorcode.org/content/vocab-semver.html">semver
vocabulary</a> that
provides some words for sorting and working with semantic version numbers.
Here’s an example using it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">semver</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"0.99.0"</span> >semver bump-alpha semver.
</span></span><span class="line"><span class="cl">0.99.1-alpha.0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"0.99.0"</span> >semver bump-preminor bump-rc semver.
</span></span><span class="line"><span class="cl">0.100.0-rc.0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"0.99.0"</span> <span class="s">"0.100.0"</span> semver<=> <span class="m">.
</span></span></span><span class="line"><span class="cl">+lt+
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"0.100.0-joke+haha"</span> >semver bump-major semver.
</span></span><span class="line"><span class="cl">1.0.0
</span></span></code></pre></div><p>Reading the <a href="https://semver.org/spec/v2.0.0.html">Semantic Versioning 2.0.0</a>
specification, it suggests using the version numbers to represent
compatibility with previous versions. And many languages have package
managers that use these compatibility guarantees with “semver ranges” to
manage project dependencies.</p>
Five Questions
https://re.factorcode.org/2023/01/five-questions.html
Sat, 21 Jan 2023 11:00:00 -0800https://re.factorcode.org/2023/01/five-questions.html<p>Many years ago, there was a blog post containing <a href="https://blog.svpino.com/2015/05/07/five-programming-problems-every-software-engineer-should-be-able-to-solve-in-less-than-1-hour">five programming problems
every software engineer should be able to solve in less than 1
hour</a>.
I had bookmarked it at the time and didn’t notice <a href="https://www.reddit.com/r/programming/comments/358tnp/five_programming_problems_every_software_engineer/">the controversy it
created on
Reddit</a>.
The original link seems to be down – you can <a href="https://web.archive.org/web/20160816193027/http://www.shiftedup.com/2015/05/07/five-programming-problems-every-software-engineer-should-be-able-to-solve-in-less-than-1-hour">view a copy of it on the
Wayback
Machine</a>
– but there are various solutions posted online, including a <a href="https://grison.me/2015/05/09/five-programming-problems-every-software-engineer-should-be-able-to-solve-in-less-than-1-hour">solution in
Python</a>.</p>
<p>I finally got around to looking at it and writing up some solutions to the
problems listed. Apparently, instead of solving this in 1 hour in
<a href="https://factorcode.org">Factor</a>, it took me almost 8 years:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2015 05 09 </span><date> days-since <span class="nb">>integer </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">2814
</span></span></span></code></pre></div><h2 id="problem-1">Problem 1</h2>
<p><em>Write three functions that compute the sum of the numbers in a given list
using a for-loop, a while-loop, and recursion.</em></p>
<p>In idiomatic Factor, this is just
<a href="https://docs.factorcode.org/content/word-sum,sequences.html">sum</a>, and we
would typically use <a href="https://docs.factorcode.org/content/article-sequences-combinators.html">sequence
combinators</a>,
but instead here are a few solutions using <a href="https://docs.factorcode.org/content/article-locals.html">lexical
variables</a>.</p>
<p>Using a for-loop, iterating forwards:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">solve-1</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>seq <span class="nb">length </span>[ seq nth-unsafe <span class="nb">+ </span>] <span class="nb">each-integer </span><span class="k">;
</span></span></span></code></pre></div><p>Using a while-loop, iterating forwards:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">solve-1</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 0 </span>seq <span class="nb">length </span>:> n
</span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>n <span class="nb">< </span>] [
</span></span><span class="line"><span class="cl"> [ seq nth-unsafe <span class="nb">+ </span>] [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while drop </span><span class="k">;
</span></span></span></code></pre></div><p>Using recursion, iterating backwards:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">(solve-1)</span> <span class="nf">( </span><span class="nv">accum</span> <span class="nv">i</span> <span class="nv">seq</span> <span class="nf">-- </span><span class="nv">accum'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> accum i [
</span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="nb">- </span>seq [ nth-unsafe <span class="nb">+ </span>] <span class="nb">2keep </span>(solve-1)
</span></span><span class="line"><span class="cl"> ] <span class="nb">unless-zero </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">solve-1</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span><span class="nb">swap </span>[ <span class="nb">length </span>] <span class="nb">keep </span>(solve-1) <span class="k">;
</span></span></span></code></pre></div><p>Some test cases to confirm behavior:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="m">0 </span>} [ { } solve-1 ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="m">1 </span>} [ { <span class="m">1 </span>} solve-1 ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="m">6 </span>} [ { <span class="m">1 2 3 </span>} solve-1 ] unit-test
</span></span></code></pre></div><h2 id="problem-2">Problem 2</h2>
<p><em>Write a function that combines two lists by alternatively taking elements.
For example: given the two lists <code>[a, b, c]</code> and <code>[1, 2, 3]</code>, the
function should return <code>[a, 1, b, 2, c, 3]</code>.</em></p>
<p>We can alternately choose items from each list:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">solve-2</span> <span class="nf">( </span><span class="nv">seq1</span> <span class="nv">seq2</span> <span class="nf">-- </span><span class="nv">newseq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">min-length </span><span class="m">2 </span><span class="nb">* </span>] <span class="nb">2keep </span>'[
</span></span><span class="line"><span class="cl"> [ <span class="nb">2/ </span>] [ <span class="nb">even? </span>] <span class="nb">bi </span>_ _ <span class="nb">? </span>nth-unsafe
</span></span><span class="line"><span class="cl"> ] { } map-integers-as <span class="k">;
</span></span></span></code></pre></div><p>Some test cases to confirm behavior:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ { <span class="s">"a"</span> <span class="m">1 </span><span class="s">"b"</span> <span class="m">2 </span><span class="s">"c"</span> <span class="m">3 </span>} } [
</span></span><span class="line"><span class="cl"> { <span class="s">"a"</span> <span class="s">"b"</span> <span class="s">"c"</span> } { <span class="m">1 2 3 </span>} solve-2
</span></span><span class="line"><span class="cl">] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ { <span class="s">"a"</span> <span class="m">1 </span><span class="s">"b"</span> <span class="m">2 </span><span class="s">"c"</span> <span class="m">3 </span>} } [
</span></span><span class="line"><span class="cl"> { <span class="s">"a"</span> <span class="s">"b"</span> <span class="s">"c"</span> <span class="s">"d"</span> } { <span class="m">1 2 3 </span>} solve-2
</span></span><span class="line"><span class="cl">] unit-test
</span></span></code></pre></div><h2 id="problem-3">Problem 3</h2>
<p><em>Write a function that computes the list of the first 100 Fibonacci numbers.
By definition, the first two numbers in the Fibonacci sequence are 0 and 1,
and each subsequent number is the sum of the previous two.</em></p>
<p>There are many approaches, including using
<a href="https://docs.factorcode.org/content/article-memoize.html">memoization</a>, but
instead we’ll just iterate from the starting values and use
<a href="https://docs.factorcode.org/content/word-replicate,sequences.html">replicate</a>
to build up an output array.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">solve-3</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">0 1 </span>] <span class="nb">dip </span>[ <span class="nb">dup rot </span>[ <span class="nb">+ </span>] <span class="nb">keep </span>] <span class="nb">replicate 2nip </span><span class="k">;
</span></span></span></code></pre></div><p>Some test cases to confirm behavior:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ { } } [ <span class="m">0 </span>solve-3 ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ { <span class="m">0 </span>} } [ <span class="m">1 </span>solve-3 ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ { <span class="m">0 1 </span>} } [ <span class="m">2 </span>solve-3 ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ { <span class="m">0 1 1 2 3 5 8 13 21 34 </span>} } [ <span class="m">10 </span>solve-3 ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="m">573147844013817084100 </span>} [ <span class="m">100 </span>solve-3 <span class="nb">sum </span>] unit-test
</span></span></code></pre></div><h2 id="problem-4">Problem 4</h2>
<p>Write a function that given a list of non negative integers, arranges them
such that they form the largest possible number. For example, given <code>[50, 2, 1, 9]</code>, the largest formed number is <code>95021</code>.</p>
<p>We can try
<a href="https://docs.factorcode.org/content/word-each-permutation,math.combinatorics.html">each-permutation</a>
of the input numbers, looking for their largest numerical value when the
digits are concatenated:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">solve-4</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span><span class="nb">swap </span>[ number>string ] <span class="nb">map
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">concat </span>string>number max ] each-permutation <span class="k">;
</span></span></span></code></pre></div><p>Some test cases to confirm behavior:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="m">95021 </span>} [ { <span class="m">50 2 1 9 </span>} solve-4 ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="m">5523 </span>} [ { <span class="m">52 5 3 </span>} solve-4 ] unit-test
</span></span></code></pre></div><h2 id="problem-5">Problem 5</h2>
<p><em>Write a program that outputs all possibilities to put <code>+</code> or <code>-</code> or
nothing between the numbers 1, 2, …, 9 (in this order) such that the
result is always 100. For example: 1 + 2 + 34 – 5 + 67 – 8 + 9 = 100.</em></p>
<p>This one is more complicated than the previous ones, but we can build it up
piece by piece, using a test-case on each step to show how it works.</p>
<p>First, we want a word to interleave numbers amongst operators using <code>solve-2</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">insert-numbers</span> <span class="nf">( </span><span class="nv">operators</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>[1..b] ] [ solve-2 ] [ <span class="nb">length </span><span class="m">1 </span><span class="nb">+ suffix </span>] <span class="nb">tri </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ { <span class="m">1 </span><span class="no">f </span><span class="m">2 </span><span class="nb">+ </span><span class="m">3 </span><span class="no">f </span><span class="m">4 </span>} } [ { <span class="no">f </span><span class="nb">+ </span><span class="no">f </span>} insert-numbers ] unit-test
</span></span></code></pre></div><p>Next, we want a word that will join adjacent digits – separated by <code>f</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">GENERIC:</span> <span class="nf">digits,</span> <span class="nf">( </span><span class="nv">prev</span> <span class="nv">intermediate</span> <span class="nf">-- </span><span class="nv">next</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">number</span> <span class="nf">digits,</span> [ <span class="m">10 </span><span class="nb">* </span>] [ <span class="nb">+ </span>] <span class="nb">bi* </span><span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">object</span> <span class="nf">digits,</span> [ [ , <span class="m">0 </span>] <span class="nb">dip </span>, ] <span class="nb">when* </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">join-digits</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ ] [ digits, ] <span class="nb">map-reduce </span>, ] { } make <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ { <span class="m">12 </span><span class="nb">+ </span><span class="m">34 </span>} } [ { <span class="m">1 </span><span class="no">f </span><span class="m">2 </span><span class="nb">+ </span><span class="m">3 </span><span class="no">f </span><span class="m">4 </span>} join-digits ] unit-test
</span></span></code></pre></div><p>Since Factor is a kind of <a href="https://en.wikipedia.org/wiki/Reverse_Polish_notation">Reverse Polish
notation</a>, we’ll want
to swap from infix to postfix:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">swap-operators</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup rest-slice </span><span class="m">2 </span><groups> [ <span class="m">0 1 </span><span class="nb">rot exchange </span>] <span class="nb">each </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ { <span class="m">12 34 </span><span class="nb">+ </span>} } [ { <span class="m">12 </span><span class="nb">+ </span><span class="m">34 </span>} swap-operators ] unit-test
</span></span></code></pre></div><p>The solution, then, is to use
<a href="https://docs.factorcode.org/content/word-all-selections,math.combinatorics.html">all-selections</a>
of addition, subtraction, and adjacency – interleaving the numbers, joining
adjacent digits, swapping operators, and then calling each sequence as a
quotation, filtering for the ones that return <code>100</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">solve-5</span> <span class="nf">( -- </span><span class="nv">solutions</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { <span class="nb">+ - </span><span class="no">f </span>} <span class="m">8 </span>all-selections
</span></span><span class="line"><span class="cl"> [ insert-numbers join-digits swap-operators ] <span class="nb">map
</span></span></span><span class="line"><span class="cl"> [ >quotation call( -- x ) <span class="m">100 </span><span class="nb">= </span>] <span class="nb">filter </span><span class="k">;
</span></span></span></code></pre></div><p>We can print the formula out by swapping the operators back to infix and
printing them out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">print-formula</span> <span class="nf">( </span><span class="nv">solutions</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ present ] <span class="nb">map </span>swap-operators <span class="s">" "</span> <span class="nb">join print </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">solve-5.</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> solve-5 [ print-formula ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>Spoilers! The printed solutions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> solve-5.
</span></span><span class="line"><span class="cl"><span class="m">1 </span><span class="nb">+ </span><span class="m">2 </span><span class="nb">+ </span><span class="m">3 </span><span class="nb">- </span><span class="m">4 </span><span class="nb">+ </span><span class="m">5 </span><span class="nb">+ </span><span class="m">6 </span><span class="nb">+ </span><span class="m">78 </span><span class="nb">+ </span><span class="m">9
</span></span></span><span class="line"><span class="cl"><span class="m">1 </span><span class="nb">+ </span><span class="m">2 </span><span class="nb">+ </span><span class="m">34 </span><span class="nb">- </span><span class="m">5 </span><span class="nb">+ </span><span class="m">67 </span><span class="nb">- </span><span class="m">8 </span><span class="nb">+ </span><span class="m">9
</span></span></span><span class="line"><span class="cl"><span class="m">1 </span><span class="nb">+ </span><span class="m">23 </span><span class="nb">- </span><span class="m">4 </span><span class="nb">+ </span><span class="m">5 </span><span class="nb">+ </span><span class="m">6 </span><span class="nb">+ </span><span class="m">78 </span><span class="nb">- </span><span class="m">9
</span></span></span><span class="line"><span class="cl"><span class="m">1 </span><span class="nb">+ </span><span class="m">23 </span><span class="nb">- </span><span class="m">4 </span><span class="nb">+ </span><span class="m">56 </span><span class="nb">+ </span><span class="m">7 </span><span class="nb">+ </span><span class="m">8 </span><span class="nb">+ </span><span class="m">9
</span></span></span><span class="line"><span class="cl"><span class="m">12 </span><span class="nb">+ </span><span class="m">3 </span><span class="nb">+ </span><span class="m">4 </span><span class="nb">+ </span><span class="m">5 </span><span class="nb">- </span><span class="m">6 </span><span class="nb">- </span><span class="m">7 </span><span class="nb">+ </span><span class="m">89
</span></span></span><span class="line"><span class="cl"><span class="m">12 </span><span class="nb">+ </span><span class="m">3 </span><span class="nb">- </span><span class="m">4 </span><span class="nb">+ </span><span class="m">5 </span><span class="nb">+ </span><span class="m">67 </span><span class="nb">+ </span><span class="m">8 </span><span class="nb">+ </span><span class="m">9
</span></span></span><span class="line"><span class="cl"><span class="m">12 </span><span class="nb">- </span><span class="m">3 </span><span class="nb">- </span><span class="m">4 </span><span class="nb">+ </span><span class="m">5 </span><span class="nb">- </span><span class="m">6 </span><span class="nb">+ </span><span class="m">7 </span><span class="nb">+ </span><span class="m">89
</span></span></span><span class="line"><span class="cl"><span class="m">123 </span><span class="nb">+ </span><span class="m">4 </span><span class="nb">- </span><span class="m">5 </span><span class="nb">+ </span><span class="m">67 </span><span class="nb">- </span><span class="m">89
</span></span></span><span class="line"><span class="cl"><span class="m">123 </span><span class="nb">+ </span><span class="m">45 </span><span class="nb">- </span><span class="m">67 </span><span class="nb">+ </span><span class="m">8 </span><span class="nb">- </span><span class="m">9
</span></span></span><span class="line"><span class="cl"><span class="m">123 </span><span class="nb">- </span><span class="m">4 </span><span class="nb">- </span><span class="m">5 </span><span class="nb">- </span><span class="m">6 </span><span class="nb">- </span><span class="m">7 </span><span class="nb">+ </span><span class="m">8 </span><span class="nb">- </span><span class="m">9
</span></span></span><span class="line"><span class="cl"><span class="m">123 </span><span class="nb">- </span><span class="m">45 </span><span class="nb">- </span><span class="m">67 </span><span class="nb">+ </span><span class="m">89
</span></span></span></code></pre></div>GetPercentageRounds
https://re.factorcode.org/2023/01/getpercentagerounds.html
Thu, 19 Jan 2023 08:10:00 -0800https://re.factorcode.org/2023/01/getpercentagerounds.html<p>There was a <a href="https://twitter.com/jeroenfrijters/status/1615204074588180481">funny post on
Twitter</a> a
couple of days ago about a recent event where the <em>“Dutch government was
forced to release the source code of their DigiD digital authentication iOS
app”</em> with this piece of C# code:</p>
<p>
<img src="https://re.factorcode.org/images/2023-01-19-getpercentagerounds-u9XlVjU.png" alt="" width="425" height="504" />
</p>
<p>Some very funny discussions continued, with comments about how good or bad
this code is, and how one might rewrite it in various ways. I thought it
would be a fun opportunity to show a few variations of this simple function
in <a href="https://factorcode.org/">Factor</a>.</p>
<h2 id="implementations">Implementations</h2>
<p>A direct translation of this code, might use
<a href="https://docs.factorcode.org/content/word-cond,combinators.html">cond</a> which
is basically a sequence of if statements:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">get-percentage-rounds</span> <span class="nf">( </span><span class="nv">percentage</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0.0 </span><span class="nb"><= </span>] [ <span class="nb">drop </span><span class="s">"⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0.0 0.1 </span>between? ] [ <span class="nb">drop </span><span class="s">"🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0.1 0.2 </span>between? ] [ <span class="nb">drop </span><span class="s">"🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0.2 0.3 </span>between? ] [ <span class="nb">drop </span><span class="s">"🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0.3 0.4 </span>between? ] [ <span class="nb">drop </span><span class="s">"🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0.4 0.5 </span>between? ] [ <span class="nb">drop </span><span class="s">"🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0.5 0.6 </span>between? ] [ <span class="nb">drop </span><span class="s">"🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0.6 0.7 </span>between? ] [ <span class="nb">drop </span><span class="s">"🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0.7 0.8 </span>between? ] [ <span class="nb">drop </span><span class="s">"🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0.8 0.9 </span>between? ] [ <span class="nb">drop </span><span class="s">"🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪"</span> ] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">"🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵"</span> ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="k">;
</span></span></span></code></pre></div><p>But since this is a series of if statements checked sequentially, you can
just check the upper bounds. And since we only care about the argument for
the comparison, we can use
<a href="https://docs.factorcode.org/content/word-cond-case,combinators.extras.html">cond-case</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">get-percentage-rounds</span> <span class="nf">( </span><span class="nv">percentage</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ <span class="m">0.0 </span><span class="nb"><= </span>] [ <span class="s">"⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="m">0.1 </span><span class="nb"><= </span>] [ <span class="s">"🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="m">0.2 </span><span class="nb"><= </span>] [ <span class="s">"🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="m">0.3 </span><span class="nb"><= </span>] [ <span class="s">"🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="m">0.4 </span><span class="nb"><= </span>] [ <span class="s">"🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="m">0.5 </span><span class="nb"><= </span>] [ <span class="s">"🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="m">0.6 </span><span class="nb"><= </span>] [ <span class="s">"🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="m">0.7 </span><span class="nb"><= </span>] [ <span class="s">"🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="m">0.8 </span><span class="nb"><= </span>] [ <span class="s">"🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="m">0.9 </span><span class="nb"><= </span>] [ <span class="s">"🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪"</span> ] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">"🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵"</span> ]
</span></span><span class="line"><span class="cl"> } cond-case <span class="k">;
</span></span></span></code></pre></div><p>One suggestion was to generate a substring based on the input – with the
somewhat negative aspect that it allocates memory for the returned string
when called:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">get-percentage-rounds</span> <span class="nf">( </span><span class="nv">percentage</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">10 </span><span class="nb">* </span><span class="m">10 </span><span class="nb">swap - >integer dup </span><span class="m">10 </span><span class="nb">+
</span></span></span><span class="line"><span class="cl"> <span class="s">"🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪"</span> <span class="nb">subseq </span><span class="k">;
</span></span></span></code></pre></div><p>But another way would be to just index into the possible results, using
<a href="https://docs.factorcode.org/content/article-qw.html">quoted words</a> to
reduce the amount of tokens involved – resulting in this fairly aesthetic
result:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">get-percentage-rounds</span> <span class="nf">( </span><span class="nv">percentage</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">10 </span><span class="nb">* </span>ceiling <span class="nb">>integer </span>qw{
</span></span><span class="line"><span class="cl"> ⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪
</span></span><span class="line"><span class="cl"> 🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪
</span></span><span class="line"><span class="cl"> 🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪
</span></span><span class="line"><span class="cl"> 🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪
</span></span><span class="line"><span class="cl"> 🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪
</span></span><span class="line"><span class="cl"> 🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪
</span></span><span class="line"><span class="cl"> 🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪
</span></span><span class="line"><span class="cl"> 🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪
</span></span><span class="line"><span class="cl"> 🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪
</span></span><span class="line"><span class="cl"> 🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪
</span></span><span class="line"><span class="cl"> 🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵
</span></span><span class="line"><span class="cl"> } <span class="nb">nth </span><span class="k">;
</span></span></span></code></pre></div><p>It’s always fun to see different ways to solve problems. In the <a href="https://twitter.com/jeroenfrijters/status/1615204074588180481">Twitter
thread</a>, that
includes using binary search, building the output character-by-character,
generating solutions using ChatGPT, one-liners in Python, pattern matching,
unit testing, and discussions of edge cases and naming conventions.</p>
Project Gemini
https://re.factorcode.org/2023/01/project-gemini.html
Mon, 16 Jan 2023 08:10:00 -0800https://re.factorcode.org/2023/01/project-gemini.html<p><a href="https://gemini.circumlunar.space/">Project Gemini</a> is a neat modern take on
the <a href="https://en.wikipedia.org/wiki/Gopher_(protocol)">Gopher protocol</a>. You
can read the <a href="https://gemini.circumlunar.space/docs/faq.gmi">Gemini FAQ</a> or
the <a href="https://gemini.circumlunar.space/docs/specification.gmi">Gemini
specification</a> to
learn more details, but the home page has a nice summary:</p>
<blockquote>
<p>Gemini is a new internet protocol which</p>
<ul>
<li>Is heavier than gopher</li>
<li>Is lighter than the web</li>
<li>Will not replace either</li>
<li>Strives for maximum power to weight ratio</li>
<li>Takes user privacy very seriously</li>
</ul>
</blockquote>
<p>There are some nice <a href="https://gemini.circumlunar.space/clients.html">Gemini
clients</a> implemented in
various languages, for both the command-line and with nice user interfaces.
I happen to enjoy using <a href="https://tildegit.org/solderpunk/AV-98">AV-98</a> and
<a href="https://gmi.skyjake.fi/lagrange/">Lagrange</a>, but many others are also
great.</p>
<p>In a similar manner to my <a href="https://re.factorcode.org/2014/12/gopher.html">Gopher
implementation</a> in
<a href="https://factorcode.org">Factor</a>, I recently implemented the <a href="https://github.com/factor/factor/blob/master/extra/gemini/gemini.factor">Gemini
protocol</a>
as well as a <a href="https://github.com/factor/factor/blob/master/extra/gemini/server/server.factor">Gemini
server</a>
and a <a href="https://github.com/factor/factor/blob/master/extra/gemini/ui/ui.factor">Gemini user interface</a>:</p>
<p>
<img src="https://re.factorcode.org/images/2023-01-16-project-gemini-zIa46Qb.png" alt="" width="619" height="666" />
</p>
<p>Instead of going into how the protocol or the user interface is implemented,
I wanted to go over the <a href="https://github.com/factor/factor/blob/master/extra/gemini/cli/cli.factor">Gemini command-line
interface</a>.
In the spirit of Python’s <a href="https://docs.python.org/3/library/cmd.html">cmd
module</a>, I contributed the
<a href="https://docs.factorcode.org/content/vocab-command-loop.html">command-loop
vocabulary</a> to
support generic line-oriented command interpreters.</p>
<p>We start by making a sequence of commands that our Gemini interpreter will support:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">COMMANDS</span> {
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"back"</span> }
</span></span><span class="line"><span class="cl"> { quot [ <span class="nb">drop </span>gemini-back ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Go back to the previous Gemini URL."</span> }
</span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">"b"</span> } } }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"forward"</span> }
</span></span><span class="line"><span class="cl"> { quot [ <span class="nb">drop </span>gemini-forward ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Go forward to the next Gemini URL."</span> }
</span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">"f"</span> } } }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"history"</span> }
</span></span><span class="line"><span class="cl"> { quot [ <span class="nb">drop </span>gemini-history ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Display recently viewed Gemini URLs."</span> }
</span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">"h"</span> <span class="s">"hist"</span> } } }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"less"</span> }
</span></span><span class="line"><span class="cl"> { quot [ <span class="nb">drop </span>gemini-less ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"View the most recent Gemini URL in a pager."</span> }
</span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">"l"</span> } } }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"ls"</span> }
</span></span><span class="line"><span class="cl"> { quot [ gemini-ls ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"List the currently available links."</span> }
</span></span><span class="line"><span class="cl"> { abbrevs <span class="no">f </span>} }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"go"</span> }
</span></span><span class="line"><span class="cl"> { quot [ gemini-go ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Go to a Gemini URL"</span> }
</span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">"g"</span> } } }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"gus"</span> }
</span></span><span class="line"><span class="cl"> { quot [ <span class="nb">drop </span><span class="s">"gemini://gus.guru/search"</span> gemini-go ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Submit a query to the GUS search engine."</span> }
</span></span><span class="line"><span class="cl"> { abbrevs <span class="no">f </span>} }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"up"</span> }
</span></span><span class="line"><span class="cl"> { quot [ <span class="nb">drop </span>gemini-up ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Go up one directory from the recent Gemini URL."</span> }
</span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">"u"</span> } } }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"url"</span> }
</span></span><span class="line"><span class="cl"> { quot [ <span class="nb">drop </span>gemini-url ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Print the most recent Gemini URL."</span> }
</span></span><span class="line"><span class="cl"> { abbrevs <span class="no">f </span>} }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"reload"</span> }
</span></span><span class="line"><span class="cl"> { quot [ <span class="nb">drop </span>gemini-reload ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Reload the most recent Gemini URL."</span> }
</span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">"r"</span> } } }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"root"</span> }
</span></span><span class="line"><span class="cl"> { quot [ <span class="nb">drop </span>gemini-root ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Navigate to the most recent Gemini URL's root."</span> }
</span></span><span class="line"><span class="cl"> { abbrevs <span class="no">f </span>} }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"shell"</span> }
</span></span><span class="line"><span class="cl"> { quot [ gemini-shell ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"'cat' the most recent Gemini URL through a shell."</span> }
</span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">"!"</span> } } }
</span></span><span class="line"><span class="cl"> T{ command
</span></span><span class="line"><span class="cl"> { name <span class="s">"quit"</span> }
</span></span><span class="line"><span class="cl"> { quot [ <span class="nb">drop </span>gemini-quit ] }
</span></span><span class="line"><span class="cl"> { help <span class="s">"Quit the program."</span> }
</span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">"q"</span> <span class="s">"exit"</span> } } }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>And then we define a custom
<a href="https://docs.factorcode.org/content/word-command-loop,command-loop.html">command-loop</a>
that will allow us to number the links on a Gemini page, and then by typing
a number we can navigate to one of the links by detecting a “missing
command”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">gemini-command-loop</span> < <span class="nc">command-loop</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">gemini-command-loop</span> <span class="nf">missing-command</span>
</span></span><span class="line"><span class="cl"> <span class="nb">over </span>string>number [ <span class="m">1 </span><span class="nb">- </span>LINKS <span class="nb">?nth </span>] [ <span class="no">f </span>] <span class="nb">if* </span>[
</span></span><span class="line"><span class="cl"> gemini-go <span class="nb">3drop
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> call-next-method
</span></span><span class="line"><span class="cl"> ] <span class="nb">if* </span><span class="k">;
</span></span></span></code></pre></div><p>Finally, we make a simple MAIN: word to run it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">gemini-main</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"Welcome to Project Gemini!"</span> <span class="s">"GEMINI>"</span>
</span></span><span class="line"><span class="cl"> gemini-command-loop new-command-loop
</span></span><span class="line"><span class="cl"> COMMANDS [ <span class="nb">over </span>add-command ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> run-command-loop <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">gemini-main</span>
</span></span></code></pre></div><p>You can see it in action:</p>
<pre tabindex="0"><code>$ ./factor -run=gemini.cli
Welcome to Project Gemini!
GEMINI> go gemini.circumlunar.space/news/
Official Project Gemini news feed
[1] Atom feed
2023 News
[2] 2023-01-14 - Tidying up gemini.circumlunar.space user capsules
[3] 2023-01-08 - Changing DNS server
2022 News
[4] 2022-06-20 - Three years of Gemini!
[5] 2022-01-30 - Minor specification update (0.16.1)
[6] 2022-01-22 - Mailing list archives, Atom feed for official news
[7] 2022-01-16 - Mailing list downtime, official news feed
</code></pre>Guided Tour of Factor
https://re.factorcode.org/2023/01/guided-tour-of-factor.html
Sun, 15 Jan 2023 08:10:00 -0800https://re.factorcode.org/2023/01/guided-tour-of-factor.html<p>Many years ago, <a href="https://github.com/andreaferretti">Andrea Ferretti</a> created
a <a href="https://andreaferretti.github.io/factor-tutorial/">Factor tutorial</a> which
I had <a href="https://re.factorcode.org/2014/11/factor-tutorial.html">written about at the
time</a>.</p>
<p>One of our new core developers, <a href="https://github.com/razetime">Raghu
Ranganathan</a>, got permission to include the
tutorial in the main <a href="https://factorcode.org">Factor</a> repository, and has
reformatted it and updated it for the not-yet-released version 0.99 as the
<a href="https://docs.factorcode.org/content/article-tour.html">Guided tour of
Factor</a>.</p>
<p>You can access it in a recent nightly build by doing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"tour"</span> help
</span></span></code></pre></div><p>Check it out!</p>
Speedrun Feedback
https://re.factorcode.org/2022/02/speedrun-feedback.html
Fri, 04 Feb 2022 08:10:00 -0800https://re.factorcode.org/2022/02/speedrun-feedback.html<p>Recently, <a href="https://t-a-w.blogspot.com">Tomasz Wegrzanowski</a> chose
<a href="https://factorcode.org">Factor</a> for his <a href="https://dev.to/taw/100-languages-speedrun-episode-71-factor-3bb2">100 Languages Speedrun:
Episode 71:
Factor</a>
and it encouraged us to make some improvements that I wanted to
describe.</p>
<p>Many of our users use the Factor environment through the <a href="https://docs.factorcode.org/content/article-ui-tools.html">UI developer
tools</a> or on
the command-line with <a href="https://docs.factorcode.org/content/article-listener.html">the
listener</a>.
Another important use case is being able to <code>eval</code> and run scripts –
and this is where much of Tomasz’ criticism was focused.</p>
<p>We now do command-line eval and run scripts with
<a href="https://docs.factorcode.org/content/word-auto-use__que__%2Cparser.html">auto-use?</a>
enabled. This will be available in the nightly builds and as part of an
upcoming 0.99 release.</p>
<p>So this works now:</p>
<pre tabindex="0"><code>$ ./factor -e="1 2 + ."
3
$ cat foo.factor
USE: io
"Hello World" print
12
$ ./factor foo.factor
Hello World
--- Data stack:
12
</code></pre><p>Previously, the first example would error with a “No word named “+”
found in current vocabulary search path” and the second example would
complain that the “Quotation’s stack effect does not match call site”
because the script did not have a <code>( -- )</code> stack effect.</p>
<p>I appreciate that some users approach <a href="https://factorcode.org">Factor</a>
differently than I do, and we love getting feedback. I wish we could
solve the name conflict with
<a href="https://man7.org/linux/man-pages/man1/factor.1.html">factor(1)</a>, but
that is more challenging.</p>
<p>We may adjust this slightly as it just landed last night, and if anyone
has further suggestions, please keep them coming!</p>
Factor 0.98 now available
https://re.factorcode.org/2018/07/factor-0-98-now-available.html
Tue, 31 Jul 2018 10:09:00 -0700https://re.factorcode.org/2018/07/factor-0-98-now-available.html<p><em>“Even though you’re growing up, you should never stop having fun.” -
Nina Dobrev</em></p>
<p>I’m very pleased to announce the release of
<a href="https://factorcode.org">Factor</a> 0.98!</p>
<table class="downloads" cellspacing="0">
<colgroup>
<col style="width: 25%" />
<col style="width: 25%" />
<col style="width: 25%" />
<col style="width: 25%" />
</colgroup>
<thead>
<tr class="header">
<th class="nobg" style="text-align: center;">OS/CPU</th>
<th class="bg" style="text-align: center;" scope="col">Windows</th>
<th class="bg" style="text-align: center;" scope="col">Mac OS</th>
<th class="bg" style="text-align: center;" scope="col">Linux</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<th class="bg" style="text-align: center;" scope="row">x86</th>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.98/factor-windows-x86-32-0.98.zip">0.98</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.98/factor-macosx-x86-32-0.98.dmg">0.98</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.98/factor-linux-x86-32-0.98.tar.gz">0.98</a>
</td>
</tr>
<tr class="even">
<th class="bg" style="text-align: center;" scope="row">x86-64</th>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.98/factor-windows-x86-64-0.98.zip">0.98</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.98/factor-macosx-x86-64-0.98.dmg">0.98</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.98/factor-linux-x86-64-0.98.tar.gz">0.98</a>
</td>
</tr>
</tbody>
</table>
<p><strong>Source code</strong>: <a
href="https://downloads.factorcode.org/releases/0.98/factor-src-0.98.zip"
class="release">0.98</a></p>
<p>This release is brought to you with almost 4,300 commits by the
following individuals:</p>
<blockquote>
<p>Alexander Ilin, Arkady Rost, Benjamin Pollack, Björn Lindqvist, Cat
Stevens, Chris Double, Dimage Sapelkin, Doug Coleman, Friedrich von
Never, John Benediktsson, Jon Harper, Mark Green, Mark Sweeney,
Nicolas Pénet, Philip Dexter, Robert Vollmert, Samuel Tardieu,
Sankaranarayanan Viswanathan, Shane Pelletier,
<a href="https://github.com/catb0t">@catb0t</a>,
<a href="https://github.com/hyphz">@hyphz</a>,
<a href="https://github.com/thron7">@thron7</a>,
<a href="https://github.com/xzenf">@xzenf</a></p>
</blockquote>
<p>Besides several years of bug fixes and library improvements, I want to
highlight the following changes:</p>
<ul>
<li>Improved user interface with light and dark themes and new icons</li>
<li>Fix GTK library issue affecting some Linux installations</li>
<li>Support Cocoa TouchBar on MacOS</li>
<li>Factor REPL version banner includes build information.</li>
<li>Bindings for <a href="https://github.com/couchbase/forestdb">ForestDB</a></li>
<li>New graphical demos including Minesweeper, Game of Life, Bubble
Chamber, etc.</li>
<li>Better handling of “out of memory” errors</li>
<li>Improved VM and compiler documentation, test fixtures, and bug fixes</li>
<li>Much faster Heaps and Heapsort</li>
<li>Support for Adobe Brackets, CotEditor, and Microsoft Visual Studio
Code editors</li>
<li>On Mac OS, allow use of symlinks to factor binary</li>
<li>Lots of improvements to FUEL (Factor’s emacs mode)</li>
</ul>
<p>Some possible backwards compatibility issues:</p>
<ul>
<li>Flattened <code>unicode</code> namespace (either <code>USE: ascii</code> or
<code>USE: unicode</code>).</li>
<li>Unified <code>CONSTRUCTOR:</code> syntax to include generated word name</li>
<li>Returning a struct by value with two register-sized values on 64bit
now works correctly</li>
<li>Since shutdown hooks run first, calling exit will now
unconditionally exit even if there is an error</li>
<li>On Windows, don’t call <code>cd</code> to change directory when launching
processes; there is another mechanism for that</li>
<li>In <code>libc</code>, renamed <code>(io-error)</code> to <code>throw-errno</code></li>
<li>In <code>match</code>, renamed <code>?1-tail</code> to <code>?rest</code></li>
<li>In <code>sequences</code>, renamed <code>iota</code> to <code><iota></code></li>
<li>In <code>sequences</code>, renamed <code>start</code>/<code>start*</code> to
<code>subseq-start</code>/<code>subseq-start-from</code></li>
<li>In <code>syntax</code>, renamed <code>GENERIC#</code> to <code>GENERIC#:</code></li>
<li>Improve command-line argument parsing of “executable”</li>
<li>Make <code>buffered-port</code> not have a length, because of problem with
Linux virtual files and TCP sockets</li>
<li>Fix broken optimization that made floats work for integer keys in
<code>case</code> statements</li>
<li>Growable sequences expand by factor of 2 (instead of 3) when growing</li>
<li>Removed support for “triple-quote” strings</li>
</ul>
<h3 id="what-is-factor">What is Factor</h3>
<p>Factor is a <a href="https://www.concatenative.org/">concatenative</a>, stack-based
programming language with <a href="https://concatenative.org/wiki/view/Factor/Features/The%20language">high-level
features</a>
including dynamic types, extensible syntax, macros, and garbage
collection. On a practical side, Factor has a <a href="https://docs.factorcode.org/content/article-vocab-index.html">full-featured
library</a>,
supports many different platforms, and has been extensively documented.</p>
<p>The implementation is <a href="https://concatenative.org/wiki/view/Factor/Optimizing%20compiler">fully
compiled</a>
for performance, while still supporting <a href="https://concatenative.org/wiki/view/Factor/Interactive%20development">interactive
development</a>.
Factor applications are portable between all common platforms. Factor
can <a href="https://concatenative.org/wiki/view/Factor/Deployment">deploy stand-alone
applications</a> on
all platforms. Full source code for the Factor project is available
under a BSD license.</p>
<h3 id="new-libraries">New Libraries:</h3>
<ul>
<li><a href="https://docs.factorcode.org/content/vocab-backticks.html">backticks</a>:
process <a href="https://re.factorcode.org/2016/07/backticks.html">backtick
syntax</a> for
processes</li>
<li><a href="https://docs.factorcode.org/content/vocab-bencode.html">bencode</a>:
support for <a href="https://en.wikipedia.org/wiki/Bencode">bencoding</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-boolean-expr.html">boolean-expr</a>:
simple boolean expression evaluator and simplifier</li>
<li><a href="https://docs.factorcode.org/content/vocab-bubble-chamber.html">bubble-chamber</a>:
variation of Jared Tarbell’s <a href="https://complexification.net/gallery/machines/bubblechamber/">Bubble
Chamber</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-calendar.elapsed.html">calendar.elapsed</a>:
rendering elapsed time to text</li>
<li><a href="https://docs.factorcode.org/content/vocab-calendar.english.html">calendar.english</a>:
separated out English localization</li>
<li><a href="https://docs.factorcode.org/content/vocab-checksums.crc16.html">checksums.crc16</a>:
CRC16 checksum implementation</li>
<li><a href="https://docs.factorcode.org/content/vocab-checksums.metrohash.html">checksums.metrohash</a>:
MetroHash checksum implementation</li>
<li><a href="https://docs.factorcode.org/content/vocab-checksums.multi.html">checksums.multi</a>:
support multiple checksums in one pass</li>
<li><a href="https://docs.factorcode.org/content/vocab-checksums.process.html">checksums.process</a>:
support for using command-line checksums</li>
<li><a href="https://docs.factorcode.org/content/vocab-checksums.ripemd.html">checksums.ripemd</a>:
RIPEMD checksum implementation</li>
<li><a href="https://docs.factorcode.org/content/vocab-checksums.sodium.html">checksums.sodium</a>:
<a href="https://download.libsodium.org/doc/">Sodium crypto library</a>
checksum implementation</li>
<li><a href="https://docs.factorcode.org/content/vocab-cocoa.touchbar.html">cocoa.touchbar</a>:
support for the Apple Touchbar</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.flex-hex.html">colors.flex-hex</a>:
implement “flex hex” color algorithm</li>
<li><a href="https://docs.factorcode.org/content/vocab-crontab.html">crontab</a>:
parsing the <a href="https://www.nncron.ru/help/EN/working/cron-format.htm">cron
format</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-cuckoo-filters.html">cuckoo-filters</a>:
implementation of <a href="https://re.factorcode.org/2016/08/cuckoo-filters.html">Cuckoo Filter data
structure</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-dbf.html">dbf</a>: parsers
for <a href="https://en.wikipedia.org/wiki/DBase#File_formats">DBase file
format</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.brackets.html">editors.brackets</a>:
support for Adobe Brackets</li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.cot.html">editors.cot</a>:
support for CotEditor</li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.visual-studio-code.html">editors.visual-studio-code</a>:
support for Microsoft Visual Studio Code</li>
<li><a href="https://docs.factorcode.org/content/vocab-emojify.html">emojify</a>:
add emoji’s to text (<code>"I :heart: Factor :+1:"</code>)</li>
<li><a href="https://docs.factorcode.org/content/vocab-english.html">english</a>:
tools for English language words</li>
<li><a href="https://docs.factorcode.org/content/vocab-enigma.html">enigma</a>:
implementation of Enigma cipher machine</li>
<li><a href="https://docs.factorcode.org/content/vocab-escape-strings.html">escape-strings</a>:
minimal string escaping algorithm</li>
<li><a href="https://docs.factorcode.org/content/vocab-etc-hosts.html">etc-hosts</a>:
cross-platform <code>/etc/hosts</code> file parser</li>
<li><a href="https://docs.factorcode.org/content/vocab-file-monitor.html">file-monitor</a>:
cross-platform <a href="https://re.factorcode.org/2015/05/file-monitor.html">file change event
monitor</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-file-picker.html">file-picker</a>:
cross-platform file picker user interface</li>
<li><a href="https://docs.factorcode.org/content/vocab-file-server.html">file-server</a>:
cross-platform <a href="https://re.factorcode.org/2015/05/file-server.html">file server web
interface</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-flip-text.html">flip-text</a>:
fun with “uʍop ǝpᴉsdn” text flipping</li>
<li><a href="https://docs.factorcode.org/content/vocab-forestdb.html">forestdb</a>:
bindings for <a href="https://github.com/couchbase/forestdb">ForestDB</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-game-of-life.html">game-of-life</a>:
implementation of <a href="https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life">John Conway’s Game of
Life</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-google.gmail.html">google.gmail</a>:
adding Google GMail API support</li>
<li><a href="https://docs.factorcode.org/content/vocab-gopher.html">gopher</a>:
library for querying <a href="https://re.factorcode.org/2014/12/gopher.html">Gopher
servers</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-gopher.server.html">gopher.server</a>:
cross-platform <a href="https://re.factorcode.org/2016/10/gopher-server.html">Gopher file
server</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-gopher.ui.html">gopher.ui</a>:
interface for viewing Gopher servers</li>
<li><a href="https://docs.factorcode.org/content/vocab-ifaddrs.html">ifaddrs</a>:
list network interfaces</li>
<li><a href="https://docs.factorcode.org/content/vocab-ip-parser.html">ip-parser</a>:
parsing IPv4 and IPv6 addresses</li>
<li><a href="https://docs.factorcode.org/content/vocab-ldcache.html">ldcache</a>:
parsing <code>/etc/ld.so.cache</code> file</li>
<li><a href="https://docs.factorcode.org/content/vocab-libtls.html">libtls</a>:
wrapper for using <code>libtls</code> functions</li>
<li><a href="https://docs.factorcode.org/content/vocab-linked-sets.html">linked-sets</a>:
sets that yield items in insertion order</li>
<li><a href="https://docs.factorcode.org/content/vocab-lru-cache.html">lru-cache</a>:
<a href="https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)">least recently
used</a>
cache algorithm</li>
<li><a href="https://docs.factorcode.org/content/vocab-machine-learning.html">machine-learning</a>:
experiments with some machine learning algorithms</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.factorials.html">math.factorials</a>:
adding
<a href="https://re.factorcode.org/2016/11/reverse-factorial.html">reverse-factorial</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.functions.integer-logs.html">math.functions.integer-logs</a>:
support for integer log2 and log10</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.primes.erato.fast.html">math.primes.erato.fast</a>:
faster <a href="https://re.factorcode.org/2015/06/genuine-sieve-of-eratosthenes.html">Sieve of
Eratosthenes</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-metar.html">metar</a>:
parsers for METAR and TAF weather reports</li>
<li><a href="https://docs.factorcode.org/content/vocab-midi.html">midi</a>: <a href="https://re.factorcode.org/2015/04/reading-midi-files.html">reading
MIDI
files</a>
and <a href="https://re.factorcode.org/2015/04/writing-midi-files.html">writing MIDI
files</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-minesweeper.html">minesweeper</a>:
playing the <a href="https://re.factorcode.org/2018/02/minesweeper.html">classic game of
Minesweeper</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-named-tuples.html">named-tuples</a>:
using tuples as a sequence and assoc</li>
<li><a href="https://docs.factorcode.org/content/vocab-oauth1.html">oauth1</a>:
support <a href="https://oauth.net/1/">OAuth 1.0</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-oauth2.html">oauth2</a>:
support <a href="https://oauth.net/2/">OAuth 2.0</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-odbc.html">odbc</a>: support
ODBC database query</li>
<li><a href="https://docs.factorcode.org/content/vocab-picomath.html">picomath</a>:
implement <a href="https://hewgill.com/picomath/">picomath.org</a> small math
words</li>
<li><a href="https://docs.factorcode.org/content/vocab-robohash.html">robohash</a>:
adding a robot-based hashing tool</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.frozen.html">sequences.frozen</a>:
virtual “frozen” sequence</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.interleaved.html">sequences.interleaved</a>:
virtual “interleaved” sequence</li>
<li><a href="https://docs.factorcode.org/content/vocab-shapefiles.html">shapefiles</a>:
parser for <a href="https://www.esri.com/library/whitepapers/pdfs/shapefile.pdf">ESRI
Shapefiles</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-shell.parser.html">shell.parser</a>:
parser for shell expressions</li>
<li><a href="https://docs.factorcode.org/content/vocab-snake-game.html">snake-game</a>:
implementation of the <a href="https://en.wikipedia.org/wiki/Snake_(video_game_genre)">snake video
game</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-sodium.html">sodium</a>:
wrapper for <a href="https://download.libsodium.org/doc/">libsodium</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-sorting.bubble.html">sorting.bubble</a>:
adding Bubblesort</li>
<li><a href="https://docs.factorcode.org/content/vocab-stream.extras.html">stream.extras</a>:
few helper words</li>
<li><a href="https://docs.factorcode.org/content/vocab-subrip-subtitles.html">subrip-subtitles</a>:
parser for <a href="https://en.wikipedia.org/wiki/SubRip">SubRip</a> <code>.SRT</code>
files</li>
<li><a href="https://docs.factorcode.org/content/vocab-successor.html">successor</a>:
algorithm to find successor to a given string</li>
<li><a href="https://docs.factorcode.org/content/vocab-text-analysis.html">text-analysis</a>:
analyze the complexity of English text</li>
<li><a href="https://docs.factorcode.org/content/vocab-text-to-pdf.html">text-to-pdf</a>:
simple “Text to PDF” utility</li>
<li><a href="https://docs.factorcode.org/content/vocab-text-to-speech.html">text-to-speech</a>:
cross-platform “Text to Speech” utility</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.cal.html">tools.cal</a>:
command-line “cal” tool</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.cat.html">tools.cat</a>:
command-line “cat” tool</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.copy.html">tools.copy</a>:
command-line “copy” tool</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.echo.html">tools.echo</a>:
command-line"echo" tool</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.grep.html">tools.grep</a>:
command-line"grep" tool</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.image-analyzer.html">tools.image-analyzer</a>:
examine Factor image files</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.move.html">tools.move</a>:
command-line “move” tool</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.seq.html">tools.seq</a>:
command-line “seq” tool</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.tree.html">tools.tree</a>:
command-line “tree” tool</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.uniq.html">tools.uniq</a>:
command-line “uniq” tool</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.wc.html">tools.wc</a>:
command-line “wc” tool</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.gadgets.charts.html">ui.gadgets.charts</a>:
UI gadget for rendering line charts</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.gadgets.frame-buffer.html">ui.gadgets.frame-buffer</a>:
UI gadget that supports a couple games</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.theme.html">ui.theme</a>:
support for light and dark themes</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.comdlg32.html">windows.comdlg32</a>:
using <code>comdlg32.dll</code> functions</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.crypt32.html">windows.crypt32</a>:
using <code>crypt32.dll</code> functions</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.dragdrop-listener.html">windows.dragdrop-listener</a>:
allow dropping files into the Listener</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.dropfiles.html">windows.dropfiles</a>:
implementing “file drop” gesture on Windows</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.surface-dial.html">windows.surface-dial</a>:
support for Microsoft Surface Dial</li>
<li><a href="https://docs.factorcode.org/content/vocab-xdg.html">xdg</a>: support
for <a href="https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html">XDG Base Directory
Specification</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-zealot.html">zealot</a>:
continuous build and testing library</li>
</ul>
<h3 id="improved-libraries">Improved Libraries:</h3>
<ul>
<li><a href="https://docs.factorcode.org/content/vocab-boids.html">boids</a>: adding
Cocoa TouchBar buttons</li>
<li><a href="https://docs.factorcode.org/content/vocab-cap.html">cap</a>: support
for screenshot on retina displays</li>
<li><a href="https://docs.factorcode.org/content/vocab-checksums.html">checksums</a>:
cleanup and improved checksum API</li>
<li><a href="https://docs.factorcode.org/content/vocab-color-table.html">color-table</a>:
add columns for filled color and hex code</li>
<li><a href="https://docs.factorcode.org/content/vocab-cpu.architecture.html">cpu.architecture</a>:
adding <a href="https://re.factorcode.org/2015/06/bit-test.html">Bit Test
primitive</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-cpu.x86.64.html">cpu.x86.64</a>:
fix for return register on x86.64</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.hex.html">colors.hex</a>:
support varying length hex notations</li>
<li><a href="https://docs.factorcode.org/content/vocab-combinators.extras.html">combinators.extras</a>:
adding <code>swap-when</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-compiler.cfg.html">compiler.cfg</a>:
fix scheduling issue</li>
<li><a href="https://docs.factorcode.org/content/vocab-concurrency.distributed.html">concurrency.distributed</a>:
fix serializing of remote threads</li>
<li><a href="https://docs.factorcode.org/content/vocab-elf.html">elf</a>: cleanup
and performance improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-formatting.html">formatting</a>:
support “space” prefix for numbers</li>
<li><a href="https://docs.factorcode.org/content/vocab-furnace.utilities.html">furnace.utilities</a>:
improve template path resolution</li>
<li><a href="https://docs.factorcode.org/content/vocab-heaps.html">heaps</a>:
<a href="https://re.factorcode.org/2014/12/heaps.html">performance
improvements</a> to
the heap algorithm</li>
<li><a href="https://docs.factorcode.org/content/vocab-help.html">help</a>: default
word help (if not provided)</li>
<li><a href="https://docs.factorcode.org/content/vocab-html.parser.printer.html">html.parser.printer</a>:
improved plain text printer</li>
<li><a href="https://docs.factorcode.org/content/vocab-http.client.html">http.client</a>:
support HTTP proxies and bug fixes</li>
<li><a href="https://docs.factorcode.org/content/vocab-http.server.html">http.server</a>:
fix use of port remapping</li>
<li><a href="https://docs.factorcode.org/content/vocab-http.server.static.html">http.server.static</a>:
support sorting by columns</li>
<li><a href="https://docs.factorcode.org/content/vocab-images.png.html">images.png</a>:
support reading color profiles</li>
<li><a href="https://docs.factorcode.org/content/vocab-images.tiff.html">images.tiff</a>:
fix bugs exposed by AFL test suite</li>
<li><a href="https://docs.factorcode.org/content/vocab-interpolate.html">interpolate</a>:
simplify and <a href="https://re.factorcode.org/2015/04/interpolate.html">some minor
improvements</a></li>
<li><a href="https://docs.factorcode.org/content/vocab-io.directories.search.html">io.directories.search</a>:
simplify interface and allow BFS and DFS searches</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.encodings.8-bit.html">io.encodings.8-bit</a>:
more encodings and simplify hierarchy</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.files.info.windows.html">io.files.info.windows</a>:
fix <code>file-readable?</code> to check current user’s permissions</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.files.unique.html">io.files.unique</a>:
create multiple unique files at same time</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.launcher.html">io.launcher</a>:
cleanup interface and support hidden processes on Windows</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.c.html">io.streams.c</a>:
faster <code>M\ c-reader stream-read-until</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-json.writer.html">json.writer</a>:
better support for non-string keys, unicode and special floats</li>
<li><a href="https://docs.factorcode.org/content/vocab-lexer.html">lexer</a>:
support universal comments</li>
<li><a href="https://docs.factorcode.org/content/vocab-logging.server.html">logging.server</a>:
simplify code</li>
<li><a href="https://docs.factorcode.org/content/vocab-macho.html">macho</a>:
updated structures and tests</li>
<li><a href="https://docs.factorcode.org/content/vocab-mason.release.html">mason.release</a>:
code signing on macOS and Windows</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.binpack.html">math.binpack</a>:
faster <code>binpack</code> and <code>map-binpack</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.combinatorics.html">math.combinatorics</a>:
performance improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.extras.html">math.extras</a>:
adding Möbius function and Kelly criterion</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.functions.html">math.functions</a>:
adding <code>logn</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.parser.html">math.parser</a>:
faster <code>format-float</code> for known format strings</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.primes.erato.html">math.primes.erato</a>:
performance improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.primes.factors.html">math.primes.factors</a>:
command-line support</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.primes.solovay-strassen.html">math.primes.solovay-strassen</a>:
adding Solovay-Strassen primality test</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.statistics.html">math.statistics</a>:
fixes and new words</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.text.french.html">math.text.french</a>:
more proper use of French</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.transforms.bwt.html">math.transforms.bwt</a>:
performance improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.vectors.html">math.vectors</a>:
adding <code>v>integer</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-mime.types.html">mime.types</a>:
update for new mime types</li>
<li><a href="https://docs.factorcode.org/content/vocab-multiline.html">multiline</a>:
support “Lua-style” strings</li>
<li><a href="https://docs.factorcode.org/content/vocab-peg.ebnf.html">peg.ebnf</a>:
handle escapes in strings better</li>
<li><a href="https://docs.factorcode.org/content/vocab-pong.html">pong</a>: bug
fixes and a bit fancier graphics</li>
<li><a href="https://docs.factorcode.org/content/vocab-readline-listener.html">readline-listener</a>:
adding vocab word completions</li>
<li><a href="https://docs.factorcode.org/content/vocab-reddit.html">reddit</a>: fix
for Reddit API changes</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.extras">sequences.extras</a>:
some possibly useful new words</li>
<li><a href="https://docs.factorcode.org/content/vocab-simple-tokenizer.html">simple-tokenizer</a>:
consider TAB, CR, LF as spaces</li>
<li><a href="https://docs.factorcode.org/content/vocab-smalltalk.html">smalltalk</a>:
cleanup grammar and fix underscore bug</li>
<li><a href="https://docs.factorcode.org/content/vocab-splitting.monotonic.html">splitting.monotonic</a>:
faster <code>monotonic-split</code>.</li>
<li><a href="https://docs.factorcode.org/content/vocab-sorting.html">sorting</a>:
faster <code>sort-keys</code> and <code>sort-values</code> on <code>hashtables</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-sorting.extras.html">sorting.extras</a>:
faster <code>map-sort</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-strings.parser.html">strings.parser</a>:
allow both <code>\u{snowman}</code> and <code>\u{2603}</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-terminfo.html">terminfo</a>:
look in multiple directories for terminfo files</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.disassembler.html">tools.disassembler</a>:
allow disassemble of compose and curry</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.dns.html">tools.dns</a>:
enable use from command-line</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.hexdump.html">tools.hexdump</a>:
support stdin hexdump</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.scaffold.html">tools.scaffold</a>:
Better examples scaffold. Add more types.</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.test.html">tools.test</a>:
adding <code>with-test-file</code> and <code>with-test-directory</code> helper words</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.which.html">tools.which</a>:
enable use from command-line</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.browser.html">ui.tools.browser</a>:
adding Cocoa TouchBar buttons</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.inspector.html">ui.tools.inspector</a>:
improved performance with large arrays and hashtables</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.listener.html">ui.tools.listener</a>:
adding Cocoa TouchBar buttons, Ctrl-Break support on Windows, and
vocab word completions</li>
<li><a href="https://docs.factorcode.org/content/vocab-unicode.html">unicode</a>:
simplified hierarchy</li>
<li><a href="https://docs.factorcode.org/content/vocab-urls.html">urls</a>: improved
parsing of scheme component</li>
<li><a href="https://docs.factorcode.org/content/vocab-wrap.html">wrap</a>:
performance improvements using better algorithm</li>
<li><a href="https://docs.factorcode.org/content/vocab-xkcd.html">xkcd</a>: fix
mouseover text.</li>
<li><a href="https://docs.factorcode.org/content/vocab-xml.data.html">xml.data</a>:
make tags support assoc protocol</li>
<li><a href="https://docs.factorcode.org/content/vocab-z-algorithm.html">z-algorithm</a>:
faster <code>z-values</code></li>
</ul>
Minesweeper
https://re.factorcode.org/2018/02/minesweeper.html
Mon, 12 Feb 2018 13:39:00 -0800https://re.factorcode.org/2018/02/minesweeper.html<p><a href="https://en.wikipedia.org/wiki/Minesweeper_(video_game)">Minesweeper</a> is
a fun game that was probably made most popular by its inclusion in
<a href="https://en.wikipedia.org/wiki/Microsoft_Minesweeper">various Microsoft Windows
versions</a> since the
early 1990’s.</p>
<p>I thought it would be fun to build a simple Minesweeper clone using
<a href="https://factorcode.org">Factor</a>.</p>
<p>
<img src="https://re.factorcode.org/images/2018-02-12-minesweeper-ZFQPR9a.png" alt="" width="224" height="330" />
</p>
<p>You can run this by updating to the <a href="https://github.com/factor/factor">latest
code</a> and running:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"minesweeper"</span> run
</span></span></code></pre></div><h3 id="game-engine">Game Engine</h3>
<p>We are going to represent our game grid as a two-dimensional array of
“cells”.</p>
<p>Each cell contains the number of mines contained in the (up to eight)
adjacent cells, whether the cell contains a mine, and a “state” flag
showing whether the cell was <code>+clicked+</code>, <code>+flagged+</code>, or marked with a
<code>+question+</code> mark.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYMBOLS: </span><span class="nf">+clicked+</span> <span class="nf">+flagged+</span> <span class="nf">+question+</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">cell</span> <span class="nv">#adjacent</span> <span class="nv">mined?</span> <span class="nv">state</span> <span class="k">;
</span></span></span></code></pre></div><p>Making a <code>(rows, cols)</code> grid of cells:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">make-cells</span> <span class="nf">( </span><span class="nv">rows</span> <span class="nv">cols</span> <span class="nf">-- </span><span class="nv">cells</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ _ [ cell <span class="nb">new </span>] <span class="nb">replicate </span>] <span class="nb">replicate </span><span class="k">;
</span></span></span></code></pre></div><p>We can lookup a particular cell using its <code>(row, col)</code> index:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">cell-at</span> <span class="nf">( </span><span class="nv">cells</span> <span class="nv">row</span> <span class="nv">col</span> <span class="nf">-- </span><span class="nv">cell/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> row cells <span class="nb">?nth </span>[ col <span class="nb">swap ?nth </span>] [ <span class="no">f </span>] <span class="nb">if* </span><span class="k">;
</span></span></span></code></pre></div><p>Placing a number of mines into cells, just looks for a certain number of
unmined cells at random, and then marks them as mined:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">unmined-cell</span> <span class="nf">( </span><span class="nv">cells</span> <span class="nf">-- </span><span class="nv">cell</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span>[ <span class="nb">dup </span>mined?>> ] [ <span class="nb">drop dup </span>random random ] <span class="nb">do while nip </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">place-mines</span> <span class="nf">( </span><span class="nv">cells</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">cells</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>unmined-cell <span class="no">t </span>>>mined? <span class="nb">drop </span>] <span class="nb">times </span><span class="k">;
</span></span></span></code></pre></div><p>We can count the number of adjacent mines for each cell, by looking at
its neighbors:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">neighbors</span> {
</span></span><span class="line"><span class="cl"> { <span class="m">-1 -1 </span>} { <span class="m">-1 </span> <span class="m">0 </span>} { <span class="m">-1 </span> <span class="m">1 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">0 -1 </span>} { <span class="m">0 </span> <span class="m">1 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">1 -1 </span>} { <span class="m">1 </span> <span class="m">0 </span>} { <span class="m">1 </span> <span class="m">1 </span>}
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">adjacent-mines</span> <span class="nf">( </span><span class="nv">cells</span> <span class="nv">row</span> <span class="nv">col</span> <span class="nf">-- </span><span class="nv">#mines</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> neighbors [
</span></span><span class="line"><span class="cl"> <span class="nb">first2 </span>[ <span class="nb">+ </span>] <span class="nb">bi-curry@ bi* </span>cell-at
</span></span><span class="line"><span class="cl"> [ mined?>> ] [ <span class="no">f </span>] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">with with with count </span><span class="k">;
</span></span></span></code></pre></div><p>The <code>each-cell</code> word looks at all the cells, helping us update the
“adjacent mines” counts:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">each-cell</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">cells</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">row</span> <span class="nv">col</span> <span class="nv">cell</span> <span class="nf">-- </span><span class="nv">...</span> <span class="nf">) -- </span><span class="nv">...</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> cells [| row |
</span></span><span class="line"><span class="cl"> [| cell col | row col cell quot <span class="nb">call </span>] <span class="nb">each-index
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-index </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">update-counts</span> <span class="nf">( </span><span class="nv">cells</span> <span class="nf">-- </span><span class="nv">cells</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> cells [| row col cell |
</span></span><span class="line"><span class="cl"> cells row col adjacent-mines cell #adjacent<<
</span></span><span class="line"><span class="cl"> ] each-cell cells <span class="k">;
</span></span></span></code></pre></div><p>Since we aren’t storing the number of rows and columns, we can get it
from the array of cells:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">cells-dim</span> <span class="nf">( </span><span class="nv">cells</span> <span class="nf">-- </span><span class="nv">rows</span> <span class="nv">cols</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>] [ <span class="nb">first length </span>] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>We can get the number of mines contained in the grid by counting them
up:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">#mines</span> <span class="nf">( </span><span class="nv">cells</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ mined?>> ] <span class="nb">count </span>] <span class="nb">map-sum </span><span class="k">;
</span></span></span></code></pre></div><p>We can reset the game by making new cells and then placing the same
number of mines in them:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">reset-cells</span> <span class="nf">( </span><span class="nv">cells</span> <span class="nf">-- </span><span class="nv">cells</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ cells-dim make-cells ] [ #mines place-mines ] <span class="nb">bi </span>update-counts <span class="k">;
</span></span></span></code></pre></div><p>The player wins if they click on all cells that aren’t mines:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">won?</span> <span class="nf">( </span><span class="nv">cells</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ { [ state>> +clicked+ <span class="nb">= </span>] [ mined?>> ] } 1|| ] <span class="nb">all? </span>] <span class="nb">all? </span><span class="k">;
</span></span></span></code></pre></div><p>The player loses if they click on any cell that’s a mine:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">lost?</span> <span class="nf">( </span><span class="nv">cells</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ { [ state>> +clicked+ <span class="nb">= </span>] [ mined?>> ] } 1&& ] <span class="nb">any? </span>] <span class="nb">any? </span><span class="k">;
</span></span></span></code></pre></div><p>And then the game is over if the player either wins or loses:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">game-over?</span> <span class="nf">( </span><span class="nv">cells</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { [ lost? ] [ won? ] } 1|| <span class="k">;
</span></span></span></code></pre></div><p>We can tell this is a new game if no cells are clicked on:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">new-game?</span> <span class="nf">( </span><span class="nv">cells</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ state>> +clicked+ <span class="nb">= </span>] <span class="nb">any? </span>] <span class="nb">any? not </span><span class="k">;
</span></span></span></code></pre></div><p>When we click on a cell, if it is not adjacent to any mines, we click on
all the “clickable” (non-mined) cells around it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">DEFER:</span> <span class="nf">click-cell-at</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">click-cells-around</span> <span class="nf">( </span><span class="nv">cells</span> <span class="nv">row</span> <span class="nv">col</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> neighbors [
</span></span><span class="line"><span class="cl"> <span class="nb">first2 </span>[ row <span class="nb">+ </span>] [ col <span class="nb">+ </span>] <span class="nb">bi* </span>:> <span class="nf">( </span><span class="nv">row'</span> <span class="nv">col'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> cells row' col' cell-at [
</span></span><span class="line"><span class="cl"> mined?>> [
</span></span><span class="line"><span class="cl"> cells row' col' click-cell-at
</span></span><span class="line"><span class="cl"> ] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">when*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>Handle clicking a cell. If it’s the first click and the cell is mined,
we move it to another random cell, then continue with the click. The
click is ignored if the cell was already clicked or flagged. Continue
clicking around any cells that have no adjacent mines and are not
themselves mined.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">click-cell-at</span> <span class="nf">( </span><span class="nv">cells</span> <span class="nv">row</span> <span class="nv">col</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> cells row col cell-at [
</span></span><span class="line"><span class="cl"> cells new-game? [
</span></span><span class="line"><span class="cl"> <span class="c">! first click shouldn't be a mine</span>
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>mined?>> [
</span></span><span class="line"><span class="cl"> cells unmined-cell <span class="no">t </span>>>mined? <span class="nb">drop </span><span class="no">f </span>>>mined?
</span></span><span class="line"><span class="cl"> cells update-counts <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>state>> { +clicked+ +flagged+ } <span class="nb">member? </span>[ <span class="nb">drop </span>] [
</span></span><span class="line"><span class="cl"> +clicked+ >>state
</span></span><span class="line"><span class="cl"> { [ mined?>> <span class="nb">not </span>] [ #adjacent>> <span class="m">0 </span><span class="nb">= </span>] } 1&& [
</span></span><span class="line"><span class="cl"> cells row col click-cells-around
</span></span><span class="line"><span class="cl"> ] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">when* </span><span class="k">;
</span></span></span></code></pre></div><p>Handle marking a cell. First by flagging it as a likely mine, or marking
with a question mark to come back to later. If the cell is not clicked,
we just cycle through flagging, question, or not clicked.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">mark-cell-at</span> <span class="nf">( </span><span class="nv">cells</span> <span class="nv">row</span> <span class="nv">col</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> cells row col cell-at [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>state>> {
</span></span><span class="line"><span class="cl"> { +clicked+ [ +clicked+ ] }
</span></span><span class="line"><span class="cl"> { +flagged+ [ +question+ ] }
</span></span><span class="line"><span class="cl"> { +question+ [ <span class="no">f </span>] }
</span></span><span class="line"><span class="cl"> { <span class="no">f </span>[ +flagged+ ] }
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span>>>state <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">when* </span><span class="k">;
</span></span></span></code></pre></div><h3 id="graphical-interface">Graphical Interface</h3>
<p>Our graphical interface is going to consist of a
<a href="https://docs.factorcode.org:8080/content/word-gadget,ui.gadgets.html">gadget</a>
with an array of cells and a cache of <a href="https://docs.factorcode.org:8080/content/word-__lt__texture__gt__%2Copengl.textures.html">OpenGL texture
objects</a>
that can be easily drawn on the screen.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">grid-gadget</span> < <span class="nc">gadget</span> <span class="nv">cells</span> <span class="nv">textures</span> <span class="k">;
</span></span></span></code></pre></div><p>When you make a new <code>grid-gadget</code>, it initializes the game to a
specified number of rows, columns, and number of mines:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf"><grid-gadget></span> <span class="nf">( </span><span class="nv">rows</span> <span class="nv">cols</span> <span class="nv">mines</span> <span class="nf">-- </span><span class="nv">gadget</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> grid-gadget <span class="nb">new
</span></span></span><span class="line"><span class="cl"> rows cols make-cells
</span></span><span class="line"><span class="cl"> mines place-mines update-counts >>cells
</span></span><span class="line"><span class="cl"> H{ } <span class="nb">clone </span>>>textures
</span></span><span class="line"><span class="cl"> COLOR: gray <solid> >>interior <span class="k">;
</span></span></span></code></pre></div><p>When
<a href="https://docs.factorcode.org:8080/content/word-ungraft__star__,ui.gadgets.html">ungraft*</a>
is called to indicate the gadget is no longer visible on the screen, we
clean up the cached textures:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">grid-gadget</span> <span class="nf">ungraft*</span>
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>find-gl-context
</span></span><span class="line"><span class="cl"> [ <span class="nb">values </span>dispose-each H{ } <span class="nb">clone </span>] change-textures
</span></span><span class="line"><span class="cl"> call-next-method <span class="k">;
</span></span></span></code></pre></div><p>Our images are going to be <code>32 x 32</code> squares, so the preferred dimension
is number of rows and columns times 32 pixels for each square.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">grid-gadget</span> <span class="nf">pref-dim*</span>
</span></span><span class="line"><span class="cl"> cells>> cells-dim [ <span class="m">32 </span><span class="nb">* </span>] <span class="nb">bi@ swap 2array </span><span class="k">;
</span></span></span></code></pre></div><p>Some slightly complex logic to decide which image to display for each
cell, taking into account whether the game is over so we can show the
positions of all the mines and whether the player was correct in
flagging a cell as mined, etc:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">cell-image-path</span> <span class="nf">( </span><span class="nv">cell</span> <span class="nv">game-over?</span> <span class="nf">-- </span><span class="nv">image-path</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> game-over? cell mined?>> <span class="nb">and </span>[
</span></span><span class="line"><span class="cl"> cell state>> +clicked+ <span class="nb">= </span><span class="s">"mineclicked.gif"</span> <span class="s">"mine.gif"</span> <span class="nb">?
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> cell state>
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { +question+ [ <span class="s">"question.gif"</span> ] }
</span></span><span class="line"><span class="cl"> { +flagged+ [ game-over? <span class="s">"misflagged.gif"</span> <span class="s">"flagged.gif"</span> <span class="nb">? </span>] }
</span></span><span class="line"><span class="cl"> { +clicked+ [
</span></span><span class="line"><span class="cl"> cell mined?>> [
</span></span><span class="line"><span class="cl"> <span class="s">"mine.gif"</span>
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> cell #adjacent>> <span class="m">0 </span><span class="nb">or </span>number>string
</span></span><span class="line"><span class="cl"> <span class="s">"open"</span> <span class="s">".gif"</span> <span class="nb">surround
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span>] }
</span></span><span class="line"><span class="cl"> { <span class="no">f </span>[ <span class="s">"blank.gif"</span> ] }
</span></span><span class="line"><span class="cl"> } <span class="nb">case
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="s">"vocab:minesweeper/_resources/"</span> <span class="nb">prepend </span><span class="k">;
</span></span></span></code></pre></div><p>Drawing a cached texture is a matter of looking up the image in our
texture cache and then rendering to the screen:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">draw-cached-texture</span> <span class="nf">( </span><span class="nv">path</span> <span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> textures>> [ load-image { <span class="m">0 0 </span>} <texture> ] <span class="nb">cache
</span></span></span><span class="line"><span class="cl"> [ dim>> ] [ draw-scaled-texture ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>Drawing our gadget, is basically drawing all of the cells at their
proper locations on the screen:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M::</span> <span class="nc">grid-gadget</span> <span class="nf">draw-gadget*</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> gadget cells>> game-over? :> game-over?
</span></span><span class="line"><span class="cl"> gadget cells>> [| row col cell |
</span></span><span class="line"><span class="cl"> col row [ <span class="m">32 </span><span class="nb">* </span>] <span class="nb">bi@ 2array </span>[
</span></span><span class="line"><span class="cl"> cell game-over? cell-image-path
</span></span><span class="line"><span class="cl"> gadget draw-cached-texture
</span></span><span class="line"><span class="cl"> ] with-translation
</span></span><span class="line"><span class="cl"> ] each-cell <span class="k">;
</span></span></span></code></pre></div><p>Basic handling for the gadget being left-clicked on:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">on-click</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> gadget hand-rel <span class="nb">first2 </span>:> <span class="nf">( </span><span class="nv">w</span> <span class="nv">h</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> h w [ <span class="m">32 </span><span class="nb">/i </span>] <span class="nb">bi@ </span>:> <span class="nf">( </span><span class="nv">row</span> <span class="nv">col</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> gadget cells>> :> cells
</span></span><span class="line"><span class="cl"> cells game-over? [
</span></span><span class="line"><span class="cl"> cells row col click-cell-at
</span></span><span class="line"><span class="cl"> ] <span class="nb">unless </span>gadget relayout-1 <span class="k">;
</span></span></span></code></pre></div><p>Basic handling for the gadget being right-clicked on:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">on-mark</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> gadget hand-rel <span class="nb">first2 </span>:> <span class="nf">( </span><span class="nv">w</span> <span class="nv">h</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> h w [ <span class="m">32 </span><span class="nb">/i </span>] <span class="nb">bi@ </span>:> <span class="nf">( </span><span class="nv">row</span> <span class="nv">col</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> gadget cells>> :> cells
</span></span><span class="line"><span class="cl"> cells game-over? [
</span></span><span class="line"><span class="cl"> cells row col mark-cell-at
</span></span><span class="line"><span class="cl"> ] <span class="nb">unless </span>gadget relayout-1 <span class="k">;
</span></span></span></code></pre></div><p>Logic for creating new games of varying difficulties: easy, medium, and
hard:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">new-game</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nv">rows</span> <span class="nv">cols</span> <span class="nv">mines</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ make-cells ] <span class="nb">dip </span>place-mines update-counts >>cells
</span></span><span class="line"><span class="cl"> relayout-window <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">com-easy</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- ) </span><span class="m">7 7 10 </span>new-game <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">com-medium</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- ) </span><span class="m">15 15 40 </span>new-game <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">com-hard</span> <span class="nf">( </span><span class="nv">gadget</span> <span class="nf">-- ) </span><span class="m">15 30 99 </span>new-game <span class="k">;
</span></span></span></code></pre></div><p>We set our gesture handlers for keyboard and mouse inputs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">grid-gadget {
</span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">"1"</span> } } [ com-easy ] }
</span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">"2"</span> } } [ com-medium ] }
</span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">"3"</span> } } [ com-hard ] }
</span></span><span class="line"><span class="cl"> { T{ button-up { # <span class="m">1 </span>} } [ on-click ] }
</span></span><span class="line"><span class="cl"> { T{ button-up { # <span class="m">3 </span>} } [ on-mark ] }
</span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">" "</span> } } [ on-mark ] }
</span></span><span class="line"><span class="cl">} set-gestures
</span></span></code></pre></div><p>And a main word that creates an easy game in our <code>grid-gadget</code> and opens
it in a new window:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">MAIN-WINDOW: run-minesweeper {
</span></span><span class="line"><span class="cl"> { title <span class="s">"Minesweeper"</span> }
</span></span><span class="line"><span class="cl"> { window-controls
</span></span><span class="line"><span class="cl"> { normal-title-bar close-button minimize-button } }
</span></span><span class="line"><span class="cl"> } <span class="m">7 7 10 </span><grid-gadget> >>gadgets <span class="k">;
</span></span></span></code></pre></div><p>The implementation above is about 200 lines of code and contains the
full game logic. The <a href="https://github.com/factor/factor/blob/master/extra/minesweeper/minesweeper.factor">final
version</a>
is just under 300 lines of code, and adds:</p>
<ul>
<li>support for a toolbar to easily start new games</li>
<li>the traditional counter of the number of mines remaining</li>
<li>display of the number of seconds elapsed</li>
<li>a smiley face showing a funny “uh-oh!” face when you are about to click as well as winning and losing smileys</li>
<li>support for <a href="https://en.wikipedia.org/wiki/Retina_Display">retina displays</a> using <code>2x</code> images</li>
</ul>
$7.11
https://re.factorcode.org/2017/02/7-11.html
Sat, 11 Feb 2017 14:05:00 -0800https://re.factorcode.org/2017/02/7-11.html<p>Today, <a href="https://wrongsideofmemphis.wordpress.com/2017/02/11/7-11-in-four-prices-and-the-decimal-type-revisited/">someone
blogged</a>
about a fun problem:</p>
<blockquote>
<p><em>“A mathematician purchased four items in a grocery store. He noticed
that when he added the prices of the four items, the sum came to
$7.11, and when he multiplied the prices of the four items, the
product came to $7.11.”</em></p>
</blockquote>
<p>In some ways, this is similar to the <a href="https://re.factorcode.org/2015/06/send-more-money.html">SEND + MORE =
MONEY</a>
problem that I blogged about awhile ago. You can always approach this
problem with an direct and iterative solution, but instead we will use
the <a href="https://docs.factorcode.org/content/vocab-backtrack.html">backtrack</a>
vocabulary to solve this problem with less code.</p>
<p>We’ll be solving this exactly, using integer “numbers of cents”,
progressively restricting the options, and then calling
<a href="https://docs.factorcode.org/content/word-fail%2Cbacktrack.html">fail</a> if
the solution is not found, so we check the next. The first valid
solution will be returned:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">solve-711</span> <span class="nf">( -- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">711 </span><iota> amb-lazy :> w
</span></span><span class="line"><span class="cl"> <span class="m">711 </span>w <span class="nb">- </span><iota> amb-lazy :> x
</span></span><span class="line"><span class="cl"> <span class="m">711 </span>w <span class="nb">- </span>x <span class="nb">- </span><iota> amb-lazy :> y
</span></span><span class="line"><span class="cl"> <span class="m">711 </span>w <span class="nb">- </span>x <span class="nb">- </span>y <span class="nb">- </span>:> z
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> w x <span class="nb">* </span>y <span class="nb">* </span>z <span class="nb">* </span><span class="m">711,000,000 </span><span class="nb">= </span>[ fail ] <span class="nb">unless
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> { w x y z } <span class="k">;
</span></span></span></code></pre></div><p>Using it, we get our answer:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> solve-711 <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">120 125 150 316 </span>}
</span></span></code></pre></div><p>And that is: $1.20, $1.25, $1.50, and $3.16.</p>
Dirty Money: Code Challenge
https://re.factorcode.org/2017/02/dirty-money-code-challenge.html
Sun, 05 Feb 2017 15:59:00 -0800https://re.factorcode.org/2017/02/dirty-money-code-challenge.html<p>There’s a fun coding challenge to <a href="https://jorin.me/dirtymoney">follow the dirty
money</a> that I discovered recently.</p>
<blockquote>
<p>A shady Internet business has been discovered.</p>
<p>The website has been made public by a whistle blower. We have enough
evidence about the dirty deals they did. But to charge them we need to
get hands on precise numbers about the transactions that happened on
their platform.</p>
<p>Unfortunately no record of the transactions could be seized so far.
The only hint we have is this one transaction:</p>
<p><a href="https://gist.githubusercontent.com/jorinvo/6f68380dd07e5db3cf5fd48b2465bb04/raw/c02b1e0b45ecb2e54b36e4410d0631a66d474323/fd0d929f-966f-4d1a-89cd-feee5a1c5347.json">fd0d929f-966f-4d1a-89cd-feee5a1c5347.json</a></p>
<p>What we need is the total of all transactions in Dollar. Can you trace
down all other transactions and get the total?</p>
<p>Be careful to count each transaction only once, even if it is linked
multiple times. You can use whatever tool works best.</p>
</blockquote>
<p>We need a way to extract the dollar amount from the transaction text.
The dollars might be specified with period or a comma to represent the
decimal point. We use <a href="https://docs.factorcode.org/content/article-regexp.html">regular
expressions</a> to
look for the dollar amount and then convert to a number.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">dollars</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">$</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> R/ \$\d*[,.]\d+/ first-match <span class="nb">rest
</span></span></span><span class="line"><span class="cl"> <span class="s">","</span> <span class="s">"."</span> replace string>number <span class="k">;
</span></span></span></code></pre></div><p>We will use a
<a href="https://docs.factorcode.org/content/article-hash-sets.html">hash-set</a> of
visited links, and only if the link has not been visited will we
<a href="https://docs.factorcode.org/content/word-http-get,http.client.html">http-get</a>
the contents of the URL, parse the
<a href="https://docs.factorcode.org/content/article-json.reader.html">JSON</a>, and
extract the dollar amount of both the transaction and any links it
contains. The set of visited links will tell us how many total
transactions we traced.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">transaction</span> <span class="nf">( </span><span class="nv">url</span> <span class="nv">visited</span> <span class="nf">-- </span><span class="nv">dollars</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> url visited ?adjoin [
</span></span><span class="line"><span class="cl"> url http-get <span class="nb">nip </span>json> :> data
</span></span><span class="line"><span class="cl"> data <span class="s">"content"</span> <span class="nb">of </span>dollars
</span></span><span class="line"><span class="cl"> data <span class="s">"links"</span> <span class="nb">of </span>[ visited transaction ] <span class="nb">map-sum +
</span></span></span><span class="line"><span class="cl"> ] [ <span class="m">0 </span>] <span class="nb">if </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">transactions</span> <span class="nf">( </span><span class="nv">url</span> <span class="nf">-- </span><span class="nv">dollars</span> <span class="nv">#transactions</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> HS{ } <span class="nb">clone </span>[ transaction ] [ cardinality ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>That’s all we need to solve the problem. We can run this with the
initial URL and get the answer:</p>
<blockquote>
<p>$9064.79 in 50 transactions.</p>
</blockquote>
The Twelve Days of Christmas
https://re.factorcode.org/2016/12/the-twelve-days-of-christmas.html
Sat, 24 Dec 2016 11:21:00 -0800https://re.factorcode.org/2016/12/the-twelve-days-of-christmas.html<p><a href="https://programmingpraxis.com">Programming Praxis</a> posted a task to
<a href="https://programmingpraxis.com/2016/12/23/a-partridge-in-a-pear-tree/">write a program to print the
words</a>
to <a href="https://en.wikipedia.org/wiki/The_Twelve_Days_of_Christmas_(song)">The Twelve Days of
Christmas</a>
song. We are going to solve it in <a href="https://factorcode.org">Factor</a>.</p>
<p>We start off by defining all the gifts received on each day:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">gifts</span> {
</span></span><span class="line"><span class="cl"> { <span class="s">"first"</span> <span class="s">"a partridge in a pear tree"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"second"</span> <span class="s">"two turtle doves and "</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"third"</span> <span class="s">"three French hens, "</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"fourth"</span> <span class="s">"four calling birds, "</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"fifth"</span> <span class="s">"five golden rings, "</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"sixth"</span> <span class="s">"six geese a-laying, "</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"seventh"</span> <span class="s">"seven swans a-swimming, "</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"eighth"</span> <span class="s">"eight maids a-milking, "</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"ninth"</span> <span class="s">"nine ladies dancing, "</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"tenth"</span> <span class="s">"ten lords a-leaping, "</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"eleventh"</span> <span class="s">"eleven pipers piping, "</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"twelfth"</span> <span class="s">"twelve drummers drumming, "</span> }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Then we iterate through the days, gathering all the gifts in reverse for
each day, and formatting them, and wrapping to 72 columns of text for
display.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">gifts [
</span></span><span class="line"><span class="cl"> [ <span class="nb">first </span>] [ <span class="m">1 </span><span class="nb">+ </span>gifts <span class="nb">swap head values reverse concat </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> <span class="s">"On the %s day of Christmas my true love gave to me %s."</span> sprintf
</span></span><span class="line"><span class="cl"> <span class="m">72 </span>wrap-string <span class="nb">print nl
</span></span></span><span class="line"><span class="cl">] <span class="nb">each-index
</span></span></span></code></pre></div><p>Which gives us these words:</p>
<blockquote>
<p>On the first day of Christmas my true love gave to me a partridge in a
pear tree.</p>
<p>On the second day of Christmas my true love gave to me two turtle
doves and a partridge in a pear tree.</p>
<p>On the third day of Christmas my true love gave to me three French
hens, two turtle doves and a partridge in a pear tree.</p>
<p>On the fourth day of Christmas my true love gave to me four calling
birds, three French hens, two turtle doves and a partridge in a pear
tree.</p>
<p>On the fifth day of Christmas my true love gave to me five golden
rings, four calling birds, three French hens, two turtle doves and a
partridge in a pear tree.</p>
<p>On the sixth day of Christmas my true love gave to me six geese
a-laying, five golden rings, four calling birds, three French hens,
two turtle doves and a partridge in a pear tree.</p>
<p>On the seventh day of Christmas my true love gave to me seven swans
a-swimming, six geese a-laying, five golden rings, four calling birds,
three French hens, two turtle doves and a partridge in a pear tree.</p>
<p>On the eighth day of Christmas my true love gave to me eight maids
a-milking, seven swans a-swimming, six geese a-laying, five golden
rings, four calling birds, three French hens, two turtle doves and a
partridge in a pear tree.</p>
<p>On the ninth day of Christmas my true love gave to me nine ladies
dancing, eight maids a-milking, seven swans a-swimming, six geese
a-laying, five golden rings, four calling birds, three French hens,
two turtle doves and a partridge in a pear tree.</p>
<p>On the tenth day of Christmas my true love gave to me ten lords
a-leaping, nine ladies dancing, eight maids a-milking, seven swans
a-swimming, six geese a-laying, five golden rings, four calling birds,
three French hens, two turtle doves and a partridge in a pear tree.</p>
<p>On the eleventh day of Christmas my true love gave to me eleven pipers
piping, ten lords a-leaping, nine ladies dancing, eight maids
a-milking, seven swans a-swimming, six geese a-laying, five golden
rings, four calling birds, three French hens, two turtle doves and a
partridge in a pear tree.</p>
<p>On the twelfth day of Christmas my true love gave to me twelve
drummers drumming, eleven pipers piping, ten lords a-leaping, nine
ladies dancing, eight maids a-milking, seven swans a-swimming, six
geese a-laying, five golden rings, four calling birds, three French
hens, two turtle doves and a partridge in a pear tree.</p>
</blockquote>
AnyBar
https://re.factorcode.org/2016/11/anybar.html
Mon, 28 Nov 2016 12:58:00 -0800https://re.factorcode.org/2016/11/anybar.html<p><a href="https://github.com/tonsky/AnyBar">AnyBar</a> is a
<a href="https://apple.com/macos">macOS</a> status indicator that displays a
“colored dot” in the menu bar that can be changed programatically. What
it means and when it changes is entirely up to the user.</p>
<p>
<img src="https://re.factorcode.org/images/2016-11-28-anybar-tk042yQ.png" alt="" width="536" height="94" />
</p>
<p>You can easily install it with <a href="https://caskroom.io/">Homebrew-cask</a>:</p>
<pre tabindex="0"><code>$ brew cask install anybar
</code></pre><p>The <a href="https://github.com/tonsky/AnyBar#alternative-clients">README</a> lists
a number of alternative clients in different programming languages. I
thought it would be fun to show how to use it from
<a href="https://factorcode.org">Factor</a>. Since AnyBar responds to AppleScript
(and I added <a href="https://re.factorcode.org/2013/10/applescript.html">support for
AppleScript</a> a
few years ago), we could do this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USE:</span> <span class="nn">cocoa.apple-script</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="s">"tell application \"AnyBar\" to set image name to \"blue\""</span>
</span></span><span class="line"><span class="cl">run-apple-script
</span></span></code></pre></div><p>The AnyBar application also listens to a UDP port (default: 1738) and
can be instructed to change from a Terminal using a simple
<a href="https://linux.die.net/man/1/echo">echo</a> |
<a href="https://linux.die.net/man/1/nc">nc</a> command:</p>
<pre tabindex="0"><code>$ echo -n "blue" | nc -4u -w0 localhost 1738
</code></pre><p>Using our
<a href="https://docs.factorcode.org/content/article-network-streams.html">networking</a>
words similarly is pretty simple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="s">"blue"</span> >byte-array <span class="s">"127.0.0.1"</span> <span class="m">1738 </span><inet4> send-once
</span></span></code></pre></div><p>But if we wanted to get more fancy, we could use
<a href="https://docs.factorcode.org/content/article-words.symbol.html">symbols</a>
to configure which AnyBar instance to send to, with default values to
make it easy to use, and
<a href="https://docs.factorcode.org/content/word-resolve-host,io.sockets.html">resolve-host</a>
to lookup hostnames:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">anybar-host</span>
</span></span><span class="line"><span class="cl"><span class="s">"localhost"</span> anybar-host <span class="nb">set-global
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">anybar-port</span>
</span></span><span class="line"><span class="cl"><span class="m">1738 </span>anybar-port <span class="nb">set-global
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">anybar</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> ascii encode
</span></span><span class="line"><span class="cl"> anybar-host <span class="nb">get </span>resolve-host <span class="nb">first
</span></span></span><span class="line"><span class="cl"> anybar-port <span class="nb">get </span>with-port send-once <span class="k">;
</span></span></span></code></pre></div><p>AnyBar is a neat little program!</p>
Reverse Factorial
https://re.factorcode.org/2016/11/reverse-factorial.html
Sat, 26 Nov 2016 22:28:00 -0800https://re.factorcode.org/2016/11/reverse-factorial.html<p>A few years ago, I wrote about implementing various
<a href="https://re.factorcode.org/2013/04/factorial.html">factorials</a> using
<a href="https://factorcode.org">Factor</a>. Recently, I came across a <a href="https://reddit.com/r/dailyprogrammer/comments/55nior/20161003_challenge_286_easy_reverse_factorial">programming
challenge</a>
to implement a “reverse factorial” function to determine what factorial
produces a number, or none if it is not a factorial.</p>
<p>To do this, we examine each factorial in order, checking against the
number being tested:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">reverse-factorial</span> <span class="nf">( </span><span class="nv">m</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1 1 </span>[ <span class="nb">2over > </span>] [ <span class="m">1 </span><span class="nb">+ </span>[ <span class="nb">* </span>] <span class="nb">keep </span>] <span class="nb">while </span>[ <span class="nb">= </span>] <span class="nb">dip and </span><span class="k">;
</span></span></span></code></pre></div><p>And some unit tests:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="m">10 </span>} [ <span class="m">3628800 </span>reverse-factorial ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">12 </span>} [ <span class="m">479001600 </span>reverse-factorial ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">3 </span>} [ <span class="m">6 </span>reverse-factorial ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="no">f </span>} [ <span class="m">18 </span>reverse-factorial ] unit-test
</span></span></code></pre></div>Gopher Server
https://re.factorcode.org/2016/10/gopher-server.html
Thu, 27 Oct 2016 14:14:00 -0700https://re.factorcode.org/2016/10/gopher-server.html<p>A few days ago, I noticed a post about building a <a href="https://blogs.perl.org/users/aaron_baugher/2015/10/the-old-becomes-new-again-a-gopher-server-in-perl-6.html">Gopher Server in Perl
6</a>.
I had already implemented a <a href="https://re.factorcode.org/2014/12/gopher.html">Gopher Client in
Factor</a>, and thought
it might be fun to show a simple Gopher Server in
<a href="https://factorcode.org">Factor</a> in around 50 lines of code.</p>
<p>Using the
<a href="https://docs.factorcode.org/content/article-io.servers.html">io.servers</a>
vocabulary, we will define a new multi-threaded server that has a
directory to serve content from and hostname that it can be accessed at:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">gopher-server</span> < <span class="nc">threaded-server</span>
</span></span><span class="line"><span class="cl"> { <span class="nv">serving-hostname</span> string }
</span></span><span class="line"><span class="cl"> { <span class="nv">serving-directory</span> string } <span class="k">;
</span></span></span></code></pre></div><p>When a file is requested, it can be streamed back to clients:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">send-file</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> binary [ [ <span class="nb">write </span>] <span class="nb">each-block </span>] with-file-reader <span class="k">;
</span></span></span></code></pre></div><p>The Gopher protocol is defined in <a href="https://tools.ietf.org/html/rfc1436">RFC
1436</a> and lists a few
differentiated file types. We use the
<a href="https://docs.factorcode.org/content/vocab-mime.types.html">mime.types</a>
vocabulary to return the correct one.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">gopher-type</span> <span class="nf">( </span><span class="nv">entry</span> <span class="nf">-- </span><span class="nv">type</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>directory? [
</span></span><span class="line"><span class="cl"> <span class="nb">drop </span><span class="s">"1"</span>
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> name>> mime-type {
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="s">"text/"</span> <span class="nb">head? </span>] [ <span class="nb">drop </span><span class="s">"0"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="s">"image/gif"</span> <span class="nb">= </span>] [ <span class="nb">drop </span><span class="s">"g"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="s">"image/"</span> <span class="nb">head? </span>] [ <span class="nb">drop </span><span class="s">"I"</span> ] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">"9"</span> ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>When a directory is requested, we can send a listing of all the
sub-directories and files it contains, sending their relative path to
the root directory being served so they can be requested properly by the
client:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">send-directory</span> <span class="nf">( </span><span class="nv">server</span> <span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> path [
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ gopher-type ] [ name>> ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>path prepend-path
</span></span><span class="line"><span class="cl"> server serving-directory>> ?head <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> server serving-hostname>
</span></span><span class="line"><span class="cl"> server insecure>
</span></span><span class="line"><span class="cl"> <span class="s">"%s%s\t%s\t%s\t%d\r\n"</span> sprintf utf8 encode <span class="nb">write
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] with-directory-entries <span class="k">;
</span></span></span></code></pre></div><p>To know which path was requested, we read the line, split on the first
tab, carriage return, or newline character we see:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-gopher-path</span> <span class="nf">( -- </span><span class="nv">path</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">readln </span>[ <span class="s">"\t\r\n"</span> <span class="nb">member? </span>] split1-when <span class="nb">drop </span><span class="k">;
</span></span></span></code></pre></div><p>With all of that built, we can now implement a word to handle a client
request:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">gopher-server</span> <span class="nf">handle-client*</span>
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>serving-directory>> read-gopher-path append-path
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>file-info directory? [
</span></span><span class="line"><span class="cl"> send-directory
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> send-file <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if flush </span><span class="k">;
</span></span></span></code></pre></div><p>Initializing a <code>gopher-server</code> instance and providing a convenience word
to start one:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><gopher-server></span> <span class="nf">( </span><span class="nv">directory</span> <span class="nv">port</span> <span class="nf">-- </span><span class="nv">server</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> utf8 gopher-server new-threaded-server
</span></span><span class="line"><span class="cl"> <span class="s">"gopher.server"</span> >>name
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span>>>insecure
</span></span><span class="line"><span class="cl"> binary >>encoding
</span></span><span class="line"><span class="cl"> <span class="s">"localhost"</span> >>serving-hostname
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span>resolve-symlinks >>serving-directory <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">start-gopher-server</span> <span class="nf">( </span><span class="nv">directory</span> <span class="nv">port</span> <span class="nf">-- </span><span class="nv">server</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <gopher-server> start-server <span class="k">;
</span></span></span></code></pre></div><p>This is available in the
<a href="https://github.com/factor/factor/blob/master/extra/gopher/server/server.factor">gopher.server</a>
vocabulary with a few improvements such as:</p>
<ul>
<li>Support for <code>.gophermap</code> files for alternate results when content is requested.</li>
<li>Support for <code>.gopherhead</code> files to print headers above directory listings.</li>
<li>Navigation to parent directories using <code>..</code> links.</li>
<li>Display file modified timestamp and file sizes.</li>
<li>Improved error handling.</li>
</ul>
Cuckoo Filters
https://re.factorcode.org/2016/08/cuckoo-filters.html
Mon, 08 Aug 2016 17:00:00 -0700https://re.factorcode.org/2016/08/cuckoo-filters.html<p>A Cuckoo filter is a <a href="https://en.wikipedia.org/wiki/Bloom_filter">Bloom
filter</a> replacement that
allows for space-efficient probabilistic membership checks. Cuckoo
filters provide the ability to add and remove items dynamically without
significantly degrading space and performance. False positive rates are
typically low.</p>
<p>This data structure is explained by Bin Fan, Dave Andersen, Michael
Kaminsky, and Michael Mitzenmacher in two papers: <a href="https://www.cs.cmu.edu/~binfan/papers/login_cuckoofilter.pdf">Cuckoo Filter: Better
Than
Bloom</a> and
<a href="https://www.cs.cmu.edu/~dga/papers/cuckoo-conext2014.pdf">Cuckoo Filter: Practically Better Than
Bloom</a>. There
is also an <a href="https://github.com/efficient/cuckoofilter">implementation in
C++</a> that can be referred to.</p>
<p>The Cuckoo filter is basically a dense <a href="https://en.wikipedia.org/wiki/Hash_table">hash
table</a> that can support high
<a href="https://en.wikipedia.org/wiki/Hash_table#Key_statistics">load factors</a>
(up to 95%) without degraded performance. Instead of storing objects, we
will store a hashed
<a href="https://en.wikipedia.org/wiki/Fingerprint_(computing)">fingerprint</a>.</p>
<h3 id="buckets">Buckets</h3>
<p>First, we need to create a number of buckets. Each bucket will hold 4
fingerprints. Load factors over 96% will cause us to grow our capacity
to the
<a href="https://docs.factorcode.org/content/word-next-power-of-2,math.html">next-power-of-2</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="c">! The number of fingerprints to store in each bucket</span>
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">bucket-size</span> <span class="m">4
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">! The maximum load factor we allow before growing the capacity</span>
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">max-load-factor</span> <span class="m">0.96
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">#buckets</span> <span class="nf">( </span><span class="nv">capacity</span> <span class="nf">-- </span><span class="nv">#buckets</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ bucket-size <span class="nb">/i next-power-of-2 </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> <span class="nb">over / </span>bucket-size <span class="nb">/ </span>max-load-factor <span class="nb">> </span>[ <span class="m">2 </span><span class="nb">* </span>] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><p>Making our buckets is then just an array of arrays:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><cuckoo-buckets></span> <span class="nf">( </span><span class="nv">capacity</span> <span class="nf">-- </span><span class="nv">buckets</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> #buckets [ bucket-size <span class="no">f </span><span class="nb"><array> </span>] <span class="nb">replicate </span><span class="k">;
</span></span></span></code></pre></div><p>Given a fingerprint, we can check if it is in a bucket by calling
<a href="https://docs.factorcode.org/content/word-member__que__,sequences.html">member?</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bucket-lookup</span> <span class="nf">( </span><span class="nv">fingerprint</span> <span class="nv">bucket</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">member? </span><span class="k">;
</span></span></span></code></pre></div><p>To insert a fingerprint into the bucket, we find the first empty slot
and replace it with the fingerprint. We return a boolean value
indicating if we were able to insert it or not:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bucket-insert</span> <span class="nf">( </span><span class="nv">fingerprint</span> <span class="nv">bucket</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ <span class="nb">not </span>] <span class="nb">find drop </span>[ <span class="nb">swap set-nth </span><span class="no">t </span>] [ <span class="nb">2drop </span><span class="no">f </span>] <span class="nb">if* </span><span class="k">;
</span></span></span></code></pre></div><p>To delete a fingerprint, we find its index (if present) and set it to false.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bucket-delete</span> <span class="nf">( </span><span class="nv">fingerprint</span> <span class="nv">bucket</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="no">f </span>] <span class="nb">2dip </span>[ <span class="nb">index </span>] <span class="nb">keep over </span>[ <span class="nb">set-nth </span><span class="no">t </span>] [ <span class="nb">3drop </span><span class="no">f </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>If the bucket is full, we need to be able to swap a fingerprint into the
bucket, replacing/removing an existing one:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bucket-swap</span> <span class="nf">( </span><span class="nv">fingerprint</span> <span class="nv">bucket</span> <span class="nf">-- </span><span class="nv">fingerprint'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>random ] <span class="nb">keep </span>[ <span class="nb">swap </span>] <span class="nb">change-nth </span><span class="k">;
</span></span></span></code></pre></div><h3 id="hashing">Hashing</h3>
<p>Our hashing strategy will be to generate the
<a href="https://en.wikipedia.org/wiki/SHA-1">SHA-1</a> hash value for a given
<a href="https://docs.factorcode.org/content/article-byte-arrays.html">byte-array</a>,
splitting it into two 32-bit values (a 32-bit fingerprint, and a 32-bit
index value). We will also generate an alternate index value as well
using a constant from the
<a href="https://en.wikipedia.org/wiki/MurmurHash">MurmurHash</a> to mix with the
primary index:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">hash-index</span> <span class="nf">( </span><span class="nv">hash</span> <span class="nf">-- </span><span class="nv">fingerprint</span> <span class="nv">index</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">over </span><displaced-alien> [ uint deref ] <span class="nb">bi@ </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">alt-index</span> <span class="nf">( </span><span class="nv">fingerprint</span> <span class="nv">index</span> <span class="nf">-- </span><span class="nv">alt-index</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">0x5bd1e995 </span>w* ] [ <span class="nb">bitxor </span>] <span class="nb">bi* </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">hash-indices</span> <span class="nf">( </span><span class="nv">bytes</span> <span class="nf">-- </span><span class="nv">fingerprint</span> <span class="nv">index</span> <span class="nv">alt-index</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> sha1 checksum-bytes hash-index <span class="nb">2dup </span>alt-index <span class="k">;
</span></span></span></code></pre></div><h3 id="insertlookupdelete">Insert/Lookup/Delete</h3>
<p>Our Cuckoo filter holds our buckets:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">cuckoo-filter</span> <span class="nv">buckets</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><cuckoo-filter></span> <span class="nf">( </span><span class="nv">capacity</span> <span class="nf">-- </span><span class="nv">cuckoo-filter</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <cuckoo-buckets> cuckoo-filter <span class="nb">boa </span><span class="k">;
</span></span></span></code></pre></div><p>To insert an item into the Cuckoo filter, we calculate its
<code>hash-indices</code> and then try inserting it into the bucket specified by
the first index, then the bucket specified by the second index. If those
buckets are full, we go through a “kickdown” process to move
fingerprints from other buckets until we find a bucket that has space,
or exceed the maximum number of attempts:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="c">! The maximum number of times we kick down items/displace from</span>
</span></span><span class="line"><span class="cl"><span class="c">! their buckets</span>
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">max-cuckoo-count</span> <span class="m">500
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">cuckoo-insert</span> <span class="nf">( </span><span class="nv">bytes</span> <span class="nv">cuckoo-filter</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> bytes hash-indices :> <span class="nf">( </span><span class="nv">fp!</span> <span class="nv">i1</span> <span class="nv">i2</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> cuckoo-filter buckets>> :> buckets
</span></span><span class="line"><span class="cl"> buckets <span class="nb">length </span>:> n
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ fp i1 n <span class="nb">mod </span>buckets <span class="nb">nth </span>bucket-insert ]
</span></span><span class="line"><span class="cl"> [ fp i2 n <span class="nb">mod </span>buckets <span class="nb">nth </span>bucket-insert ]
</span></span><span class="line"><span class="cl"> } 0|| [
</span></span><span class="line"><span class="cl"> <span class="no">t
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="m">2 </span>random <span class="nb">zero? </span>i1 i2 <span class="nb">? </span>:> i!
</span></span><span class="line"><span class="cl"> max-cuckoo-count [
</span></span><span class="line"><span class="cl"> <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> fp i n <span class="nb">mod </span>buckets <span class="nb">nth </span>bucket-swap fp!
</span></span><span class="line"><span class="cl"> fp i alt-index i!
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> fp i n <span class="nb">mod </span>buckets <span class="nb">nth </span>bucket-insert
</span></span><span class="line"><span class="cl"> ] <span class="nb">find-integer >boolean
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>To lookup an item, we calculate the <code>hash-indices</code> and then check the
two buckets to see if the fingerprint can be found.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">cuckoo-lookup</span> <span class="nf">( </span><span class="nv">bytes</span> <span class="nv">cuckoo-filter</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> bytes hash-indices :> <span class="nf">( </span><span class="nv">fp</span> <span class="nv">i1</span> <span class="nv">i2</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> cuckoo-filter buckets>> :> buckets
</span></span><span class="line"><span class="cl"> buckets <span class="nb">length </span>:> n
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ fp i1 n <span class="nb">mod </span>buckets <span class="nb">nth </span>bucket-lookup ]
</span></span><span class="line"><span class="cl"> [ fp i2 n <span class="nb">mod </span>buckets <span class="nb">nth </span>bucket-lookup ]
</span></span><span class="line"><span class="cl"> } 0|| <span class="k">;
</span></span></span></code></pre></div><p>To delete an item, we calculate the <code>hash-indices</code> and then try and
remove it from the first index, or the second index if not found in the
first bucket.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">cuckoo-delete</span> <span class="nf">( </span><span class="nv">bytes</span> <span class="nv">cuckoo-filter</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> bytes hash-indices :> <span class="nf">( </span><span class="nv">fp</span> <span class="nv">i1</span> <span class="nv">i2</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> cuckoo-filter buckets>> :> buckets
</span></span><span class="line"><span class="cl"> buckets <span class="nb">length </span>:> n
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ fp i1 n <span class="nb">mod </span>buckets <span class="nb">nth </span>bucket-delete ]
</span></span><span class="line"><span class="cl"> [ fp i2 n <span class="nb">mod </span>buckets <span class="nb">nth </span>bucket-delete ]
</span></span><span class="line"><span class="cl"> } 0|| <span class="k">;
</span></span></span></code></pre></div><p>This is available in the
<a href="https://github.com/factor/factor/blob/master/extra/cuckoo-filters/cuckoo-filters.factor">cuckoo-filters</a>
vocabulary along with some tests, documentation, and a few extra
features including the ability to change the checksum being used.</p>
Backticks
https://re.factorcode.org/2016/07/backticks.html
Fri, 15 Jul 2016 09:49:00 -0700https://re.factorcode.org/2016/07/backticks.html<p>Most languages support running arbitrary commands using something like
the Linux <a href="https://man7.org/linux/man-pages/man3/system.3.html">system</a>
function. Often, this support has both quick-and-easy and
full-featured-but-complex versions.</p>
<p>In <a href="https://python.org">Python</a>, you can use
<a href="https://docs.python.org/2/library/os.html#os.system">os.system</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">>>></span> <span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s2">"ls -l"</span><span class="p">)</span>
</span></span></code></pre></div><p>In <a href="https://ruby-lang.org">Ruby</a>, you can use
<a href="https://ruby-doc.org/core-2.2.0/Kernel.html#method-i-system">system</a> as
well as “backticks”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">irb</span><span class="p">(</span><span class="n">main</span><span class="p">):</span><span class="mo">001</span><span class="p">:</span><span class="mi">0</span><span class="o">></span> <span class="nb">system</span><span class="p">(</span><span class="s2">"ls -l"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">irb</span><span class="p">(</span><span class="n">main</span><span class="p">):</span><span class="mo">002</span><span class="p">:</span><span class="mi">0</span><span class="o">></span> <span class="sb">`ls -l`</span>
</span></span></code></pre></div><p>Basically, the difference between “system” and “backticks” is:</p>
<ul>
<li>“system” executes a command, returning the exit code of the process.</li>
<li>“backticks” executes a command, returning the standard output of the process.</li>
</ul>
<p><a href="https://factorcode.org">Factor</a> has extensive cross-platform support for
<a href="https://docs.factorcode.org/content/article-io.launcher.html">launching
processes</a>,
but I thought it would be fun to show how custom syntax can be created
to implement “backticks”, capturing and returning standard output from
the process:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYNTAX: </span>`
</span></span><span class="line"><span class="cl"> <span class="s">"`"</span> parse-multiline-string '[
</span></span><span class="line"><span class="cl"> _ utf8 [ <span class="nb">contents </span>] with-process-reader
</span></span><span class="line"><span class="cl"> ] <span class="nb">append! </span><span class="k">;
</span></span></span></code></pre></div><p>You can use this in a similar fashion to Ruby or Perl:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> ` ls -l`
</span></span></code></pre></div><blockquote>
<p><em>Note: This syntax currently requires a space after the leading
backtick. In the future, we have plans for an improved lexer that
removes this requirement.</em></p>
</blockquote>
<p>This is available in the
<a href="https://github.com/factor/factor/blob/master/extra/backticks/backticks.factor">backticks</a>
vocabulary.</p>
Clock Angles
https://re.factorcode.org/2016/07/clock-angles.html
Wed, 06 Jul 2016 16:37:00 -0700https://re.factorcode.org/2016/07/clock-angles.html<p><a href="https://programmingpraxis.com">Programming Praxis</a> posted about
calculating <a href="https://programmingpraxis.com/2016/07/01/clock-angles">clock
angles</a>,
specifically to:</p>
<blockquote>
<p><em>Write a program that, given a time as hours and minutes (using a
12-hour clock), calculates the angle between the two hands. For
instance, at 2:00 the angle is 60°.</em></p>
</blockquote>
<p><a href="https://en.wikipedia.org/wiki/Main_Page">Wikipedia</a> has a page about
<a href="https://en.wikipedia.org/wiki/Clock_angle_problem">clock angle
problems</a> that we can
pull a few test cases from:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="m">0 </span>} [ <span class="s">"12:00"</span> clock-angle ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">60 </span>} [ <span class="s">"2:00"</span> clock-angle ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">180 </span>} [ <span class="s">"6:00"</span> clock-angle ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">18 </span>} [ <span class="s">"5:24"</span> clock-angle ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">50 </span>} [ <span class="s">"2:20"</span> clock-angle ] unit-test
</span></span></code></pre></div><p>The hour hand moves 360° in 12 hours and depends on the number of hours
and minutes (properly handling midnight and noon to be <code>0°</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">hour°</span> <span class="nf">( </span><span class="nv">hour</span> <span class="nv">minutes</span> <span class="nf">-- </span><span class="nv">degrees</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> hour [ <span class="m">12 </span><span class="nb">= </span><span class="m">0 </span>] <span class="nb">keep ? </span>minutes <span class="m">60 </span><span class="nb">/ + </span>360/12 <span class="nb">* </span><span class="k">;
</span></span></span></code></pre></div><p>The minute hand moves 360° in 60 minutes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">minute°</span> <span class="nf">( </span><span class="nv">minutes</span> <span class="nf">-- </span><span class="nv">degrees</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> 360/60 <span class="nb">* </span><span class="k">;
</span></span></span></code></pre></div><p>Using these words, we can calculate the clock angle from a time string:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">clock-angle</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">degrees</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">":"</span> split1 [ number>string ] <span class="nb">bi@
</span></span></span><span class="line"><span class="cl"> [ hour° ] [ minute° ] <span class="nb">bi - abs </span><span class="k">;
</span></span></span></code></pre></div>left-pad
https://re.factorcode.org/2016/03/left-pad.html
Fri, 25 Mar 2016 09:39:00 -0700https://re.factorcode.org/2016/03/left-pad.html<p>In the wake of an <a href="https://medium.com/@azerbike/i-ve-just-liberated-my-modules-9045c06be67c">epic
ragequit</a>
where <a href="https://github.com/azer">Azer Koçulu</a> removed all of his modules
from <a href="https://www.npmjs.com/">npm</a> (the <a href="https://nodejs.org">node.js</a>
package manager), there have been
<a href="https://www.haneycodes.net/npm-left-pad-have-we-forgotten-how-to-program">so</a>
<a href="https://left-pad.io/">many</a>
<a href="https://gist.github.com/rauchg/5b032c2c2166e4e36713">entertaining</a>
<a href="https://blog.npmjs.org/post/141577284765/kik-left-pad-and-npm">discussions</a>
and
<a href="https://medium.com/@mproberts/a-discussion-about-the-breaking-of-the-internet-3d4d2a83aa4d">explanations</a>
covering <a href="https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/">what
happened</a>.</p>
<p>Today, <a href="https://programmingpraxis.com">Programming Praxis</a> posted the
<a href="https://programmingpraxis.com/2016/03/25/leftpad/">leftpad challenge</a>,
pointing out that the original solution ran in <a href="https://en.wikipedia.org/wiki/Time_complexity#Polynomial_time">quadratic
time</a> due
to it’s use of character-by-character string concatenation (but not
pointing out that it only works with strings).</p>
<p>First, the original code in Javascript:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">leftpad</span> <span class="p">(</span><span class="nx">str</span><span class="p">,</span> <span class="nx">len</span><span class="p">,</span> <span class="nx">ch</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">str</span> <span class="o">=</span> <span class="nb">String</span><span class="p">(</span><span class="nx">str</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">ch</span> <span class="o">&&</span> <span class="nx">ch</span> <span class="o">!==</span> <span class="mi">0</span><span class="p">)</span> <span class="nx">ch</span> <span class="o">=</span> <span class="s1">' '</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="nx">len</span> <span class="o">=</span> <span class="nx">len</span> <span class="o">-</span> <span class="nx">str</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="o">++</span><span class="nx">i</span> <span class="o"><</span> <span class="nx">len</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">str</span> <span class="o">=</span> <span class="nx">ch</span> <span class="o">+</span> <span class="nx">str</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">str</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Now, a (simpler? faster? more general?) version in
<a href="https://factorcode.org">Factor</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">left-pad</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">n</span> <span class="nv">elt</span> <span class="nf">-- </span><span class="nv">newseq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> seq n seq <span class="nb">length </span>[-] elt <span class="nb"><repetition> prepend </span><span class="k">;
</span></span></span></code></pre></div><p>Using it, you can see it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"hello"</span> <span class="m">3 </span><span class="sc">CHAR: h </span>left-pad <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"hello"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"hello"</span> <span class="m">10 </span><span class="sc">CHAR: h </span>left-pad <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"hhhhhhello"</span>
</span></span></code></pre></div><p>And it even works with other types of
<a href="https://docs.factorcode.org/content/article-sequence-protocol.html">sequences</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1 2 3 </span>} <span class="m">3 0 </span>left-pad <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">1 2 3 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1 2 3 </span>} <span class="m">10 0 </span>left-pad <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">0 0 0 0 0 0 0 1 2 3 </span>}
</span></span></code></pre></div><p>I should also point out that <a href="https://factorcode.org">Factor</a> has
<a href="https://docs.factorcode.org/content/word-pad-head,sequences.html">pad-head</a>
that does this in the standard library and <a href="https://nodejs.org">node.js</a>
has a
<a href="https://github.com/jonschlinkert/pad-left/blob/master/index.js">pad-left</a>
module that solves the quadratic time problem (but still only works with
strings).</p>
ISBN
https://re.factorcode.org/2015/09/isbn.html
Sat, 19 Sep 2015 14:36:00 -0700https://re.factorcode.org/2015/09/isbn.html<p>Most books are issued a unique <a href="https://en.wikipedia.org/wiki/International_Standard_Book_Number">International Standard Book
Number</a>
(ISBN) number. Often different formats of the same book will have
different ISBN numbers. On a print book, you can usually find the ISBN
on a barcode on the back cover.</p>
<p>
<img src="https://re.factorcode.org/images/2015-09-19-isbn-isbn.png" alt="" width="222" height="120" />
</p>
<p>Most countries seem to have a national ISBN registration agency. In some
countries this is a free service provided by a government agency. In
other countries, this is operated by a commercial entity. In the United
States, one company (<a href="https://www.myidentifiers.com/Get-your-isbn-now/">R.R. Bowker
LLC</a>) has an apparent
monopoly on issuing ISBN numbers which can cost $125 for one (less if
you buy in bulk).</p>
<p>The ISBN is 13 digits long if assigned starting in 2007, and 10 digits
long if assigned before 2007. Each ISBN contains a <a href="https://en.wikipedia.org/wiki/Check_digit">check
digit</a> which is used for
basic error detection We are going to build a few words in
<a href="https://factorcode.org">Factor</a> to calculate the check digits and
validate ISBNs.</p>
<p>We need to turn an ISBN (which might include spaces or dashes) into
numeric digits:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">digits</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">digits</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ digit? ] <span class="nb">filter </span>string>digits <span class="k">;
</span></span></span></code></pre></div><p>For ISBN-10, the check digit is the sum of each of 10 digits multiplied
by a weight (descending from 10 to 1) modulo 11.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">isbn-10-check</span> <span class="nf">( </span><span class="nv">digits</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>[ <span class="m">10 </span><span class="nb">swap - * + </span>] <span class="nb">reduce-index </span><span class="m">11 </span><span class="nb">mod </span><span class="k">;
</span></span></span></code></pre></div><p>For ISBN-13, the check digit is the sum of each of 13 digits multiplied
by a weight (alternating between 1 and 3) modulo 10.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">isbn-13-check</span> <span class="nf">( </span><span class="nv">digits</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>[ <span class="nb">even? </span><span class="m">1 3 </span><span class="nb">? * + </span>] <span class="nb">reduce-index </span><span class="m">10 </span><span class="nb">mod </span><span class="k">;
</span></span></span></code></pre></div><p>We can validate an ISBN by grabbing the digits and running either the
ISBN-10 or ISBN-13 check and verifying that the result is zero.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">valid-isbn?</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> digits <span class="nb">dup length </span>{
</span></span><span class="line"><span class="cl"> { <span class="m">10 </span>[ isbn-10-check ] }
</span></span><span class="line"><span class="cl"> { <span class="m">13 </span>[ isbn-13-check ] }
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="m">0 </span><span class="nb">= </span><span class="k">;
</span></span></span></code></pre></div><p>The code (and some tests) for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/isbn/isbn.factor">GitHub</a>.</p>
Pig Latin
https://re.factorcode.org/2015/09/pig-latin.html
Fri, 11 Sep 2015 18:19:00 -0700https://re.factorcode.org/2015/09/pig-latin.html<p><a href="https://en.wikipedia.org/wiki/Pig_Latin">Pig Latin</a> is a somewhat
ridiculous language game which modifies words in such a funny way that
is hard to figure out if you don’t know how it works but easy if you do.
Using <a href="https://factorcode.org">Factor</a>, we will build a converter from
English to Pig Latin words.</p>
<p>There are two basic rules we should implement:</p>
<ol>
<li>For words that begin with consonant sounds, the initial consonant or
consonant cluster is moved to the end of the word, and “ay” is added
to the end.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="s">"igpay"</span> } [ <span class="s">"pig"</span> pig-latin ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"ananabay"</span> } [ <span class="s">"banana"</span> pig-latin ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"ashtray"</span> } [ <span class="s">"trash"</span> pig-latin ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"appyhay"</span> } [ <span class="s">"happy"</span> pig-latin ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"uckday"</span> } [ <span class="s">"duck"</span> pig-latin ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"oveglay"</span> } [ <span class="s">"glove"</span> pig-latin ] unit-test
</span></span></code></pre></div><ol start="2">
<li>For words that begin with a vowel sounds or silent letter, add “way”
to the end.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="s">"eggway"</span> } [ <span class="s">"egg"</span> pig-latin ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"inboxway"</span> } [ <span class="s">"inbox"</span> pig-latin ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"eightway"</span> } [ <span class="s">"eight"</span> pig-latin ] unit-test
</span></span></code></pre></div><p>We can implement our two basic rules:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pig-latin</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ <span class="s">"aeiou"</span> <span class="nb">member? </span>] <span class="nb">find drop </span>[
</span></span><span class="line"><span class="cl"> <span class="s">"way"</span> <span class="nb">append
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="nb">cut swap </span><span class="s">"ay"</span> <span class="nb">3append
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-zero </span><span class="k">;
</span></span></span></code></pre></div><p>We could improve this by:</p>
<ul>
<li>better handling of words that start with capital vowels or are all
consonants</li>
<li>reverse the rules to convert Pig Latin back to English</li>
<li>variations such as adding “yay” (or “i”) instead of “way”</li>
<li>different rules like adding “ag” before each vowel (“pagig
lagatagin”)</li>
<li>support <a href="https://en.wikipedia.org/wiki/Language_game#List_of_common_language_games">language games in other
languages</a></li>
</ul>
<p>Anyway, this is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/pig-latin/pig-latin.factor">GitHub</a>.</p>
Bowling Scores
https://re.factorcode.org/2015/08/bowling-scores.html
Sun, 30 Aug 2015 12:30:00 -0700https://re.factorcode.org/2015/08/bowling-scores.html<p>Today we are going to explore building a
<a href="https://en.wikipedia.org/wiki/Bowling">bowling</a> score calculator using
<a href="https://factorcode.org">Factor</a>. In particular, we will be scoring
<a href="https://en.wikipedia.org/wiki/Ten-pin_bowling">ten-pin bowling</a>.</p>
<p>There are a lot of ways to “golf” this, including this <a href="https://www.fssnip.net/ei">short version in
F#</a>, but we will build this in several steps
through transformations of the input. The test input is a string
representation of the hits, misses, spares, and strikes. The output will
be a number which is your total score. We will assume valid inputs and
not do much error-checking.</p>
<p>A sample game might look like this:</p>
<pre tabindex="0"><code>12X4--3-69/-98/8-8-
</code></pre><p>Our first transformation is to convert each character to a number of
pins that have been knocked down for each ball. Strikes are denoted with
<code>X</code>, spares with <code>/</code>, misses with <code>-</code>, and normal hits with a number.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pin</span> <span class="nf">( </span><span class="nv">last</span> <span class="nv">ch</span> <span class="nf">-- </span><span class="nv">pin</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: X </span>[ <span class="m">10 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: / </span>[ <span class="m">10 </span><span class="nb">over - </span>] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: - </span>[ <span class="m">0 </span>] }
</span></span><span class="line"><span class="cl"> [ <span class="sc">CHAR: 0 </span><span class="nb">- </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">case nip </span><span class="k">;
</span></span></span></code></pre></div><p>We use this to convert the entire string into a series of pins knocked
down for each ball.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pins</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">pins</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span><span class="nb">swap </span>[ pin <span class="nb">dup </span>] { } <span class="nb">map-as nip </span><span class="k">;
</span></span></span></code></pre></div><p>A single frame will be either one ball, if a strike, or two balls. We
are going to use
<a href="https://docs.factorcode.org/content/word-cut-slice,sequences.html">cut-slice</a>
instead of
<a href="https://docs.factorcode.org/content/word-cut,sequences.html">cut</a>
because it will be helpful later.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">frame</span> <span class="nf">( </span><span class="nv">pins</span> <span class="nf">-- </span><span class="nv">rest</span> <span class="nv">frame</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup first </span><span class="m">10 </span><span class="nb">= </span><span class="m">1 2 </span><span class="nb">? short cut-slice swap </span><span class="k">;
</span></span></span></code></pre></div><p>A game is 9 “normal” frames and then a last frame that could have up to
three balls in it.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">frames</span> <span class="nf">( </span><span class="nv">pins</span> <span class="nf">-- </span><span class="nv">frames</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">9 </span>[ frame ] <span class="nb">replicate swap suffix </span><span class="k">;
</span></span></span></code></pre></div><p>Some frames will trigger a bonus. Strikes add the value of the next two
balls. Spares add the value of the next ball. We build this by
“un-slicing” the frame and calling
<a href="https://docs.factorcode.org/content/word-sum,sequences.html">sum</a> on the
next balls.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bonus</span> <span class="nf">( </span><span class="nv">frame</span> <span class="nf">-- </span><span class="nv">bonus</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ seq>> ] [ to>> <span class="nb">tail </span>] [ <span class="nb">length </span><span class="m">3 </span><span class="nb">swap - </span>] <span class="nb">tri head sum </span><span class="k">;
</span></span></span></code></pre></div><p>We can score the frames by checking for frames where all ten pins are
knocked down (either spares or strikes) and adding their bonus.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">scores</span> <span class="nf">( </span><span class="nv">frames</span> <span class="nf">-- </span><span class="nv">scores</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ <span class="nb">sum </span>] <span class="nb">keep over </span><span class="m">10 </span><span class="nb">= </span>[ bonus <span class="nb">+ </span>] [ <span class="nb">drop </span>] <span class="nb">if </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>We can solve the original goal by just adding all the scores:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bowl</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">score</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> pins frames scores <span class="nb">sum </span><span class="k">;
</span></span></span></code></pre></div><p>And write a bunch of unit tests to make sure it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="m">0 </span>} [ <span class="s">"---------------------"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">11 </span>} [ <span class="s">"------------------X1-"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">12 </span>} [ <span class="s">"----------------X1-"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">15 </span>} [ <span class="s">"------------------5/5"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">20 </span>} [ <span class="s">"11111111111111111111"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">20 </span>} [ <span class="s">"5/5-----------------"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">20 </span>} [ <span class="s">"------------------5/X"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">40 </span>} [ <span class="s">"X5/5----------------"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">80 </span>} [ <span class="s">"-8-7714215X6172183-"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">83 </span>} [ <span class="s">"12X4--3-69/-98/8-8-"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">150 </span>} [ <span class="s">"5/5/5/5/5/5/5/5/5/5/5"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">144 </span>} [ <span class="s">"XXX6-3/819-44X6-"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">266 </span>} [ <span class="s">"XXXXXXXXX81-"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">271 </span>} [ <span class="s">"XXXXXXXXX9/2"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">279 </span>} [ <span class="s">"XXXXXXXXXX33"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">295 </span>} [ <span class="s">"XXXXXXXXXXX5"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">300 </span>} [ <span class="s">"XXXXXXXXXXXX"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">100 </span>} [ <span class="s">"-/-/-/-/-/-/-/-/-/-/-"</span> bowl ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">190 </span>} [ <span class="s">"9/9/9/9/9/9/9/9/9/9/9"</span> bowl ] unit-test
</span></span></code></pre></div><p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/bowling/bowling.factor">GitHub</a>.</p>
Haikunator
https://re.factorcode.org/2015/08/haikunator.html
Wed, 26 Aug 2015 19:02:00 -0700https://re.factorcode.org/2015/08/haikunator.html<p>The <a href="https://github.com/usmanbashir/haikunator">Haikunator</a> is a project
to provide “Heroku-like memorable random names”. These names usually
consist of an adjective, a noun, and a random number or token. The
original repository is implemented in Ruby, with ports to Go,
Javascript, Python, PHP, Elixer, .NET, Java, and Dart.</p>
<p>We will be implementing this in <a href="https://factorcode.org">Factor</a> using
the <a href="https://docs.factorcode.org/content/article-qw.html">qw</a> vocabulary
that provides a simple way to make “arrays of strings” using the
<a href="https://docs.factorcode.org/content/word-qw%7B,qw.html">qw{</a> syntax.</p>
<p>First, a list of adjectives:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">adjectives</span> qw{
</span></span><span class="line"><span class="cl"> autumn hidden bitter misty silent empty dry dark summer icy
</span></span><span class="line"><span class="cl"> delicate quiet white cool spring winter patient twilight
</span></span><span class="line"><span class="cl"> dawn crimson wispy weathered blue billowing broken cold
</span></span><span class="line"><span class="cl"> damp falling frosty green long late lingering bold little
</span></span><span class="line"><span class="cl"> morning muddy old red rough still small sparkling throbbing
</span></span><span class="line"><span class="cl"> shy wandering withered wild black young holy solitary
</span></span><span class="line"><span class="cl"> fragrant aged snowy proud floral restless divine polished
</span></span><span class="line"><span class="cl"> ancient purple lively nameless lucky odd tiny free dry
</span></span><span class="line"><span class="cl"> yellow orange gentle tight super royal broad steep flat
</span></span><span class="line"><span class="cl"> square round mute noisy hushy raspy soft shrill rapid sweet
</span></span><span class="line"><span class="cl"> curly calm jolly fancy plain shinny
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Next, a list of nouns:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">nouns</span> qw{
</span></span><span class="line"><span class="cl"> waterfall river breeze moon rain wind sea morning snow lake
</span></span><span class="line"><span class="cl"> sunset pine shadow leaf dawn glitter forest hill cloud
</span></span><span class="line"><span class="cl"> meadow sun glade bird brook butterfly bush dew dust field
</span></span><span class="line"><span class="cl"> fire flower firefly feather grass haze mountain night pond
</span></span><span class="line"><span class="cl"> darkness snowflake silence sound sky shape surf thunder
</span></span><span class="line"><span class="cl"> violet water wildflower wave water resonance sun wood dream
</span></span><span class="line"><span class="cl"> cherry tree fog frost voice paper frog smoke star atom band
</span></span><span class="line"><span class="cl"> bar base block boat term credit art fashion truth disk
</span></span><span class="line"><span class="cl"> math unit cell scene heart recipe union limit bread toast
</span></span><span class="line"><span class="cl"> bonus lab mud mode poetry tooth hall king queen lion tiger
</span></span><span class="line"><span class="cl"> penguin kiwi cake mouse rice coke hola salad hat
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>We will make a token out of digits:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">token-chars</span> <span class="s">"0123456789"</span>
</span></span></code></pre></div><p>Finally, a simple <code>haikunate</code> implementation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">haikunate</span> <span class="nf">( -- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> adjectives random
</span></span><span class="line"><span class="cl"> nouns random
</span></span><span class="line"><span class="cl"> <span class="m">4 </span>[ token-chars random ] <span class="s">""</span> <span class="nb">replicate-as
</span></span></span><span class="line"><span class="cl"> <span class="s">"%s-%s-%s"</span> sprintf <span class="k">;
</span></span></span></code></pre></div><p>We can try it a few times, to see how it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> haikunate <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"odd-water-8344"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> haikunate <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"flat-tooth-9324"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> haikunate <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"wandering-lion-8346"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> haikunate <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"yellow-mud-9780"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> haikunate <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"patient-unit-4203"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> haikunate <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"floral-feather-1023"</span>
</span></span></code></pre></div><p>Some versions of “haikunate” in other languages include features such
as:</p>
<ul>
<li>allow customization of the delimiter (dots are popular)</li>
<li>allow the token to be specified as a range of possible numbers</li>
<li>allow the token to be restricted to a maximum length</li>
<li>allow the token to be represented using hex digits</li>
<li>allow the token to be represented with custom character sets</li>
<li>etc.</li>
</ul>
<p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/haikunator/haikunator.factor">GitHub</a>.</p>
Random Desktop Background
https://re.factorcode.org/2015/08/random-desktop-background.html
Mon, 17 Aug 2015 09:41:00 -0700https://re.factorcode.org/2015/08/random-desktop-background.html<p>As a follow-up to my <a href="https://re.factorcode.org/2015/08/desktop-background.html">Desktop
Background</a>
post, I wanted to show how to set your desktop background to random
images from various online image sites. We will be downloading a
<a href="https://en.wikipedia.org/wiki/Uniform_resource_locator">URL</a> to a local
file and then setting the desktop picture to that file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">download-and-set-desktop-picture</span> <span class="nf">( </span><span class="nv">url</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"/"</span> split1-last <span class="nb">nip </span>cache-file
</span></span><span class="line"><span class="cl"> [ download-to ] [ set-desktop-picture ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>Okay, now we need some random images!</p>
<p><a href="https://imgur.com/">Imgur</a> is a huge image-hosting website frequently
used on sites like <a href="https://reddit.com">Reddit</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-imgur</span> <span class="nf">( -- </span><span class="nv">url</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://imgur.com/random"</span> scrape-html <span class="nb">nip
</span></span></span><span class="line"><span class="cl"> <span class="s">"image_src"</span> <span class="s">"rel"</span> find-by-attribute-key-value
</span></span><span class="line"><span class="cl"> <span class="nb">first </span><span class="s">"href"</span> attribute <span class="k">;
</span></span></span></code></pre></div><p><a href="https://xkcd.com/">XKCD</a> has some fun comics. Maybe they would look
good on the desktop!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-xkcd</span> <span class="nf">( -- </span><span class="nv">url</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://dynamic.xkcd.com/random/comic/"</span> http-get <span class="nb">nip
</span></span></span><span class="line"><span class="cl"> R@ https://imgs\.xkcd\.com/comics/[^\.]+\.(png|jpg)@
</span></span><span class="line"><span class="cl"> first-match <span class="nb">>string </span><span class="k">;
</span></span></span></code></pre></div><p><a href="https://wallpaperstock.net/">WallpaperStock</a> has a bunch of more
traditional desktop images. We will scrape their random wallpaper page,
find the first wallpaper thumbnail, load that wallpaper page, find the
default image page, and then load that to find the image URL.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-wallpaperstock</span> <span class="nf">( -- </span><span class="nv">url</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://wallpaperstock.net/random-wallpapers.html"</span>
</span></span><span class="line"><span class="cl"> scrape-html <span class="nb">nip </span><span class="s">"wallpaper_thumb"</span> find-by-class-between
</span></span><span class="line"><span class="cl"> <span class="s">"a"</span> find-by-name <span class="nb">nip </span><span class="s">"href"</span> attribute
</span></span><span class="line"><span class="cl"> <span class="s">"https://wallpaperstock.net"</span> <span class="nb">prepend </span>scrape-html <span class="nb">nip
</span></span></span><span class="line"><span class="cl"> <span class="s">"the_view_link"</span> find-by-id <span class="nb">nip </span><span class="s">"href"</span> attribute
</span></span><span class="line"><span class="cl"> <span class="s">"https:"</span> <span class="nb">prepend </span>scrape-html <span class="nb">nip </span><span class="s">"myImage"</span> find-by-id <span class="nb">nip
</span></span></span><span class="line"><span class="cl"> <span class="s">"src"</span> attribute <span class="s">"https:"</span> <span class="nb">prepend </span><span class="k">;
</span></span></span></code></pre></div><p>Using this is as easy as:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> random-imgur
</span></span><span class="line"><span class="cl"> download-and-set-desktop-picture
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> random-xkcd
</span></span><span class="line"><span class="cl"> download-and-set-desktop-picture
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> random-wallpaperstock
</span></span><span class="line"><span class="cl"> download-and-set-desktop-picture
</span></span></code></pre></div><p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/desktop-picture/desktop-picture.factor">GitHub</a>.</p>
Desktop Background
https://re.factorcode.org/2015/08/desktop-background.html
Fri, 14 Aug 2015 08:59:00 -0700https://re.factorcode.org/2015/08/desktop-background.html<p>One of the benefits of learning to program is learning how to automate
tasks performed with a computer. I thought it might be fun to build a
simple vocabulary to allow getting and setting of the desktop background
picture. Since <a href="https://factorcode.org">Factor</a> makes it pretty easy to
build cross-platform vocabularies, we will implement this on Mac OS,
Linux, and Windows.</p>
<p>Our API consists of two words, one that gets the current desktop picture
and one that sets a new desktop picture, dispatching based on which
operating system we are running on (technically, based on the value of
the <a href="https://docs.factorcode.org/content/word-os,system.html">os</a>
variable).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">HOOK:</span> <span class="nf">get-desktop-picture</span> <span class="nf">os</span> <span class="nf">( -- </span><span class="nv">path</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">HOOK:</span> <span class="nf">set-desktop-picture</span> <span class="nf">os</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span></code></pre></div><h3 id="mac-os">Mac OS</h3>
<p>On Mac OS, we use
<a href="https://re.factorcode.org/2013/10/applescript.html">AppleScript</a> to
ask the <a href="https://support.apple.com/en-us/HT201732">Finder</a> what the path
to the current desktop picture is, or tell it to set the desktop picture
to a specific path.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">macosx</span> <span class="nf">get-desktop-picture</span>
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> <span class="s">"osascript"</span> <span class="s">"-e"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"tell app \"Finder\" to get posix path of (get desktop picture as alias)"</span>
</span></span><span class="line"><span class="cl"> } utf8 [ <span class="nb">readln </span>] with-process-reader <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">macosx</span> <span class="nf">set-desktop-picture</span>
</span></span><span class="line"><span class="cl"> absolute-path
</span></span><span class="line"><span class="cl"> <span class="s">"tell application \"Finder\" to set desktop picture to POSIX file \"%s\""</span>
</span></span><span class="line"><span class="cl"> sprintf run-apple-script <span class="k">;
</span></span></span></code></pre></div><h3 id="windows">Windows</h3>
<p>On Windows, we use the
<a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms724947(v=vs.85).aspx">SystemParametersInfo</a>
function to get and set the desktop wallpaper.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">SPI_GETDESKWALLPAPER</span> <span class="m">0x0073
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">SPI_SETDESKWALLPAPER</span> <span class="m">0x0014
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">windows</span> <span class="nf">get-desktop-picture</span>
</span></span><span class="line"><span class="cl"> SPI_GETDESKWALLPAPER MAX_PATH <span class="nb">dup </span><span class="m">1 </span><span class="nb">+ </span>WCHAR <c-array> [
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>SystemParametersInfo win32-error<>0
</span></span><span class="line"><span class="cl"> ] <span class="nb">keep </span>alien>native-string <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">windows</span> <span class="nf">set-desktop-picture</span>
</span></span><span class="line"><span class="cl"> [ SPI_SETDESKWALLPAPER <span class="m">0 </span>] <span class="nb">dip </span>utf16n encode
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>SystemParametersInfo win32-error<>0 <span class="k">;
</span></span></span></code></pre></div><h3 id="linux">Linux</h3>
<p>On Linux, which has many different desktops, we are going to assume a
<a href="https://gnome.org/">GNOME</a> environment. Other window managers have
different ways to <a href="https://stackoverflow.com/questions/1977694/change-desktop-background/21213504#21213504">change the desktop
background</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">linux</span> <span class="nf">get-desktop-picture</span>
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> <span class="s">"gsettings"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"get"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"org.gnome.desktop.background"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"picture-uri"</span>
</span></span><span class="line"><span class="cl"> } utf8 [ <span class="nb">readln </span>] with-process-reader
</span></span><span class="line"><span class="cl"> <span class="s">"'file://"</span> ?head <span class="nb">drop </span><span class="s">"'"</span> ?tail <span class="nb">drop </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">linux</span> <span class="nf">set-desktop-picture</span>
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> <span class="s">"gsettings"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"set"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"org.gnome.desktop.background"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"picture-uri"</span>
</span></span><span class="line"><span class="cl"> } <span class="nb">swap </span>absolute-path <span class="s">"file://"</span> <span class="nb">prepend suffix </span>try-process <span class="k">;
</span></span></span></code></pre></div><p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/tree/master/desktop-picture">GitHub</a>.</p>
Automated Reasoning
https://re.factorcode.org/2015/08/automated-reasoning.html
Thu, 06 Aug 2015 17:04:00 -0700https://re.factorcode.org/2015/08/automated-reasoning.html<p>There was a post about <a href="https://phdp.github.io/posts/2015-04-05-automated-reasoning.html">Automated Reasoning in F#, Scala, Haskell, C++,
and
Julia</a>
that uses a simple algorithm from John Harrison’s book <a href="https://www.cambridge.org/ca/academic/subjects/computer-science/programming-languages-and-applied-logic/handbook-practical-logic-and-automated-reasoning">Handbook of
Practical Logic and Automated
Reasoning</a>
to simplify this equation:</p>
<pre tabindex="0"><code>e = (1 + (0 * x)) * 3) + 12
</code></pre><p><a href="https://factorcode.org">Factor</a> has support for <a href="https://docs.factorcode.org/content/article-match.html">ML-style pattern
matching</a> and I
thought it would be fun to contribute a simple solution using the
<a href="https://docs.factorcode.org/content/vocab-match.html">match</a> vocabulary.</p>
<p>We want to define a few types of expressions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">Var</span> <span class="nv">s</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">Const</span> <span class="nv">n</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">Add</span> <span class="nv">x</span> <span class="nv">y</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">Mul</span> <span class="nv">x</span> <span class="nv">y</span> <span class="k">;
</span></span></span></code></pre></div><blockquote>
<p><em>Note: we could have made this simpler by assuming
<a href="https://docs.factorcode.org/content/word-integer,math.html">integers</a>
are constants and
<a href="https://docs.factorcode.org/content/word-string,strings.html">strings</a>
are variables rather than define the <code>Const</code> and <code>Var</code> tuples, but I
wanted to keep this close to the code in the original blog post.</em></p>
</blockquote>
<p>To be able to pattern match, we need to define some match variables:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">MATCH-VARS: ?x ?y <span class="k">;
</span></span></span></code></pre></div><p>We want a way to do a single simplification of an expression:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">simplify1</span> <span class="nf">( </span><span class="nv">expr</span> <span class="nf">-- </span><span class="nv">expr'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { T{ Add <span class="no">f </span>T{ Const <span class="no">f </span><span class="m">0 </span>} ?x } [ ?x ] }
</span></span><span class="line"><span class="cl"> { T{ Add <span class="no">f </span>?x T{ Const <span class="no">f </span><span class="m">0 </span>} } [ ?x ] }
</span></span><span class="line"><span class="cl"> { T{ Mul <span class="no">f </span>?x T{ Const <span class="no">f </span><span class="m">1 </span>} } [ ?x ] }
</span></span><span class="line"><span class="cl"> { T{ Mul <span class="no">f </span>T{ Const <span class="no">f </span><span class="m">1 </span>} ?x } [ ?x ] }
</span></span><span class="line"><span class="cl"> { T{ Mul <span class="no">f </span>?x T{ Const <span class="no">f </span><span class="m">0 </span>} } [ T{ Const <span class="no">f </span><span class="m">0 </span>} ] }
</span></span><span class="line"><span class="cl"> { T{ Mul <span class="no">f </span>T{ Const <span class="no">f </span><span class="m">0 </span>} ?x } [ T{ Const <span class="no">f </span><span class="m">0 </span>} ] }
</span></span><span class="line"><span class="cl"> { T{ Add <span class="no">f </span>T{ Const <span class="no">f </span>?x } T{ Const <span class="no">f </span>?y } }
</span></span><span class="line"><span class="cl"> [ ?x ?y <span class="nb">+ </span>Const <span class="nb">boa </span>] }
</span></span><span class="line"><span class="cl"> { T{ Mul <span class="no">f </span>T{ Const <span class="no">f </span>?x } T{ Const <span class="no">f </span>?y } }
</span></span><span class="line"><span class="cl"> [ ?x ?y <span class="nb">* </span>Const <span class="nb">boa </span>] }
</span></span><span class="line"><span class="cl"> [ ]
</span></span><span class="line"><span class="cl"> } match-cond <span class="k">;
</span></span></span></code></pre></div><p>We have a way to recursively simplify some expressions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">simplify</span> <span class="nf">( </span><span class="nv">expr</span> <span class="nf">-- </span><span class="nv">expr'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { T{ Add <span class="no">f </span>?x ?y } [ ?x ?y [ simplify ] <span class="nb">bi@ </span>Add <span class="nb">boa </span>] }
</span></span><span class="line"><span class="cl"> { T{ Mul <span class="no">f </span>?x ?y } [ ?x ?y [ simplify ] <span class="nb">bi@ </span>Mul <span class="nb">boa </span>] }
</span></span><span class="line"><span class="cl"> [ ]
</span></span><span class="line"><span class="cl"> } match-cond simplify1 <span class="k">;
</span></span></span></code></pre></div><p>Finally, we have a word that tries to simplify a value to a constant:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">simplify-value</span> <span class="nf">( </span><span class="nv">expr</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> simplify {
</span></span><span class="line"><span class="cl"> { T{ Const <span class="no">f </span>?x } [ ?x ] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">"Could not be simplified to a Constant."</span> ]
</span></span><span class="line"><span class="cl"> } match-cond <span class="k">;
</span></span></span></code></pre></div><p>To check that it works, we can write a unit test that simplifies the
original expression above:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="m">15 </span>} [
</span></span><span class="line"><span class="cl"> T{ Add <span class="no">f
</span></span></span><span class="line"><span class="cl"> T{ Mul <span class="no">f
</span></span></span><span class="line"><span class="cl"> T{ Add <span class="no">f
</span></span></span><span class="line"><span class="cl"> T{ Const <span class="no">f </span><span class="m">1 </span>}
</span></span><span class="line"><span class="cl"> T{ Mul <span class="no">f
</span></span></span><span class="line"><span class="cl"> T{ Const <span class="no">f </span><span class="m">0 </span>}
</span></span><span class="line"><span class="cl"> T{ Var <span class="no">f </span><span class="s">"x"</span> } } }
</span></span><span class="line"><span class="cl"> T{ Const <span class="no">f </span><span class="m">3 </span>} }
</span></span><span class="line"><span class="cl"> T{ Const <span class="no">f </span><span class="m">12 </span>} }
</span></span><span class="line"><span class="cl"> simplify-value
</span></span><span class="line"><span class="cl">] unit-test
</span></span></code></pre></div><p>That’s cool, but wouldn’t it be better if we could work on
<a href="https://docs.factorcode.org/content/word-quotation,quotations.html">quotations</a>
directly? Let’s make a word that converts a quotation to an expression:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>expr</span> <span class="nf">( </span><span class="nv">quot</span> <span class="nf">-- </span><span class="nv">expr</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup string? </span>] [ '[ _ Var <span class="nb">boa </span>] ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup integer? </span>] [ '[ _ Const <span class="nb">boa </span>] ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="no">\ +</span> <span class="nb">= </span>] [ <span class="nb">drop </span>[ Add <span class="nb">boa </span>] ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="no">\ *</span> <span class="nb">= </span>] [ <span class="nb">drop </span>[ Mul <span class="nb">boa </span>] ] }
</span></span><span class="line"><span class="cl"> } <span class="nb">cond
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map concat </span>call( -- expr ) <span class="k">;
</span></span></span></code></pre></div><p>Now that we have that, our test case is a lot simpler:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="m">15 </span>} [
</span></span><span class="line"><span class="cl"> [ <span class="s">"x"</span> <span class="m">0 </span><span class="nb">* </span><span class="m">1 </span><span class="nb">+ </span><span class="m">3 </span><span class="nb">* </span><span class="m">12 </span><span class="nb">+ </span>] >expr simplify-value
</span></span><span class="line"><span class="cl">] unit-test
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/reasoning/reasoning.factor">GitHub</a>.</p>
<blockquote>
<p><em>Note: this takes advantage of a <a href="https://github.com/factor/factor/commit/8c82f46ee96193cc016f6ea2fca472d2e0ee7c42">small
feature</a>
that I added to the
<a href="https://docs.factorcode.org/content/word-match-cond,match.html">match-cond</a>
word to provide a way to easily handle a fall-through pattern like the
<a href="https://docs.factorcode.org/content/word-cond,combinators.html">cond</a>
word.</em></p>
</blockquote>
Bit Test
https://re.factorcode.org/2015/06/bit-test.html
Fri, 19 Jun 2015 08:30:00 -0700https://re.factorcode.org/2015/06/bit-test.html<p><a href="https://factorcode.org">Factor</a> has a
<a href="https://docs.factorcode.org/content/word-bit__que__,math.html">bit?</a>
generic that is used to test an integer to see if a particular bit is
set. When operating on
<a href="https://docs.factorcode.org/content/word-fixnum,math.html">fixnum</a>
integers, this is implemented by the <code>fixnum-bit?</code> word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fixnum-bit?</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { <span class="nb">fixnum fixnum </span>} declare
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb">>= </span>[ <span class="nb">neg shift even? not </span>] [ <span class="nb">2drop </span><span class="no">f </span>] <span class="nb">if </span>
</span></span></code></pre></div><p>Many <a href="https://en.wikipedia.org/wiki/List_of_CPU_architectures">CPU
architectures</a>
have instructions for performing a <a href="https://en.wikipedia.org/wiki/Bit_Test">Bit
Test</a>. On
<a href="https://en.wikipedia.org/wiki/X86">x86</a>, the <a href="https://www.felixcloutier.com/x86/BT.html">BT
instruction</a> is available.
Below, we are going to implement a <a href="https://en.wikipedia.org/wiki/Intrinsic_function">compiler
intrinsic</a> in the
hopes of speeding up this operation in <a href="https://factorcode.org">Factor</a>.</p>
<h3 id="intrinsic">Intrinsic</h3>
<p>In
<a href="https://docs.factorcode.org/content/vocab-cpu.architecture.html">cpu.architecture</a>,
add a generic <code>%bit-test</code> based on our CPU:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">HOOK:</span> <span class="nf">%bit-test</span> <span class="nf">cpu</span> <span class="nf">( </span><span class="nv">dst</span> <span class="nv">src1</span> <span class="nv">src2</span> <span class="nv">temp</span> <span class="nf">-- )
</span></span></span></code></pre></div><p>In <a href="https://docs.factorcode.org/content/vocab-cpu.x86.html">cpu.x86</a>,
implement <code>%bit-test</code> on x86 (returning a boolean using a temporary
register and the <a href="https://www.felixcloutier.com/x86/CMOVcc.html">CMOVB
instruction</a> to check the
<a href="https://en.wikipedia.org/wiki/Carry_flag">carry flag</a> which holds the
result of the bit test):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M::</span> <span class="nc">x86</span> <span class="nf">%bit-test</span> <span class="nf">( </span><span class="nv">dst</span> <span class="nv">src1</span> <span class="nv">src2</span> <span class="nv">temp</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> src1 src2 BT
</span></span><span class="line"><span class="cl"> dst temp <span class="no">\ CMOVB</span> (%boolean) <span class="k">;
</span></span></span></code></pre></div><p>In
<a href="https://docs.factorcode.org/content/vocab-compiler.cfg.instructions.html">compiler.cfg.instructions</a>,
add a <code>##bit-test</code> instruction:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">FOLDABLE-INSN: ##bit-test
</span></span><span class="line"><span class="cl">def: dst/tagged-rep
</span></span><span class="line"><span class="cl">use: src1/int-rep src2/int-rep
</span></span><span class="line"><span class="cl">temp: temp/int-rep <span class="k">;
</span></span></span></code></pre></div><p>In
<a href="https://docs.factorcode.org/content/vocab-compiler.codegen.html">compiler.codegen</a>,
we link the <code>##bit-test</code> instruction with the <code>%bit-test</code> word.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">CODEGEN: ##bit-test %bit-test
</span></span></code></pre></div><p>In
<a href="https://docs.factorcode.org/content/vocab-compiler.cfg.intrinsics.html">compiler.cfg.intrinsics</a>,
enable replacing <code>fixnum-bit?</code> with the <code>%bit-test</code> intrinsic:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">enable-bit-test</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { fixnum-bit? [ <span class="nb">drop </span>[ ^^bit-test ] binary-op ] }
</span></span><span class="line"><span class="cl"> } enable-intrinsics <span class="k">;
</span></span></span></code></pre></div><h3 id="disassemble">Disassemble</h3>
<p>The old assembly using <code>fixnum-bit?</code> looked like this:</p>
<pre tabindex="0"><code>000000010ea00250: mov [rip-0x1ff256], eax
000000010ea00256: sub rsp, 0x8
000000010ea0025a: call 0x10eedf3b0 (integer>fixnum-strict)
000000010ea0025f: mov rax, [r14]
000000010ea00262: cmp rax, 0x0
000000010ea00266: jl 0x10ea002a7 (M\ fixnum bit? + 0x57)
000000010ea0026c: neg rax
000000010ea0026f: mov [r14], rax
000000010ea00272: call 0x10ebec060 (fixnum-shift)
000000010ea00277: mov rax, [r14]
000000010ea0027a: test rax, 0x10
000000010ea00281: mov rax, 0x1
000000010ea0028b: mov rbx, 0x1010eff4c
000000010ea00295: cmovnz rax, rbx
000000010ea00299: mov [r14], rax
000000010ea0029c: mov [rip-0x1ff2a2], eax
000000010ea002a2: add rsp, 0x8
000000010ea002a6: ret
000000010ea002a7: sub r14, 0x8
000000010ea002ab: mov qword [r14], 0x1
000000010ea002b2: mov [rip-0x1ff2b8], eax
000000010ea002b8: add rsp, 0x8
000000010ea002bc: ret
</code></pre><p>The new assembly looks like this with BT:</p>
<pre tabindex="0"><code>000000010e656ec0: mov [rip-0x293ec6], eax
000000010e656ec6: sub rsp, 0x8
000000010e656eca: call 0x10e467ea0 (integer>fixnum-strict)
000000010e656ecf: mov rax, [r14]
000000010e656ed2: mov rbx, [r14-0x8]
000000010e656ed6: sar rbx, 0x4
000000010e656eda: sar rax, 0x4
000000010e656ede: bt rbx, rax
000000010e656ee2: mov rbx, 0x1
000000010e656eec: mov rcx, 0x1018f169c
000000010e656ef6: cmovb rbx, rcx
000000010e656efa: sub r14, 0x8
000000010e656efe: mov [r14], rbx
000000010e656f01: mov [rip-0x293f07], eax
000000010e656f07: add rsp, 0x8
000000010e656f0b: ret
</code></pre><h3 id="performance">Performance</h3>
<p>Turns out that this speeds up <code>fixnum-bit?</code> by a lot. Using a simple
test case:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bench-fixnum-bit</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { <span class="nb">fixnum fixnum </span>} declare
</span></span><span class="line"><span class="cl"> [ <span class="m">0 100,000,000 </span>] <span class="nb">2dip
</span></span></span><span class="line"><span class="cl"> '[ _ _ <span class="nb">bit? </span>[ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">when </span>] <span class="nb">times </span><span class="k">;
</span></span></span></code></pre></div><p>Our old version was decently fast:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ <span class="mb">0b101010101 </span><span class="m">0 </span>bench-fixnum-bit ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.821433838 </span>seconds
</span></span></code></pre></div><p>But our new version is much faster!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ <span class="mb">0b101010101 </span><span class="m">0 </span>bench-fixnum-bit ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.239439108 </span>seconds
</span></span></code></pre></div><p>This has been
<a href="https://github.com/factor/factor/commit/621b50a8e5c6beab78fd60c4c3a3d94f968406dd">committed</a>
to the <a href="https://github.com/factor/factor">development version</a> and
is available now.</p>
Compressed Sieve of Eratosthenes
https://re.factorcode.org/2015/06/compressed-sieve-of-eratosthenes.html
Wed, 17 Jun 2015 10:10:00 -0700https://re.factorcode.org/2015/06/compressed-sieve-of-eratosthenes.html<p>In my <a href="https://re.factorcode.org/2015/06/genuine-sieve-of-eratosthenes.html">previous post</a>, we used
“2-3-5-7” <a href="https://en.wikipedia.org/wiki/Wheel_factorization">wheel
factorization</a> to produce
a faster <a href="https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes">Sieve of
Eratosthenes</a> with a
compression factor of 2 (by storing only the odd numbers). Samuel Tardieu
reminded me that the sieve variation used by
<a href="https://factorcode.org">Factor</a> uses less memory with a higher compression
factor of 3.75.</p>
<p>I thought it would be a fun implementation to show below.</p>
<h3 id="version-6">Version 6</h3>
<p>The “2-3-5” wheel with the first 30 numbers that are not divisible by 2,
3, or 5.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">wheel-2-3-5</span> $[
</span></span><span class="line"><span class="cl"> <span class="m">30 </span>[1..b] [
</span></span><span class="line"><span class="cl"> { <span class="m">2 3 5 </span>} [ divisor? ] <span class="nb">with any? not
</span></span></span><span class="line"><span class="cl"> ] B{ } <span class="nb">filter-as
</span></span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><blockquote>
<p><em>Note: We use 30 here because the “<code>{ 2 3 5 } product</code>” cycle is 30.</em></p>
</blockquote>
<p>If you look at the wheel, you see that there are 8 candidates in every
30 numbers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> wheel-2-3-5 <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">1 7 11 13 17 19 23 29 </span>}
</span></span></code></pre></div><p>The number <code>8</code> is interesting because it suggests that we can use a
single byte to store the 8 candidates in each block of 30 numbers, using
a <a href="https://en.wikipedia.org/wiki/Mask_(computing)">bitmask</a> for possible
primes or <code>f</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">masks</span> $[
</span></span><span class="line"><span class="cl"> <span class="m">30 </span>[0,b) [
</span></span><span class="line"><span class="cl"> wheel-2-3-5 <span class="nb">index </span>[ <span class="m">7 </span><span class="nb">swap - 2^ </span>] [ <span class="no">f </span>] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map
</span></span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><p>We can index into this array to find the byte/mask for every number:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">byte-mask</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">byte</span> <span class="nv">mask/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">30 </span><span class="nb">/mod </span>masks <span class="nb">nth </span><span class="k">;
</span></span></span></code></pre></div><p>Using the byte/mask we can check if a number is marked in the sieve
<a href="https://docs.factorcode.org/content/article-byte-arrays.html">byte-array</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">marked?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">sieve</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> n byte-mask :> <span class="nf">( </span><span class="nv">byte</span> <span class="nv">mask</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> mask [ byte sieve <span class="nb">nth </span>mask <span class="nb">bitand zero? not </span>] [ <span class="no">f </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>And we can unmark a number in the sieve in a similar fashion:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">unmark</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">sieve</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> n byte-mask :> <span class="nf">( </span><span class="nv">byte</span> <span class="nv">mask</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> mask [ byte sieve [ mask <span class="nb">bitnot bitand </span>] <span class="nb">change-nth </span>] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><p>Using that, we can unmark multiples of prime numbers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">unmark-multiples</span> <span class="nf">( </span><span class="nv">i</span> <span class="nv">upper</span> <span class="nv">sieve</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> i <span class="nb">sq </span>upper i <span class="m">2 </span><span class="nb">* </span><range> [ sieve unmark ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>That’s all we need to build our sieve, starting from a byte-array
initialized with all numbers marked and then progressively unmarking
multiples of primes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">sieve6</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">sieve</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> n <span class="m">30 </span><span class="nb">/i </span><span class="m">1 </span><span class="nb">+ </span>[ <span class="m">0xff </span>] B{ } <span class="nb">replicate-as </span>:> sieve
</span></span><span class="line"><span class="cl"> sieve <span class="nb">length </span><span class="m">30 </span><span class="nb">* </span><span class="m">1 </span><span class="nb">- </span>:> upper
</span></span><span class="line"><span class="cl"> <span class="m">3 </span>upper sqrt <span class="m">2 </span><range> [| i |
</span></span><span class="line"><span class="cl"> i sieve marked? [
</span></span><span class="line"><span class="cl"> i upper sieve unmark-multiples
</span></span><span class="line"><span class="cl"> ] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span>sieve <span class="k">;
</span></span></span></code></pre></div><p>As you can see, storing prime numbers up to 10 million takes only 333
KB!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10,000,000 </span>sieve6 <span class="nb">length </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">333334
</span></span></span></code></pre></div><p>This approach is used by the
<a href="https://docs.factorcode.org/content/article-math.primes.html">math.primes</a>
vocabulary to quickly check primality of any number less than 9 million
using only 300 KB of memory.</p>
<p>Neat!</p>
Genuine Sieve of Eratosthenes
https://re.factorcode.org/2015/06/genuine-sieve-of-eratosthenes.html
Mon, 08 Jun 2015 15:48:00 -0700https://re.factorcode.org/2015/06/genuine-sieve-of-eratosthenes.html<p>Iain Gray <a href="https://www.mail-archive.com/[email protected]/msg07220.html">posted on the mailing
list</a>
about a paper called the <a href="https://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf">Genuine Sieve of
Eratosthenes</a> by
Melissa O’Neill, a Professor of Computer Science at <a href="https://www.hmc.edu/">Harvey Mudd
College</a>.</p>
<p>It begins with a discussion about some clever looking Haskell code that
is just <a href="https://en.wikipedia.org/wiki/Trial_division">trial division</a>
and not the <a href="https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes">Sieve of
Eratosthenes</a>. At
the end of the paper is an interesting discussion of performance
improvements that I thought might be fun to implement in
<a href="https://factorcode.org">Factor</a> as a followup to the three versions that
I <a href="https://re.factorcode.org/2015/06/sieve-of-eratosthenes.html">posted about
recently</a>.</p>
<h3 id="version-4">Version 4</h3>
<p>We are going to use <a href="https://en.wikipedia.org/wiki/Wheel_factorization">wheel
factorization</a> as a
way of reducing the search space by ignoring some multiples of small
prime numbers. It is a bit similar to ignoring all even numbers in our
previous “Version 3”. In this case, we want to ignore all multiples of
the first four prime numbers.</p>
<p>To calculate the “2-3-5-7” wheel, we start with <code>11</code> and filter the next
<code>210</code> numbers that are not divisible by <code>2</code>, <code>3</code>, <code>5</code>, or <code>7</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">wheel-2-3-5-7</span> $[
</span></span><span class="line"><span class="cl"> <span class="m">11 </span><span class="nb">dup </span><span class="m">210 </span><span class="nb">+ </span>[a..b] [
</span></span><span class="line"><span class="cl"> { <span class="m">2 3 5 7 </span>} [ divisor? ] <span class="nb">with any? not
</span></span></span><span class="line"><span class="cl"> ] B{ } <span class="nb">filter-as </span>differences
</span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><blockquote>
<p><em>Note: We use 210 here because the “<code>{ 2 3 5 7 } product</code>” cycle is
210.</em></p>
</blockquote>
<p>Next, we want a way to iterate across all the numbers that might be
prime, calling a quotation on each number that is prime.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">each-prime</span> <span class="nf">( </span><span class="nv">upto</span> <span class="nv">sieve</span> <span class="nv">quot</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="m">11 </span>upto '[ <span class="nb">dup </span>_ <span class="nb"><= </span>] [
</span></span><span class="line"><span class="cl"> wheel-2-3-5-7 [
</span></span><span class="line"><span class="cl"> <span class="nb">over dup 2/ </span>sieve <span class="nb">nth </span>[ <span class="nb">drop </span>] quot <span class="nb">if
</span></span></span><span class="line"><span class="cl"> <span class="nb">+
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while drop </span><span class="k">; inline
</span></span></span></code></pre></div><p>For each prime found, we will want to mark all odd multiples as not
prime.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">mark-multiples</span> <span class="nf">( </span><span class="nv">i</span> <span class="nv">upto</span> <span class="nv">sieve</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> i <span class="nb">sq </span>upto i <span class="m">2 </span><span class="nb">* </span><range> [| j |
</span></span><span class="line"><span class="cl"> <span class="no">t </span>j <span class="nb">2/ </span>sieve <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span><span class="k">; inline
</span></span></span></code></pre></div><p>We will use a
<a href="https://docs.factorcode.org/content/article-bit-arrays.html">bit-array</a>
that is sized in 210-bit blocks, so the number of bits needed is:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">sieve-bits</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">bits</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">210 </span><span class="nb">/i </span><span class="m">1 </span><span class="nb">+ </span><span class="m">210 </span><span class="nb">* 2/ </span><span class="m">6 </span><span class="nb">+ </span><span class="k">; inline
</span></span></span></code></pre></div><p>Finally, we calculate our sieve, first for <code>11</code> and then for all the
primes above <code>11</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">sieve4</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">primes</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> n sieve-bits <bit-array> :> sieve
</span></span><span class="line"><span class="cl"> <span class="no">t </span><span class="m">0 </span>sieve <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> <span class="no">t </span><span class="m">4 </span>sieve <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> n sqrt sieve [ n sieve mark-multiples ] each-prime
</span></span><span class="line"><span class="cl"> V{ <span class="m">2 3 5 7 </span>} <span class="nb">clone </span>:> primes
</span></span><span class="line"><span class="cl"> n sieve [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>n <span class="nb"><= </span>[ primes <span class="nb">push </span>] [ <span class="nb">drop </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] each-prime primes <span class="k">;
</span></span></span></code></pre></div><p>Calculating prime numbers up to 10 million is getting even faster.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ <span class="m">10,000,000 </span>sieve4 ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.08523477 </span>seconds
</span></span></code></pre></div><h3 id="version-5">Version 5</h3>
<p>If you use the
<a href="https://docs.factorcode.org/content/word-optimized.,compiler.tree.debugger.html">compiler.tree.debugger</a>
to inspect the optimized output, you can see some dynamic dispatch with
branches for fixed-width
(<a href="https://docs.factorcode.org/content/word-fixnum,math.html">fixnum</a>) and
arbitrary-precision
(<a href="https://docs.factorcode.org/content/word-bignum,math.html">bignum</a>)
integers.</p>
<p>It would be nice to perform <a href="https://github.com/factor/factor/issues/788">fixnum
specialization</a> or
improve type propagation in the compiler, but for now we are going to
write some ugly code to force
<a href="https://docs.factorcode.org/content/word-fixnum,math.html">fixnum</a> math
for performance.</p>
<p>We make a version of <code>each-prime</code> that inlines the iteration:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">each-prime2</span> <span class="nf">( </span><span class="nv">upto</span> <span class="nv">sieve</span> <span class="nv">quot</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="m">11 </span>upto <span class="nb">>fixnum </span>'[ <span class="nb">dup </span>_ <span class="nb"><= </span>] [
</span></span><span class="line"><span class="cl"> wheel-2-3-5-7 [
</span></span><span class="line"><span class="cl"> <span class="nb">over dup 2/ </span>sieve nth-unsafe [ <span class="nb">drop </span>] quot <span class="nb">if
</span></span></span><span class="line"><span class="cl"> fixnum+fast
</span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while drop </span><span class="k">; inline
</span></span></span></code></pre></div><p>And similarly for <code>mark-multiples</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">mark-multiples2</span> <span class="nf">( </span><span class="nv">i</span> <span class="nv">upto</span> <span class="nv">sieve</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> i <span class="m">2 </span>fixnum*fast :> step
</span></span><span class="line"><span class="cl"> i i fixnum*fast upto <span class="nb">>fixnum </span>'[ <span class="nb">dup </span>_ <span class="nb"><= </span>] [
</span></span><span class="line"><span class="cl"> <span class="no">t </span><span class="nb">over 2/ </span>sieve set-nth-unsafe
</span></span><span class="line"><span class="cl"> step fixnum+fast
</span></span><span class="line"><span class="cl"> ] <span class="nb">while drop </span><span class="k">; inline
</span></span></span></code></pre></div><p>And use them in our sieve computation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">sieve5</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">primes</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> n sieve-bits <bit-array> :> sieve
</span></span><span class="line"><span class="cl"> <span class="no">t </span><span class="m">0 </span>sieve <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> <span class="no">t </span><span class="m">4 </span>sieve <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> n sqrt sieve [ n sieve mark-multiples2 ] each-prime2
</span></span><span class="line"><span class="cl"> V{ <span class="m">2 3 5 7 </span>} <span class="nb">clone </span>:> primes
</span></span><span class="line"><span class="cl"> n sieve [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>n <span class="nb"><= </span>[ primes <span class="nb">push </span>] [ <span class="nb">drop </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] each-prime2 primes <span class="k">;
</span></span></span></code></pre></div><p>Calculating prime numbers up to 10 million is super fast!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ <span class="m">10,000,000 </span>sieve5 ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.033914378 </span>seconds
</span></span></code></pre></div><p>We can compute all the 11,078,937 prime numbers up to 200 million in
about a second!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ <span class="m">200,000,000 </span>sieve5 ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.970876855 </span>seconds
</span></span></code></pre></div><p>And all the 50,847,534 prime numbers up to 1 billion in about six
seconds!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ <span class="m">1,000,000,000 </span>sieve5 ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">6.141224805 </span>seconds
</span></span></code></pre></div><p>Not quite <a href="https://cr.yp.to/primegen.html">primegen</a> or
<a href="https://primesieve.org/">primesieve</a>, but not bad for a laptop!</p>
<p>This is available in the
<a href="https://github.com/factor/factor/blob/master/basis/math/primes/erato/fast/fast.factor">math.primes.erato.fast</a>
vocabulary on our nightly builds!</p>
Sieve of Eratosthenes
https://re.factorcode.org/2015/06/sieve-of-eratosthenes.html
Sun, 07 Jun 2015 08:21:00 -0700https://re.factorcode.org/2015/06/sieve-of-eratosthenes.html<p>I noticed that the <a href="https://crystal-lang.org/">Crystal Programming
Language</a> homepage has an example of the
<a href="https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes">Sieve of
Eratosthenes</a>
algorithm for finding prime numbers. I thought it would be fun to show a
few simple variations of that algorithm in
<a href="https://factorcode.org">Factor</a>.</p>
<p>The algorithm works by iteratively marking as not prime the multiples of
each prime, starting with the multiples of 2. In pseudocode, it looks
like this:</p>
<pre tabindex="0"><code>Input: an integer n > 1
Let A be an array of Boolean values, indexed by integers 2 to n,
initially all set to true.
for i = 2, 3, 4, ..., not exceeding √n:
if A[i] is true:
for j = i2, i2+i, i2+2i, i2+3i, ..., not exceeding n :
A[j] := false
Output: all i such that A[i] is true.
</code></pre><p>We will be looking at the performance and memory usage to calculate the
664,579 prime numbers between 1 and 10,000,000.</p>
<h3 id="version-1">Version 1</h3>
<p>Our first version, is a straightforward implementation from the
pseudocode:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">sieve1</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">primes</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> n <span class="m">1 </span><span class="nb">+ </span><span class="no">t </span><span class="nb"><array> </span>:> sieve
</span></span><span class="line"><span class="cl"> <span class="no">f </span><span class="m">0 </span>sieve <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span><span class="m">1 </span>sieve <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="m">2 </span>n sqrt [a..b] [| i |
</span></span><span class="line"><span class="cl"> i sieve <span class="nb">nth </span>[
</span></span><span class="line"><span class="cl"> i <span class="nb">sq </span>n i <range> [| j |
</span></span><span class="line"><span class="cl"> <span class="no">f </span>j sieve <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> sieve [ <span class="nb">drop </span>] <span class="nb">selector </span>[ <span class="nb">each-index </span>] <span class="nb">dip </span><span class="k">;
</span></span></span></code></pre></div><p>We can show that it works for the first few prime numbers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">25 </span>sieve1 <span class="m">.
</span></span></span><span class="line"><span class="cl">V{ <span class="m">2 3 5 7 11 13 17 19 23 </span>}
</span></span></code></pre></div><p>Calculating prime numbers up to 10 million takes less than half a
second:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">10,000,000 </span>sieve1 ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.408065579 </span>seconds
</span></span></code></pre></div><p>The memory used is basically an array with 10 million elements in it,
each element of the array is a boolean (64 bits on my laptop). Total
memory used is 80 MB.</p>
<h3 id="version-2">Version 2</h3>
<p>Storing booleans can be more memory-efficient using
<a href="https://docs.factorcode.org/content/article-bit-arrays.html">bit-arrays</a>,
where each boolean is stored as a single bit. The logic is the same,
however primes are marked by false rather than true.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">sieve2</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">primes</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> n <span class="m">1 </span><span class="nb">+ </span><bit-array> :> sieve
</span></span><span class="line"><span class="cl"> <span class="no">t </span><span class="m">0 </span>sieve <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> <span class="no">t </span><span class="m">1 </span>sieve <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="m">2 </span>n sqrt [a..b] [| i |
</span></span><span class="line"><span class="cl"> i sieve <span class="nb">nth </span>[
</span></span><span class="line"><span class="cl"> i <span class="nb">sq </span>n i <range> [| j |
</span></span><span class="line"><span class="cl"> <span class="no">t </span>j sieve <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> sieve [ <span class="nb">drop not </span>] <span class="nb">selector </span>[ <span class="nb">each-index </span>] <span class="nb">dip </span><span class="k">;
</span></span></span></code></pre></div><p>Calculating prime numbers up to 10 million is faster:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">10,000,000 </span>sieve2 ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.241894524 </span>seconds
</span></span></code></pre></div><p>The memory used is now one bit per number, so only 10 million bits or
1.25 MB.</p>
<h3 id="version-3">Version 3</h3>
<p>Since we know that even numbers (except <code>2</code>) are not prime, we can only
work with odd numbers and start our search from <code>3</code>. For each number
found, we can check only the odd multiples by marking off <code>i2 + k*i</code> for
only even <code>k</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">sieve3</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">sieve</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> n <span class="nb">dup odd? </span>[ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">when 2/ </span><bit-array> :> sieve
</span></span><span class="line"><span class="cl"> <span class="no">t </span><span class="m">0 </span>sieve <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="m">3 </span>n sqrt <span class="m">2 </span><range> [| i |
</span></span><span class="line"><span class="cl"> i <span class="nb">2/ </span>sieve <span class="nb">nth </span>[
</span></span><span class="line"><span class="cl"> i <span class="nb">sq </span>n i <span class="m">2 </span><span class="nb">* </span><range> [| j |
</span></span><span class="line"><span class="cl"> <span class="no">t </span>j <span class="nb">2/ </span>sieve <span class="nb">set-nth
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> V{ <span class="m">2 </span>} <span class="nb">clone </span>:> primes
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> sieve [
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span>[ <span class="nb">drop </span>] [ <span class="m">2 </span><span class="nb">* </span><span class="m">1 </span><span class="nb">+ </span>primes <span class="nb">push </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-index
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> primes <span class="k">;
</span></span></span></code></pre></div><blockquote>
<p><em>Note: the convenience of
<a href="https://docs.factorcode.org/content/word-selector,sequences.html">selector</a>
is not available because we want to initialize the list of primes with
<code>2</code>. Instead, we just do it manually.</em></p>
</blockquote>
<p>Calculating prime numbers up to 10 million is <em><strong>much</strong></em> faster!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">10,000,000 </span>sieve3 ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.110387127 </span>seconds
</span></span></code></pre></div><p>As for memory usage, it is storing one bit for each odd number, so only
5 million bits or 625 KB.</p>
SEND + MORE = MONEY?
https://re.factorcode.org/2015/06/send-more-money.html
Mon, 01 Jun 2015 17:52:00 -0700https://re.factorcode.org/2015/06/send-more-money.html<p>There’s a classic <a href="https://en.wikipedia.org/wiki/Verbal_arithmetic">verbal
arithmetic</a> puzzle that
was published in 1924 by <a href="https://en.wikipedia.org/wiki/Henry_Dudeney">Henry
Dudeney</a>, where you solve
the equation:</p>
<pre tabindex="0"><code> S E N D
+ M O R E
-----------
= M O N E Y
</code></pre><blockquote>
<p><em>Note: Each letter is a unique digit and <code>S</code> and <code>M</code> can’t be zero.</em></p>
</blockquote>
<p>A few days ago, I noticed a blog post with <a href="https://strangelyconsistent.org/blog/send-more-money-in-perl6">solutions in Perl
6</a> that
references another blog post with a <a href="https://blog.plover.com/prog/haskell/monad-search.html">solution in
Haskell</a>. I
thought I’d show a solution in <a href="https://factorcode.org">Factor</a> using the
<a href="https://docs.factorcode.org/content/vocab-backtrack.html">backtrack</a>
vocabulary that provides something like John McCarthy’s <code>amb</code> <a href="https://www-formal.stanford.edu/jmc/basis1/node7.html#SECTION00025000000000000000">ambiguous
operator</a>.</p>
<p>First, we need a list of available digits, just the numbers 0 through 9:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">digits</span> { <span class="m">0 1 2 3 4 5 6 7 8 9 </span>}
</span></span></code></pre></div><p>Next, we turn a sequence of digits into a number (e.g., <code>{ 1 2 3 4 }</code>
becomes <code>1234</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>number</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">) </span><span class="m">0 </span>[ [ <span class="m">10 </span><span class="nb">* </span>] <span class="nb">dip + </span>] <span class="nb">reduce </span><span class="k">;
</span></span></span></code></pre></div><p>We can then implement our solver, by choosing digits while progressively
restricting the possible values for future digits using the ones we’ve
chosen so far (using <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a> to
store the digits):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">send-more-money</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> digits { <span class="m">0 </span>} diff amb-lazy :> s
</span></span><span class="line"><span class="cl"> digits { s } diff amb-lazy :> e
</span></span><span class="line"><span class="cl"> digits { s e } diff amb-lazy :> n
</span></span><span class="line"><span class="cl"> digits { s e n } diff amb-lazy :> d
</span></span><span class="line"><span class="cl"> { s e n d } >number :> send
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> digits { <span class="m">0 </span>s e n d } diff amb-lazy :> m
</span></span><span class="line"><span class="cl"> digits { s e n d m } diff amb-lazy :> o
</span></span><span class="line"><span class="cl"> digits { s e n d m o } diff amb-lazy :> r
</span></span><span class="line"><span class="cl"> { m o r e } >number :> more
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> digits { s e n d m o r } diff amb-lazy :> y
</span></span><span class="line"><span class="cl"> { m o n e y } >number :> money
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> send more <span class="nb">+ </span>money <span class="nb">= </span>[
</span></span><span class="line"><span class="cl"> send more money <span class="s">" %s\n+ %s\n= %s\n"</span> printf
</span></span><span class="line"><span class="cl"> ] <span class="nb">when
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> ] amb-all <span class="k">;
</span></span></span></code></pre></div><blockquote>
<p><em>Note: We search all solutions using
<a href="https://docs.factorcode.org/content/word-amb-all,backtrack.html">amb-all</a>
(even though there is only one). In this case, it is effectively an
iterative search, which we could implement without backtracking. If we
wanted the first solution, we could use
<a href="https://docs.factorcode.org/content/word-if-amb,backtrack.html">if-amb</a>.</em></p>
</blockquote>
<p>So, what’s the answer? Let’s see!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> send-more-money
</span></span><span class="line"><span class="cl"> <span class="m">9567
</span></span></span><span class="line"><span class="cl"><span class="nb">+ </span> <span class="m">1085
</span></span></span><span class="line"><span class="cl"><span class="nb">= </span><span class="m">10652
</span></span></span></code></pre></div><p>Neat! And it’s fast too – solving this puzzle in about 2.5 seconds on
my laptop.</p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/send-more-money/send-more-money.factor">GitHub</a>.</p>
File Monitor
https://re.factorcode.org/2015/05/file-monitor.html
Tue, 05 May 2015 15:31:00 -0700https://re.factorcode.org/2015/05/file-monitor.html<p><a href="https://factorcode.org">Factor</a> has a cross-platform <a href="https://docs.factorcode.org/content/article-io.monitors.html">file-system change
monitor</a>
which supports detecting changes to file names, attributes and contents
under a specified directory.</p>
<p>There is some <a href="https://docs.factorcode.org/content/article-io.monitors.platforms.html">minor platform
differences</a>
between Mac OS, Windows, and Linux which may be worth looking at if
you are building on top of the
<a href="https://docs.factorcode.org/content/vocab-io.monitors.html">io.monitors</a>
vocabulary. I was curious about what kind of events are generated for
various test-cases and built a small utility to experiment with it.</p>
<p>Some code to monitor for changed paths recursively in a directory and
print each one out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">watch-loop</span> <span class="nf">( </span><span class="nv">monitor</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>next-change path>> <span class="nb">print flush </span>watch-loop <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">watch-directory</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <span class="no">t </span>[ watch-loop ] with-monitor ] with-monitors <span class="k">;
</span></span></span></code></pre></div><p>I’ve committed this as the
<a href="https://github.com/factor/factor/blob/master/extra/file-monitor/file-monitor.factor">file-monitor</a>
tool (with support for an optional <a href="https://docs.factorcode.org/content/article-command-line.html">command-line
argument</a>
to specify which directory to monitor as well as printing the <a href="https://docs.factorcode.org/content/article-io.monitors.descriptors.html">change
descriptors</a>).
You can run it very simply:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ factor -run<span class="o">=</span>file-monitor <span class="o">[</span>path<span class="o">]</span>
</span></span></code></pre></div><p>An example session on Linux, monitoring some simple changes to files in
<code>/tmp</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ factor -run<span class="o">=</span>file-monitor /tmp <span class="p">&</span>
</span></span><span class="line"><span class="cl">Monitoring /tmp
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ touch /tmp/a
</span></span><span class="line"><span class="cl"><span class="o">{</span> +add-file+ <span class="o">}</span> /tmp/a
</span></span><span class="line"><span class="cl"><span class="o">{</span> +add-file+ <span class="o">}</span> /tmp/a
</span></span><span class="line"><span class="cl"><span class="o">{</span> +modify-file+ <span class="o">}</span> /tmp/a
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ <span class="nb">echo</span> <span class="s2">"test"</span> > /tmp/a
</span></span><span class="line"><span class="cl"><span class="o">{</span> +modify-file+ <span class="o">}</span> /tmp/a
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ rm /tmp/a
</span></span><span class="line"><span class="cl"><span class="o">{</span> +remove-file+ <span class="o">}</span> /tmp/a
</span></span></code></pre></div>File Server
https://re.factorcode.org/2015/05/file-server.html
Fri, 01 May 2015 17:17:00 -0700https://re.factorcode.org/2015/05/file-server.html<p>Python has a neat feature that lets you serve files from the current
directory.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="c1"># Python 2</span>
</span></span><span class="line"><span class="cl">$ python2 -m SimpleHTTPServer
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Python 3</span>
</span></span><span class="line"><span class="cl">$ python3 -m http.server
</span></span></code></pre></div><p>I always thought this was a quick and useful way to share files on a
local network. Given that <a href="https://factorcode.org">Factor</a> has a <a href="https://docs.factorcode.org/content/article-http.server.html">HTTP
Server</a>, we
should be able to implement this!</p>
<p>We already have support for <a href="https://docs.factorcode.org/content/article-http.server.static.html">serving static
content</a>
and <a href="https://docs.factorcode.org/content/article-http.server.cgi.html">serving CGI
scripts</a>,
so we can very simply implement a script to create and launch a <a href="https://docs.factorcode.org/content/article-http.server.html">HTTP
server</a> for
the <a href="https://docs.factorcode.org/content/word-current-directory,io.pathnames.html">current
directory</a>
(or the one specified on the
<a href="https://docs.factorcode.org/content/article-command-line.html">command-line</a>),
logging HTTP connections to
<a href="https://en.wikipedia.org/wiki/Standard_streams#Standard_output_.28stdout.29">stdout</a>.</p>
<p>This is available in the
<a href="https://github.com/factor/factor/blob/master/extra/file-server/file-server.factor">file-server</a>
vocabulary, now you can:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ factor -run<span class="o">=</span>file-server <span class="o">[</span>--cgi<span class="o">]</span> <span class="o">[</span>path<span class="o">]</span>
</span></span></code></pre></div><p>Currently, this defaults to serving files on port 8080 from all
available network interfaces. In the future, it would be nice to add the
ability to specify port and network interfaces to bind.</p>
Burrows-Wheeler Transform
https://re.factorcode.org/2015/04/burrows-wheeler-transform.html
Wed, 29 Apr 2015 15:49:00 -0700https://re.factorcode.org/2015/04/burrows-wheeler-transform.html<p>The <a href="https://en.wikipedia.org/wiki/Burrows%E2%80%93Wheeler_transform">Burrows–Wheeler
transform</a> is a
reversible method of rearranging text used to improve the performance of
compression algorithms, such as
<a href="https://en.wikipedia.org/wiki/Bzip2">bzip2</a>.</p>
<p>We will implement transform, <code>bwt</code>, and inverse transform, <code>ibwt</code>, in
both <a href="https://python.org">Python</a> and <a href="https://factorcode.org">Factor</a>.
First with a slow and simple algorithm, and then second with a faster
version.</p>
<h3 id="version-1">Version 1</h3>
<p>We start with the pseudocode suggested in the <a href="https://en.wikipedia.org/wiki/Burrows%E2%80%93Wheeler_transform">Wikipedia
article</a>:</p>
<pre tabindex="0"><code>function BWT (string s)
append an 'EOF' character to s
create a table, rows are all possible rotations of s
sort rows alphabetically
return (last column of the table)
</code></pre><p>In Python, this might look like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">bwt</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">s</span> <span class="o">=</span> <span class="n">s</span> <span class="o">+</span> <span class="s1">'</span><span class="se">\0</span><span class="s1">'</span>
</span></span><span class="line"><span class="cl"> <span class="n">n</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">m</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">s</span><span class="p">[</span><span class="n">i</span><span class="p">:]</span> <span class="o">+</span> <span class="n">s</span><span class="p">[:</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s1">''</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">m</span><span class="p">)</span>
</span></span></code></pre></div><p>In Factor, using
<a href="https://docs.factorcode.org/content/word-all-rotations,sequences.extras.html">all-rotations</a>,
it might look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bwt</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span><span class="nb">suffix </span>all-rotations natural-sort [ <span class="nb">last </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>The pseudocode to perform the inverse transform:</p>
<pre tabindex="0"><code>function inverseBWT (string s)
create empty table
repeat length(s) times
// first insert creates first column
insert s as a column of table before first column
sort rows of the table alphabetically
return (row that ends with the 'EOF' character)
</code></pre><p>In Python, this might look like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">ibwt</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">n</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">m</span> <span class="o">=</span> <span class="p">[</span><span class="s1">''</span><span class="p">]</span> <span class="o">*</span> <span class="n">n</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">m</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">s</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">m</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">[</span><span class="n">x</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">m</span> <span class="k">if</span> <span class="n">x</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s1">'</span><span class="se">\0</span><span class="s1">'</span><span class="p">)][</span><span class="mi">0</span><span class="p">][:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
</span></span></code></pre></div><p>In Factor, we could implement it like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">ibwt</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>[ <span class="s">""</span> <span class="nb"><array> </span>] <span class="nb">keep </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> '[ _ [ <span class="nb">prefix </span>] <span class="nb">2map </span>natural-sort ] <span class="nb">times
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">last </span><span class="m">0 </span><span class="nb">= </span>] <span class="nb">find nip but-last </span><span class="k">;
</span></span></span></code></pre></div><p>Unfortunately, this is very slow, with most of the performance loss in
the invert transform.</p>
<h3 id="version-2">Version 2</h3>
<p>Another way to <a href="https://codereview.stackexchange.com/a/21120">increase the speed of BWT
inverse</a> is to use an
algorithm that returns an index into the sorted rotations along with the
transform.</p>
<p>In Python, it looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">bwt</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">n</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">m</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">s</span><span class="p">[</span><span class="n">i</span><span class="p">:]</span> <span class="o">+</span> <span class="n">s</span><span class="p">[:</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">m</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="n">s</span><span class="p">),</span> <span class="s1">''</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">m</span><span class="p">)</span>
</span></span></code></pre></div><p>In Factor, it might look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bwt</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">i</span> <span class="nv">seq'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>all-rotations natural-sort
</span></span><span class="line"><span class="cl"> [ <span class="nb">index </span>] [ [ <span class="nb">last </span>] <span class="nb">map </span>] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>In Python, the inverse transform looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">ibwt</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">s</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">row</span><span class="p">(</span><span class="n">k</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">permutation</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">((</span><span class="n">t</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">t</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">s</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="n">s</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">t</span><span class="p">,</span> <span class="n">k</span> <span class="o">=</span> <span class="n">permutation</span><span class="p">[</span><span class="n">k</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="k">yield</span> <span class="n">t</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s1">''</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">row</span><span class="p">(</span><span class="n">k</span><span class="p">))</span>
</span></span></code></pre></div><p>In Factor, that roughly translates to:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">ibwt</span> <span class="nf">( </span><span class="nv">i</span> <span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>] [ <span class="nb"><enum> </span>sort-values ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> '[ _ <span class="nb">nth first2 </span>] <span class="nb">replicate nip </span><span class="k">;
</span></span></span></code></pre></div><p>An <a href="https://github.com/factor/factor/blob/master/extra/math/transforms/bwt/bwt.factor">improved version
2</a>
is available in the development version. In particular, it uses <a href="https://docs.factorcode.org/content/vocab-sequences.rotated.html">rotated
virtual
sequences</a>
for increased performance and returns transformations that match the
type of the input sequence.</p>
Writing MIDI Files
https://re.factorcode.org/2015/04/writing-midi-files.html
Tue, 28 Apr 2015 09:16:00 -0700https://re.factorcode.org/2015/04/writing-midi-files.html<p>Previously, I wrote about <a href="https://re.factorcode.org/2015/04/reading-midi-files.html">Reading MIDI
Files</a>
using <a href="https://factorcode.org">Factor</a>.</p>
<p>Now, we are going to create a writer for MIDI files in less than 180
lines of additional code.</p>
<h3 id="variable-length-quantity">Variable-Length Quantity</h3>
<p>To write a variable-length integer, we first “reverse” it, tagging the
8th bit of each additional byte. Then, we write each byte out to the
<a href="https://docs.factorcode.org/content/word-output-stream,io.html">output-stream</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-number</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <span class="m">0x7f </span><span class="nb">bitand </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ <span class="m">-7 </span><span class="nb">shift dup zero? </span>] [
</span></span><span class="line"><span class="cl"> [ <span class="m">8 </span><span class="nb">shift </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> [ <span class="m">0x7f </span><span class="nb">bitand </span><span class="m">0x80 </span><span class="nb">bitor + </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">until drop
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ [ <span class="m">-8 </span><span class="nb">shift </span>] [ <span class="m">7 </span><span class="nb">bit? </span>] <span class="nb">bi </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span><span class="m">0xff </span><span class="nb">bitand write1 </span>] <span class="nb">do while drop </span><span class="k">;
</span></span></span></code></pre></div><blockquote>
<p><em>Note: there is probably a cleaner way to do this. Patches are
welcome! ☺</em></p>
</blockquote>
<h3 id="text">Text</h3>
<p>Strings are encoded in <a href="https://en.wikipedia.org/wiki/UTF-8">UTF-8</a>,
prefixed with their encoded length in bytes (as a variable-length
quantity).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-string</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> utf8 encode [ <span class="nb">length </span>write-number ] [ <span class="nb">write </span>] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><h3 id="writing-events">Writing Events</h3>
<p>The three types of events will each have to be handled differently. To
do this, we will make a <a href="https://docs.factorcode.org/content/article-generic.html">generic
method</a> that is
given the previous status byte (to enable “running status” for MIDI
events) and returns the new status byte.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">GENERIC:</span> <span class="nf">write-event</span> <span class="nf">( </span><span class="nv">prev-status</span> <span class="nv">event</span> <span class="nf">-- </span><span class="nv">status</span> <span class="nf">)
</span></span></span></code></pre></div><p>First, we write MIDI events, implementing the “running status”.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-status</span> <span class="nf">( </span><span class="nv">prev-status</span> <span class="nv">status</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0xf0 </span><span class="nb">< </span>[
</span></span><span class="line"><span class="cl"> [ <span class="nb">= </span>] <span class="nb">keep swap </span>[ <span class="nb">drop </span>] [ <span class="nb">write1 </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="nb">nip write1
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-channel</span> <span class="nf">( </span><span class="nv">prev-status</span> <span class="nv">value</span> <span class="nv">status</span> <span class="nv">quot</span> <span class="nf">-- </span><span class="nv">status</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span>[
</span></span><span class="line"><span class="cl"> <span class="s">"channel"</span> <span class="nb">of + </span>[ write-status ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">dip call </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">midi-event</span> <span class="nf">write-event</span>
</span></span><span class="line"><span class="cl"> [ delta>> write-number ] [ value>> ] [ name>> ] <span class="nb">tri </span>{
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> { <span class="s">"note-off"</span> [
</span></span><span class="line"><span class="cl"> <span class="m">0x80 </span>[
</span></span><span class="line"><span class="cl"> [ <span class="s">"note"</span> <span class="nb">of write1 </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"velocity"</span> <span class="nb">of write1 </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] write-channel ] }
</span></span><span class="line"><span class="cl"> { <span class="s">"note-on"</span> [
</span></span><span class="line"><span class="cl"> <span class="m">0x90 </span>[
</span></span><span class="line"><span class="cl"> [ <span class="s">"note"</span> <span class="nb">of write1 </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"velocity"</span> <span class="nb">of write1 </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] write-channel ] }
</span></span><span class="line"><span class="cl"> { <span class="s">"polytouch"</span> [
</span></span><span class="line"><span class="cl"> <span class="m">0xa0 </span>[
</span></span><span class="line"><span class="cl"> [ <span class="s">"note"</span> <span class="nb">of write1 </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"value"</span> <span class="nb">of write1 </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] write-channel ] }
</span></span><span class="line"><span class="cl"> { <span class="s">"control-change"</span> [
</span></span><span class="line"><span class="cl"> <span class="m">0xb0 </span>[
</span></span><span class="line"><span class="cl"> [ <span class="s">"control"</span> <span class="nb">of write1 </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"value"</span> <span class="nb">of write1 </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] write-channel ] }
</span></span><span class="line"><span class="cl"> { <span class="s">"program-change"</span> [
</span></span><span class="line"><span class="cl"> <span class="m">0xc0 </span>[ <span class="s">"program"</span> <span class="nb">of write1 </span>] write-channel ] }
</span></span><span class="line"><span class="cl"> { <span class="s">"aftertouch"</span> [
</span></span><span class="line"><span class="cl"> <span class="m">0xd0 </span>[ <span class="s">"value"</span> <span class="nb">of write1 </span>] write-channel ] }
</span></span><span class="line"><span class="cl"> { <span class="s">"pitchwheel"</span> [
</span></span><span class="line"><span class="cl"> <span class="m">0xe0 </span>[
</span></span><span class="line"><span class="cl"> <span class="s">"pitch"</span> <span class="nb">of </span>min-pitchwheel <span class="nb">-
</span></span></span><span class="line"><span class="cl"> [ <span class="m">0x7f </span><span class="nb">bitand write1 </span>]
</span></span><span class="line"><span class="cl"> [ <span class="m">-7 </span><span class="nb">shift write1 </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] write-channel ] }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! system common messages</span>
</span></span><span class="line"><span class="cl"> { <span class="s">"sysex"</span> [
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="m">0xf0 </span><span class="nb">dup write1 </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> <span class="nb">write </span><span class="m">0xf7 </span><span class="nb">write1 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"quarter-made"</span> [
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="m">0xf1 </span><span class="nb">dup write1 </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"frame-type"</span> <span class="nb">of </span><span class="m">4 </span><span class="nb">shift </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"frame-value"</span> <span class="nb">of + </span>] <span class="nb">bi write1 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"songpos"</span> [
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="m">0xf2 </span><span class="nb">dup write1 </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> [ <span class="m">0x7f </span><span class="nb">bitand write1 </span>]
</span></span><span class="line"><span class="cl"> [ <span class="m">-7 </span><span class="nb">shift write1 </span>] <span class="nb">bi </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"song-select"</span> [
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="m">0xf3 </span><span class="nb">dup write1 </span>] <span class="nb">dip write1 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"tune-request"</span> [ <span class="nb">2drop </span><span class="m">0xf6 </span><span class="nb">dup write1 </span>] }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! real-time messages</span>
</span></span><span class="line"><span class="cl"> { <span class="s">"clock"</span> [ <span class="nb">2drop </span><span class="m">0xf8 </span><span class="nb">dup write1 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"start"</span> [ <span class="nb">2drop </span><span class="m">0xfa </span><span class="nb">dup write1 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"continue"</span> [ <span class="nb">2drop </span><span class="m">0xfb </span><span class="nb">dup write1 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"stop"</span> [ <span class="nb">2drop </span><span class="m">0xfc </span><span class="nb">dup write1 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"active-sensing"</span> [ <span class="nb">2drop </span><span class="m">0xfe </span><span class="nb">dup write1 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"reset"</span> [ <span class="nb">2drop </span><span class="m">0xff </span><span class="nb">dup write1 </span>] }
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div><p>Next, we write meta events:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">meta-event</span> <span class="nf">write-event</span>
</span></span><span class="line"><span class="cl"> [ delta>> write-number ] [ value>> ] [ name>> ] <span class="nb">tri </span>
</span></span><span class="line"><span class="cl"> <span class="m">0xff </span><span class="nb">write1 </span>{
</span></span><span class="line"><span class="cl"> { <span class="s">"sequence-number"</span> [
</span></span><span class="line"><span class="cl"> B{ <span class="m">0x00 0x02 </span>} <span class="nb">write </span><span class="m">2 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"text"</span> [ <span class="m">0x01 </span><span class="nb">write1 </span>write-string ] }
</span></span><span class="line"><span class="cl"> { <span class="s">"copyright"</span> [ <span class="m">0x02 </span><span class="nb">write1 </span>write-string ] }
</span></span><span class="line"><span class="cl"> { <span class="s">"track-name"</span> [ <span class="m">0x03 </span><span class="nb">write1 </span>write-string ] }
</span></span><span class="line"><span class="cl"> { <span class="s">"instrument-name"</span> [ <span class="m">0x04 </span><span class="nb">write1 </span>write-string ] }
</span></span><span class="line"><span class="cl"> { <span class="s">"lyrics"</span> [ <span class="m">0x05 </span><span class="nb">write1 </span>write-string ] }
</span></span><span class="line"><span class="cl"> { <span class="s">"marker"</span> [ <span class="m">0x06 </span><span class="nb">write1 </span>write-string ] }
</span></span><span class="line"><span class="cl"> { <span class="s">"cue-point"</span> [ <span class="m">0x07 </span><span class="nb">write1 </span>write-string ] }
</span></span><span class="line"><span class="cl"> { <span class="s">"device-name"</span> [ <span class="m">0x09 </span><span class="nb">write1 </span>write-string ] }
</span></span><span class="line"><span class="cl"> { <span class="s">"channel-prefix"</span> [ B{ <span class="m">0x20 0x01 </span>} <span class="nb">write write1 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"midi-port"</span> [ B{ <span class="m">0x21 0x01 </span>} <span class="nb">write write1 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"end-of-track"</span> [ B{ <span class="m">0x2f 0x00 </span>} <span class="nb">write drop </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"set-tempo"</span> [ B{ <span class="m">0x51 0x03 </span>} <span class="nb">write </span><span class="m">3 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"smpte-offset"</span> [
</span></span><span class="line"><span class="cl"> B{ <span class="m">0x54 0x05 </span>} <span class="nb">write </span>{
</span></span><span class="line"><span class="cl"> [ <span class="s">"frame-rate"</span> <span class="nb">of </span><span class="m">6 </span><span class="nb">shift </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"hours"</span> <span class="nb">of + write1 </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"minutes"</span> <span class="nb">of write1 </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"seconds"</span> <span class="nb">of write1 </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"frames"</span> <span class="nb">of write1 </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"subframes"</span> <span class="nb">of write1 </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"time-signature"</span> [
</span></span><span class="line"><span class="cl"> B{ <span class="m">0x58 0x04 </span>} <span class="nb">write </span>{
</span></span><span class="line"><span class="cl"> [ <span class="s">"numerator"</span> <span class="nb">of write1 </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"denominator"</span> <span class="nb">of </span><span class="m">2 </span><span class="nb">/i write1 </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"clocks-per-tick"</span> <span class="nb">of write1 </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"notated-32nd-notes-per-beat"</span> <span class="nb">of write1 </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"key-signature"</span> [
</span></span><span class="line"><span class="cl"> B{ <span class="m">0x59 0x02 </span>} <span class="nb">write
</span></span></span><span class="line"><span class="cl"> key-signatures <span class="nb">value-at write </span>] }
</span></span><span class="line"><span class="cl"> { <span class="s">"sequencer-specific"</span> [
</span></span><span class="line"><span class="cl"> <span class="m">0x7f </span><span class="nb">write1
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>write-number ] [ <span class="nb">write </span>] <span class="nb">bi </span>] }
</span></span><span class="line"><span class="cl"> } <span class="nb">case drop </span><span class="no">f </span><span class="k">;
</span></span></span></code></pre></div><p>Finally, we write system-exclusive events:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">sysex-event</span> <span class="nf">write-event</span>
</span></span><span class="line"><span class="cl"> <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> [ delta>> write-number ]
</span></span><span class="line"><span class="cl"> [ type>> <span class="nb">write1 </span>]
</span></span><span class="line"><span class="cl"> [ bytes>> <span class="nb">write </span>] <span class="nb">tri </span><span class="no">f </span><span class="k">;
</span></span></span></code></pre></div><p>Writing a MIDI header and tracks, generically as “chunks”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">GENERIC:</span> <span class="nf">write-chunk</span> <span class="nf">( </span><span class="nv">chunk</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">midi-header</span> <span class="nf">write-chunk</span>
</span></span><span class="line"><span class="cl"> $[ <span class="s">"MThd"</span> >byte-array ] <span class="nb">write
</span></span></span><span class="line"><span class="cl"> $[ <span class="m">6 4 </span>>be ] <span class="nb">write
</span></span></span><span class="line"><span class="cl"> [ format>> ] [ #chunks>> ] [ division>> ] <span class="nb">tri
</span></span></span><span class="line"><span class="cl"> [ <span class="m">2 </span>>be <span class="nb">write </span>] <span class="nb">tri@ </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">midi-track</span> <span class="nf">write-chunk</span>
</span></span><span class="line"><span class="cl"> $[ <span class="s">"MTrk"</span> >byte-array ] <span class="nb">write
</span></span></span><span class="line"><span class="cl"> binary [
</span></span><span class="line"><span class="cl"> events>> <span class="no">f </span><span class="nb">swap </span>[ write-event ] <span class="nb">each drop
</span></span></span><span class="line"><span class="cl"> ] with-byte-writer
</span></span><span class="line"><span class="cl"> [ <span class="nb">length </span><span class="m">4 </span>>be <span class="nb">write </span>] [ <span class="nb">write </span>] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>Finally, words to write MIDI objects, either to a
<a href="https://docs.factorcode.org/content/article-byte-arrays.html">byte-array</a>,
or to a file.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-midi</span> <span class="nf">( </span><span class="nv">midi</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ header>> write-chunk ]
</span></span><span class="line"><span class="cl"> [ chunks>> [ write-chunk ] <span class="nb">each </span>] <span class="nb">bi </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">midi></span> <span class="nf">( </span><span class="nv">midi</span> <span class="nf">-- </span><span class="nv">byte-array</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> binary [ write-midi ] with-byte-writer <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">midi>file</span> <span class="nf">( </span><span class="nv">midi</span> <span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> binary [ write-midi ] with-file-writer <span class="k">;
</span></span></span></code></pre></div><p>This is available now in the
<a href="https://github.com/factor/factor/blob/master/extra/midi/midi.factor">midi</a>
vocabulary.</p>
Reading MIDI Files
https://re.factorcode.org/2015/04/reading-midi-files.html
Fri, 24 Apr 2015 14:43:00 -0700https://re.factorcode.org/2015/04/reading-midi-files.html<p><a href="https://en.wikipedia.org/wiki/MIDI">MIDI</a> is a specification for music,
describing how electronic musical instruments and computers can
communicate with each other.</p>
<p>Unlike digital audio formats such as
<a href="https://en.wikipedia.org/wiki/MP3">MP3</a>, the <a href="https://www.midi.org/techspecs/smf.php">Standard MIDI
File</a> does not contain sounds,
but rather a stream of instructions for playing notes, volume, tempo,
and sound effects, as well as track names and other descriptive
information. Because of this, MIDI files tend to be much smaller and
typically allow the music to be easily rearranged or edited.</p>
<p>Using <a href="https://factorcode.org">Factor</a>, we will be creating a parser for
reading MIDI files in under 180 lines of code.</p>
<h3 id="variable-length-quantity">Variable-Length Quantity</h3>
<p>Some integers will be encoded as variable length, using 7 bits per byte
with one bit reserved for the stop bit (indicating you have finished
reading the number). This means the numbers 0 through 127 can be encoded
in a single byte, but larger numbers will require additional bytes.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-number</span> <span class="nf">( -- </span><span class="nv">number</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>[ <span class="m">7 </span><span class="nb">bit? </span>] [
</span></span><span class="line"><span class="cl"> <span class="m">7 </span><span class="nb">shift read1 </span>[ <span class="m">0x7f </span><span class="nb">bitand + </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">do while </span><span class="k">;
</span></span></span></code></pre></div><h3 id="midi-events">MIDI Events</h3>
<p>There are three types of events: MIDI events, system-exclusive events,
and meta events. The majority of events will usually be MIDI events, so
we will parse those first.</p>
<p>Some MIDI events will include the channel in 4 bits of the status byte,
so we handle those separately from the system common and realtime
messages.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">midi-event</span> <span class="nv">delta</span> <span class="nv">name</span> <span class="nv">value</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">C:</span> <span class="nf"><midi-event></span> <span class="nc">midi-event</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-message</span> <span class="nf">( </span><span class="nv">delta</span> <span class="nv">status</span> <span class="nf">-- </span><span class="nv">message</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0xf0 </span><span class="nb">< </span>[
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="c">! channel messages</span>
</span></span><span class="line"><span class="cl"> [ <span class="m">0x0f </span><span class="nb">bitand </span><span class="s">"channel"</span> ,, ] [ <span class="m">0xf0 </span><span class="nb">bitand </span>] <span class="nb">bi </span>{
</span></span><span class="line"><span class="cl"> { <span class="m">0x80 </span>[ <span class="s">"note-off"</span>
</span></span><span class="line"><span class="cl"> <span class="nb">read1 </span><span class="s">"note"</span> ,, <span class="nb">read1 </span><span class="s">"velocity"</span> ,, ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x90 </span>[ <span class="s">"note-on"</span>
</span></span><span class="line"><span class="cl"> <span class="nb">read1 </span><span class="s">"note"</span> ,, <span class="nb">read1 </span><span class="s">"velocity"</span> ,, ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0xa0 </span>[ <span class="s">"polytouch"</span>
</span></span><span class="line"><span class="cl"> <span class="nb">read1 </span><span class="s">"note"</span> ,, <span class="nb">read1 </span><span class="s">"value"</span> ,, ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0xb0 </span>[ <span class="s">"control-change"</span>
</span></span><span class="line"><span class="cl"> <span class="nb">read1 </span><span class="s">"control"</span> ,, <span class="nb">read1 </span><span class="s">"value"</span> ,, ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0xc0 </span>[ <span class="s">"program-change"</span>
</span></span><span class="line"><span class="cl"> <span class="nb">read1 </span><span class="s">"program"</span> ,, ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0xd0 </span>[ <span class="s">"aftertouch"</span>
</span></span><span class="line"><span class="cl"> <span class="nb">read1 </span><span class="s">"value"</span> ,, ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0xe0 </span>[ <span class="s">"pitchwheel"</span>
</span></span><span class="line"><span class="cl"> <span class="nb">read1 read1 </span><span class="m">7 </span><span class="nb">shift + </span><span class="s">"pitch"</span> ,, ] }
</span></span><span class="line"><span class="cl"> } <span class="nb">case
</span></span></span><span class="line"><span class="cl"> ] H{ } make
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> <span class="c">! system common messages</span>
</span></span><span class="line"><span class="cl"> { <span class="m">0xf0 </span>[ <span class="s">"sysex"</span> { <span class="m">0xf7 </span>} <span class="nb">read-until drop </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">0xf1 </span>[ <span class="s">"quarter-made"</span> [
</span></span><span class="line"><span class="cl"> <span class="nb">read1
</span></span></span><span class="line"><span class="cl"> [ <span class="m">-4 </span><span class="nb">shift </span><span class="s">"frame-type"</span> ,, ]
</span></span><span class="line"><span class="cl"> [ <span class="m">0x0f </span><span class="nb">bitand </span><span class="s">"frame-value"</span> ,, ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] H{ } make ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0xf2 </span>[ <span class="s">"songpos"</span> <span class="nb">read1 read1 </span><span class="m">7 </span><span class="nb">shift + </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">0xf3 </span>[ <span class="s">"song-select"</span> <span class="nb">read1 </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">0xf6 </span>[ <span class="s">"tune-request"</span> <span class="no">f </span>] }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! real-time messages</span>
</span></span><span class="line"><span class="cl"> { <span class="m">0xf8 </span>[ <span class="s">"clock"</span> <span class="no">f </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">0xfa </span>[ <span class="s">"start"</span> <span class="no">f </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">0xfb </span>[ <span class="s">"continue"</span> <span class="no">f </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">0xfc </span>[ <span class="s">"stop"</span> <span class="no">f </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">0xfe </span>[ <span class="s">"active-sensing"</span> <span class="no">f </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">0xff </span>[ <span class="s">"reset"</span> <span class="no">f </span>] }
</span></span><span class="line"><span class="cl"> } <span class="nb">case
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><midi-event> <span class="k">;
</span></span></span></code></pre></div><h3 id="meta-events">Meta Events</h3>
<p>Meta events contain descriptive information such as track name, tempo
and time signatures. They are also used to indicate the end of the track
has been reached.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">meta-event</span> <span class="nv">delta</span> <span class="nv">name</span> <span class="nv">value</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">C:</span> <span class="nf"><meta-event></span> <span class="nc">meta-event</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-meta</span> <span class="nf">( </span><span class="nv">status</span> <span class="nv">bytes</span> <span class="nf">-- </span><span class="nv">name</span> <span class="nv">value</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">swap </span>{
</span></span><span class="line"><span class="cl"> { <span class="m">0x00 </span>[ <span class="m">2 </span><span class="nb">head </span>be> <span class="s">"sequence-number"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x01 </span>[ utf8 decode <span class="s">"text"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x02 </span>[ utf8 decode <span class="s">"copyright"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x03 </span>[ utf8 decode <span class="s">"track-name"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x04 </span>[ utf8 decode <span class="s">"instrument-name"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x05 </span>[ utf8 decode <span class="s">"lyrics"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x06 </span>[ utf8 decode <span class="s">"marker"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x07 </span>[ utf8 decode <span class="s">"cue-point"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x09 </span>[ utf8 decode <span class="s">"device-name"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x20 </span>[ <span class="nb">first </span><span class="s">"channel-prefix"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x21 </span>[ <span class="nb">first </span><span class="s">"midi-port"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x2f </span>[ <span class="nb">drop </span><span class="no">t </span><span class="s">"end-of-track"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x51 </span>[ <span class="m">3 </span><span class="nb">head </span>be> <span class="s">"set-tempo"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x54 </span>[
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="m">5 </span>firstn {
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ <span class="m">-6 </span><span class="nb">shift </span><span class="s">"frame-rate"</span> ,, ]
</span></span><span class="line"><span class="cl"> [ <span class="m">0x3f </span><span class="nb">bitand </span><span class="s">"hours"</span> ,, ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"minutes"</span> ,, ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"seconds"</span> ,, ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"frames"</span> ,, ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"subframes"</span> ,, ]
</span></span><span class="line"><span class="cl"> } <span class="nb">spread
</span></span></span><span class="line"><span class="cl"> ] H{ } make <span class="s">"smpte-offset"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x58 </span>[
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="nb">first4 </span>{
</span></span><span class="line"><span class="cl"> [ <span class="s">"numerator"</span> ,, ]
</span></span><span class="line"><span class="cl"> [ <span class="m">2 </span><span class="nb">* </span><span class="s">"denominator"</span> ,, ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"clocks-per-tick"</span> ,, ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"notated-32nd-notes-per-beat"</span> ,, ]
</span></span><span class="line"><span class="cl"> } <span class="nb">spread
</span></span></span><span class="line"><span class="cl"> ] H{ } make <span class="s">"time-signature"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x59 </span>[ <span class="s">"key-signature"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x7f </span>[ <span class="s">"sequencer-specific"</span> ] }
</span></span><span class="line"><span class="cl"> } <span class="nb">case swap </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-meta</span> <span class="nf">( </span><span class="nv">delta</span> <span class="nf">-- </span><span class="nv">event</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">read1 </span>read-number <span class="nb">read </span>parse-meta <meta-event> <span class="k">;
</span></span></span></code></pre></div><h3 id="sysex-events">Sysex Events</h3>
<p>For system-exclusive events, which are typically a sequence of bytes
that are proprietary to particularly MIDI devices, we just preserve the
type (<code>0xf0</code> or <code>0xf7</code>) and raw bytes.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">sysex-event</span> <span class="nv">delta</span> <span class="nv">status</span> <span class="nv">bytes</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">C:</span> <span class="nf"><sysex-event></span> <span class="nc">sysex-event</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-sysex</span> <span class="nf">( </span><span class="nv">delta</span> <span class="nv">status</span> <span class="nf">-- </span><span class="nv">event</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> read-number <span class="nb">read </span><sysex-event> <span class="k">;
</span></span></span></code></pre></div><h3 id="reading-events">Reading Events</h3>
<p>We can now read all types of events, dispatching on the status byte.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-event</span> <span class="nf">( </span><span class="nv">delta</span> <span class="nv">status</span> <span class="nf">-- </span><span class="nv">event</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { <span class="m">0xf0 </span>[ <span class="m">0xf0 </span>read-sysex ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0xf7 </span>[ <span class="m">0xf7 </span>read-sysex ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0xff </span>[ read-meta ] }
</span></span><span class="line"><span class="cl"> [ read-message ]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div><p>Status bytes can be “running”, which means that for channel events they
can be dropped from the stream if they are identical to the previous
MIDI channel event. Meta events (<code>0xff</code>) do not set the running status.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-status</span> <span class="nf">( </span><span class="nv">prev-status</span> <span class="nf">-- </span><span class="nv">prev-status'</span> <span class="nv">status</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> peek1 <span class="nb">dup </span><span class="m">0x80 </span><span class="nb">< </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">drop dup
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="nb">drop read1 dup </span><span class="m">0xff </span><span class="nb">= </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">nip dup
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Each event has a header that is the delta-time (encoded as a variable
length integer) and the status (which may not be present if it is
“running”).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-event-header</span> <span class="nf">( </span><span class="nv">prev-status</span> <span class="nf">-- </span><span class="nv">prev-status'</span> <span class="nv">delta</span> <span class="nv">status</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ read-number ] <span class="nb">dip </span>read-status <span class="nb">swapd </span><span class="k">;
</span></span></span></code></pre></div><p>There are a few ways to parse all events from a
<a href="https://docs.factorcode.org/content/article-byte-arrays.html">byte-array</a>,
but I thought it was a good opportunity to try out <a href="https://docs.factorcode.org/content/vocab-io.streams.peek.html">peekable
streams</a>,
checking if the next event is present.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-events</span> <span class="nf">( </span><span class="nv">data</span> <span class="nf">-- </span><span class="nv">events</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> binary <byte-reader> <peek-stream> [
</span></span><span class="line"><span class="cl"> <span class="no">f </span>[
</span></span><span class="line"><span class="cl"> peek1 [ read-event-header ] [ <span class="no">f f </span>] <span class="nb">if dup
</span></span></span><span class="line"><span class="cl"> ] [ read-event ] <span class="nb">produce 2nip nip
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">with-input-stream </span><span class="k">;
</span></span></span></code></pre></div><h3 id="reading-midi">Reading MIDI</h3>
<p>MIDI files are grouped into a series of chunks. The first chunk is a
MIDI header indicating the format (single or multiple simultaneous
tracks), number of tracks in the file, and division (indicating how to
interpret the delta-times in the file).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">midi-header</span> <span class="nv">format</span> <span class="nv">#chunks</span> <span class="nv">division</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><midi-header></span> <span class="nf">( </span><span class="nv">bytes</span> <span class="nf">-- </span><span class="nv">header</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">2 </span><span class="nb">cut </span><span class="m">2 </span><span class="nb">cut </span>[ be> ] <span class="nb">tri@ </span>midi-header <span class="nb">boa </span><span class="k">;
</span></span></span></code></pre></div><p>Typically, that is followed by MIDI tracks, each containing a series of
events.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">midi-track</span> <span class="nv">events</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><midi-track></span> <span class="nf">( </span><span class="nv">bytes</span> <span class="nf">-- </span><span class="nv">track</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> parse-events midi-track <span class="nb">boa </span><span class="k">;
</span></span></span></code></pre></div><p>Reading the chunks in the file dispatch off the “chunk type”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-chunk</span> <span class="nf">( -- </span><span class="nv">chunk</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span><span class="m">4 </span><span class="nb">read </span>be> <span class="nb">read swap </span>{
</span></span><span class="line"><span class="cl"> { $[ <span class="s">"MThd"</span> >byte-array ] [ <midi-header> ] }
</span></span><span class="line"><span class="cl"> { $[ <span class="s">"MTrk"</span> >byte-array ] [ <midi-track> ] }
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div><p>To read a MIDI stream, we read the header and then all the chunks in the
file, storing them in a <code>midi</code> tuple.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">midi</span> <span class="nv">header</span> <span class="nv">chunks</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">C:</span> <span class="nf"><midi></span> <span class="nc">midi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-header</span> <span class="nf">( -- </span><span class="nv">header</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> read-chunk <span class="nb">dup </span>midi-header? <span class="no">t </span><span class="nb">assert= </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-chunks</span> <span class="nf">( </span><span class="nv">header</span> <span class="nf">-- </span><span class="nv">chunks</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> #chunks>> [ read-chunk ] <span class="nb">replicate </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-midi</span> <span class="nf">( -- </span><span class="nv">midi</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> read-header <span class="nb">dup </span>read-chunks <midi> <span class="k">;
</span></span></span></code></pre></div><p>Parsing a MIDI from raw bytes or a file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>midi</span> <span class="nf">( </span><span class="nv">byte-array</span> <span class="nf">-- </span><span class="nv">midi</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> binary [ read-midi ] with-byte-reader <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">file>midi</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">midi</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> binary [ read-midi ] with-file-reader <span class="k">;
</span></span></span></code></pre></div><p>This is available now in the
<a href="https://github.com/factor/factor/blob/master/extra/midi/midi.factor">midi</a>
vocabulary.</p>
Long URLs
https://re.factorcode.org/2015/04/long-urls.html
Mon, 20 Apr 2015 07:57:00 -0700https://re.factorcode.org/2015/04/long-urls.html<p>In a world of <a href="https://en.wikipedia.org/wiki/Twitter">140 characters</a>,
space is at a premium (even for the <a href="https://www.forbes.com/2009/07/09/longest-tweet-ever-technology-internet-forbes.html">longest tweet
ever</a>).
It has become very common to <a href="https://en.wikipedia.org/wiki/URL_shortening">shorten
URLs</a> when embedding
links.</p>
<p>There are a lot of <a href="https://longurl.org/services">URL shortening
services</a> available, including branded ones
such as <a href="https://t.co">t.co</a> (Twitter), <a href="https://goo.gl">goo.gl</a> (Google),
<a href="https://nyti.ms">nyti.ms</a> (New York Times), and
<a href="https://youtu.be">youtu.be</a> (YouTube). You might not know it, but
<a href="https://factorcode.org">Factor</a> even includes one in the
<a href="https://github.com/factor/factor/tree/master/extra/webapps/wee-url">wee-url</a>
web application.</p>
<p>You could use something like the <a href="https://longurl.org/">LongURL</a> service
to resolve short URLs back to the long URL they point to, but I thought
it would be more fun to show how to use <a href="https://factorcode.org">Factor</a>
to do it!</p>
<p>By default, our
<a href="https://docs.factorcode.org/content/vocab-http.client.html">http.client</a>
automatically follows redirects until exceeding a configurable maximum.
We will need to make requests that do not redirect, using HEAD to
retrieve only the <a href="https://en.wikipedia.org/wiki/List_of_HTTP_header_fields">HTTP
headers</a> and
not the full contents:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">http-head-no-redirects</span> <span class="nf">( </span><span class="nv">url</span> <span class="nf">-- </span><span class="nv">response</span> <span class="nv">data</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <head-request> <span class="m">0 </span>>>redirects http-request* <span class="k">;
</span></span></span></code></pre></div><p>We use symbols to configure a maximum number of redirects (defaulting to
5) and to store the current number of redirects.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">max-redirects</span>
</span></span><span class="line"><span class="cl"><span class="m">5 </span>max-redirects <span class="nb">set-global
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">redirects</span>
</span></span></code></pre></div><p>We want a word that takes a URL and retrieves the next URL, if
redirected. If we exceed our maximum number of redirects, it should
throw an error.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-url</span> <span class="nf">( </span><span class="nv">url</span> <span class="nf">-- </span><span class="nv">next-url</span> <span class="nv">redirected?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> redirects <span class="nb">inc
</span></span></span><span class="line"><span class="cl"> redirects <span class="nb">get </span>max-redirects <span class="nb">get <= </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>http-head-no-redirects <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>redirect? [
</span></span><span class="line"><span class="cl"> <span class="nb">nip </span><span class="s">"location"</span> header <span class="no">t
</span></span></span><span class="line"><span class="cl"> ] [ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] [ too-many-redirects ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>To find the “long URL”, just loop until we are no longer redirected:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">long-url</span> <span class="nf">( </span><span class="nv">short-url</span> <span class="nf">-- </span><span class="nv">long-url</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ next-url ] <span class="nb">loop </span>] <span class="nb">with-scope </span><span class="k">;
</span></span></span></code></pre></div><p>To see it work, we can try it out with a short URL that I just made:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"https://bit.ly/1J0vm1x"</span> long-url <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"https://factorcode.org/"</span>
</span></span></code></pre></div><p>Neat!</p>
<p>This code is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/long-urls/long-urls.factor">GitHub</a>.</p>
Interpolate
https://re.factorcode.org/2015/04/interpolate.html
Sat, 18 Apr 2015 20:42:00 -0700https://re.factorcode.org/2015/04/interpolate.html<p>Today, I made some minor improvements to the
<a href="https://docs.factorcode.org/content/vocab-interpolate.html">interpolate</a>
vocabulary, which provides simple string interpolation and formatting.</p>
<p>We have had the ability to use named variables:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"World"</span> <span class="s">"name"</span> <span class="nb">set
</span></span></span><span class="line"><span class="cl"> <span class="s">"Hello, ${name}"</span> interpolate
</span></span><span class="line"><span class="cl">Hello, World
</span></span></code></pre></div><p>But now we can just as easily use stack arguments (numbered from the top
of the stack):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Mr."</span> <span class="s">"Anderson"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Hello, ${1} ${0}"</span> interpolate
</span></span><span class="line"><span class="cl">Hello, Mr. Anderson
</span></span></code></pre></div><p>In any order, even repeated:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"James"</span> <span class="s">"Bond"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"${0}, ${1} ${0}"</span> interpolate
</span></span><span class="line"><span class="cl">Bond, James Bond
</span></span></code></pre></div><p>As well as anonymously, by order of arguments:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Roses"</span> <span class="s">"red"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"${} are ${}"</span> interpolate
</span></span><span class="line"><span class="cl">Roses are red
</span></span></code></pre></div><p>And even mix named variables and stack arguments:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Factor"</span> <span class="s">"lang"</span> <span class="nb">set
</span></span></span><span class="line"><span class="cl"> <span class="s">"cool"</span> <span class="s">"${lang} is ${0}!"</span> interpolate
</span></span><span class="line"><span class="cl">Factor is cool!
</span></span></code></pre></div><p>Right now we simply convert objects to human-readable strings using the
<a href="https://docs.factorcode.org/content/vocab-present.html">present</a>
vocabulary. In the future, it would be nice to support something like
Python’s <a href="https://docs.python.org/3/library/string.html#formatspec">string format
specifications</a>,
which are similar but slightly different than our
<a href="https://docs.factorcode.org/content/word-printf%2Cformatting.html">printf</a>
support.</p>
Morse Code
https://re.factorcode.org/2015/02/morse-code.html
Sat, 28 Feb 2015 18:19:00 -0800https://re.factorcode.org/2015/02/morse-code.html<p>A couple days ago, Verizon posted a <a href="https://publicpolicy.verizon.com/blog/entry/fccs-throwback-thursday-move-imposes-1930s-rules-on-the-internet">press
release</a>
complaining about the FCC’s recent changes to Internet regulations.
Normally, I wouldn’t really bother with things like this, but they
posted their statement using <a href="https://en.wikipedia.org/wiki/Morse_code">morse
code</a>. While it would be easy
enough to read their <a href="https://publicpolicy.verizon.com/assets/docs/VZ_NR_--_2-26-15_VZ_Statement_on_Open_Internet_Order_FINAL_1.pdf">English
version</a>,
I thought it would be fun to decode it using
<a href="https://factorcode.org">Factor</a>.</p>
<p>This feels a little wonky and a little fragile, but part of that is
probably not having higher level words that we can use for moving
between parsed HTML and its TEXT representation.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">html.parser</span> <span class="nn">html.parser.analyzer</span> <span class="nn">html.parser.printer</span>
</span></span><span class="line"><span class="cl"><span class="nn">http.client</span> <span class="nn">io</span> <span class="nn">kernel</span> <span class="nn">morse</span> <span class="nn">sequences</span> <span class="nn">splitting</span> <span class="nn">wrap.strings</span> <span class="k">;
</span></span></span></code></pre></div><p>1. Download the blog post and parse the HTML.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="s">"https://publicpolicy.verizon.com/blog/entry/fccs-throwback-thursday-move-imposes-1930s-rules-on-the-internet"</span>
</span></span><span class="line"><span class="cl">http-get <span class="nb">nip </span>parse-html
</span></span></code></pre></div><p>2. Extract the morse code text from the post.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="s">"blog"</span> find-by-class-between
</span></span><span class="line"><span class="cl"><span class="s">"p"</span> find-between-first html-text
</span></span></code></pre></div><p>3. Split the morse code into words.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="s">"&nbsp;&nbsp;"</span> split-subseq
</span></span></code></pre></div><p>4. Parse each word’s morse code, joining and wrapping the text.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">[ morse> ] <span class="nb">map </span><span class="s">" "</span> <span class="nb">join </span><span class="m">60 </span>wrap-string <span class="nb">print
</span></span></span></code></pre></div><p>The result is:</p>
<pre tabindex="0"><code>today's decision by the fcc to encumber broadband internet
services with badly antiquated regulations is a radical
step that presages a time of uncertainty for consumers,
innovators and investors. over the past two decades
a bipartisan, light-touch policy approach unleashed
unprecedented investment and enabled the broadband internet
age consumers now enjoy. the fcc today chose to change
the way the commercial internet has operated since its
creation. changing a platform that has been so successful
should be done, if at all, only after careful policy
analysis, full transparency, and by the legislature, which
is constitutionally charged with determining policy. as a
result, it is likely that history will judge today's actions
as misguided. the fcc's move is especially regrettable
because it is wholly unnecessary. the fcc had targeted tools
available to preserve an open internet, but instead chose to
use this order as an excuse to adopt 300-plus pages of broad
and open-ended regulatory arcana that will have unintended
negative consequences for consumers and various parts of the
internet ecosystem for years to come. what has been and will
remain constant before, during and after the existence of
any regulations is verizon's commitment to an open internet
that provides consumers with competitive broadband choices
and internet access when, where, and how they want.
</code></pre>Gödel Numbering
https://re.factorcode.org/2015/01/godel-numbering.html
Fri, 16 Jan 2015 17:50:00 -0800https://re.factorcode.org/2015/01/godel-numbering.html<p>The <a href="https://programmingpraxis.com/">Programming Praxis blog</a> has a post
about <a href="https://programmingpraxis.com/2015/01/16/gdel-numbering/">Gödel
Numbering</a>
which I thought it would be fun to implement in
<a href="https://factorcode.org">Factor</a>, showing off our support for working
with <a href="https://en.wikipedia.org/wiki/Prime_number">prime numbers</a>.</p>
<p>Spoiler alert: my solution is below.</p>
<blockquote>
<p>Gödel numbering is a system that assigns a natural number to each
symbol and expression in a logical system, invented in 1931 by Kurt
Gödel for the proofs of his incompleteness theorems. Consider the
logical system where symbols are letters and expressions are words;
for instance, the word PRAXIS consists of six symbols P, R, A, X, I,
and S. Gödel numbering would assign numbers to the letters, say A=1 …
Z=26, then assign each letter as the exponent of the next prime, so
PRAXIS would be numbered 2<sup>16</sup> × 3<sup>18</sup> ×
5<sup>1</sup> × 7<sup>24</sup> × 11<sup>9</sup> × 13<sup>19</sup> =</p>
<p>83838469305478699942290773046821079668485703984645720358854000640</p>
<p>The process is reversible; factor the Gödel number and decode the
exponents.</p>
<p>Your task is to write functions that encode strings as Gödel numbers
and decode Gödel numbers as strings.</p>
</blockquote>
<p>The
<a href="https://docs.factorcode.org/content/vocab-math.primes.html">math.primes</a>
vocabulary provides words for checking if numbers are prime or not and
working with various sequences of prime numbers. The
<a href="https://docs.factorcode.org/content/vocab-math.primes.factors.html">math.primes.factors</a>
vocabulary provides ways to find the prime factors for a number. Using
these, we can simply implement our conversion words:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>gödel</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>nprimes ] <span class="nb">keep </span>[ <span class="m">64 </span><span class="nb">- </span>^ ] <span class="nb">2map product </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">gödel></span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> group-factors <span class="nb">values </span>[ <span class="m">64 </span><span class="nb">+ </span>] <span class="s">""</span> <span class="nb">map-as </span><span class="k">;
</span></span></span></code></pre></div>Gopher
https://re.factorcode.org/2014/12/gopher.html
Thu, 18 Dec 2014 08:56:00 -0800https://re.factorcode.org/2014/12/gopher.html<p>The <a href="https://en.wikipedia.org/wiki/Gopher_%28protocol%29">Gopher
protocol</a> is
relatively dated now, but when it was first released in 1991, it had a
number of modern features that we would later enjoy in the <a href="https://en.wikipedia.org/wiki/World_Wide_Web">World Wide
Web</a>. In particular, in
<a href="https://tools.ietf.org/html/rfc1436">RFC 1436</a>, it lists these features:</p>
<ul>
<li>A file-like hierarchical arrangement that would be familiar to users.</li>
<li>A simple syntax.</li>
<li>A system that can be created quickly and inexpensively.</li>
<li>Extending the file system metaphor, such as searches.</li>
</ul>
<p>We’re going to build a simple word to let us look through
<a href="https://www.wordnik.com/words/gopherspace">Gopherspace</a> using
<a href="https://factorcode.org">Factor</a>.</p>
<p>Using the <a href="https://docs.factorcode.org/content/article-urls.html">URLs</a>
vocabulary, we will build a tool to fetch documents from a Gopher server
using a URL that looks like this:</p>
<pre tabindex="0"><code>gopher://gopher.floodgap.com/0/gopher/proxy
</code></pre><p>This specifies a host, an optional port (defaulting to 70 if not
specified), and a path which includes an item type and a selector
identifying the document to obtain.</p>
<p>Once a network connection is opened, we can retrieve the specified
document by sending the selector followed by a CRLF (carriage return and
line feed, <a href="https://en.wikipedia.org/wiki/ASCII">ASCII</a> bytes 13 and 10
respectively), and then reading the response:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">get-selector</span> <span class="nf">( </span><span class="nv">selector</span> <span class="nf">-- </span><span class="nv">document</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"/"</span> split1 <span class="s">""</span> <span class="nb">or nip write </span><span class="s">"\r\n"</span> <span class="nb">write flush contents </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">gopher</span> <span class="nf">( </span><span class="nv">url</span> <span class="nf">-- </span><span class="nv">document</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> >url {
</span></span><span class="line"><span class="cl"> [ host>> ]
</span></span><span class="line"><span class="cl"> [ port>> <span class="m">70 </span><span class="nb">or </span><inet> ascii ]
</span></span><span class="line"><span class="cl"> [ path>> <span class="nb">rest </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span>'[ _ get-selector ] with-client <span class="k">;
</span></span></span></code></pre></div><p>The item type, which we are ignoring in the code above, can be used a
bit like a <a href="https://en.wikipedia.org/wiki/Filename_extension">filename
extension</a> to handle
documents of different types in particular ways. Some common types that
you might see:</p>
<ul>
<li>0 - plain text</li>
<li>1 - menus</li>
<li>9 - binary</li>
<li>s - sound</li>
<li>g - GIF images</li>
</ul>
<p>Right now, our code assumes that all the documents we will fetch are
<a href="https://en.wikipedia.org/wiki/ASCII">ASCII</a>, and it doesn’t have any
special handling for menus, or support for a query string that would
allow using Gopher “search servers”. I added some basic support for
those items in the new <a href="https://github.com/factor/factor/blob/master/extra/gopher/gopher.factor">gopher
vocabulary</a>
that I committed yesterday. In addition, I built a simple Gopher browser
complete with history support and ability to view GIF images in the
<a href="https://github.com/factor/factor/blob/master/extra/gopher/ui/ui.factor">gopher.ui
vocabulary</a>.</p>
<p>Here’s how you would use it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">gopher.ui</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"gopher://gopher.floodgap.com/1"</span>
</span></span><span class="line"><span class="cl"> open-gopher-window
</span></span></code></pre></div><p>That will pop up a window that looks like this, with clickable links and
everything:</p>
<p>
<img src="https://re.factorcode.org/images/2014-12-18-gopher-gopher.png" alt="" width="390" height="400" />
</p>
<p>It’s neat using some of these early protocols, both because they tend to
be simpler, but especially when you see that they have a passionate
following. As of December 2014,
<a href="https://en.wikipedia.org/wiki/Veronica_(computer)">Veronica-2</a> has
indexed 150 gopher servers with over 3 million unique selectors. You can
see current stats by going to:</p>
<pre tabindex="0"><code>gopher://gopher.floodgap.com/0/v2/vstat
</code></pre><p>Check it out!</p>
Binary Puzzle
https://re.factorcode.org/2014/12/binary-puzzle.html
Wed, 03 Dec 2014 10:54:00 -0800https://re.factorcode.org/2014/12/binary-puzzle.html<p>I’ve enjoyed being a subscriber to <a href="https://thelistserve.com/">The
ListServe</a>, a mailing list where each day one
subscriber wins a lottery to write to the entire list of over 24,000
subscribers. There has been a lot of life advice, stories, recipes,
music and book recommendations, and even puzzles posted to the list. You
can see some of the past posts on <a href="https://thelistserveblog.blogspot.com/">The ListServe
Blog</a>.</p>
<p>In <a href="https://thelistserveblog.blogspot.com/2014/12/let-me-trade-list-for-your-time.html">today’s
post</a>,
someone includes a quick puzzle, which (semi-spoiler alert!) I wanted to
show how to solve using <a href="https://factorcode.org">Factor</a>:</p>
<blockquote>
<p><em>One preachy puzzle (easy to solve with the aid of the Internet, and
therein lies the irony): 01001000 01110101 01101101 01100001 01101110
01110011 00100000 01100001 01110010 01100101 00100000 01101101
01101111 01110010 01100101 00100000 01110100 01101000 01100001
01101110 00100000 01100100 01100001 01110100 01100001</em></p>
</blockquote>
<p>At first glance, it looks like <a href="https://en.wikipedia.org/wiki/Binary_number">binary
numbers</a> separated by spaces
in a sequence with some meaning, probably some kind of sentence,
probably in English, and probably
<a href="https://en.wikipedia.org/wiki/ASCII">ASCII</a> encoded.</p>
<p>Let’s try and solve it with that in mind:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="s">"""
</span></span></span><span class="line"><span class="cl"><span class="s">01001000 01110101 01101101 01100001 01101110 01110011
</span></span></span><span class="line"><span class="cl"><span class="s">00100000 01100001 01110010 01100101 00100000 01101101
</span></span></span><span class="line"><span class="cl"><span class="s">01101111 01110010 01100101 00100000 01110100 01101000
</span></span></span><span class="line"><span class="cl"><span class="s">01100001 01101110 00100000 01100100 01100001 01110100
</span></span></span><span class="line"><span class="cl"><span class="s">01100001
</span></span></span><span class="line"><span class="cl"><span class="s">"""</span>
</span></span><span class="line"><span class="cl">[ blank? ] split-when <span class="nb">harvest </span>[ bin> ] <span class="s">""</span> <span class="nb">map-as </span><span class="m">.
</span></span></span></code></pre></div><p>It’s a neat message, but I won’t spoil the answer for you.</p>
Heaps
https://re.factorcode.org/2014/12/heaps.html
Tue, 02 Dec 2014 15:50:00 -0800https://re.factorcode.org/2014/12/heaps.html<p>Yesterday, I
<a href="https://github.com/factor/factor/commit/3aa14d7570c2dc7a2801d739a720d1c9a1b1a319">committed</a>
a performance improvement to the
<a href="https://en.wikipedia.org/wiki/Heap_%28data_structure%29">heap</a>
implementation in <a href="https://factorcode.org">Factor</a>.</p>
<p>There’s an interesting comment on the <a href="https://bitbucket.org/pypy/pypy/src/849bdb0dca781cdea0bd4f894a751ce8e2872b6c/lib-python/2/heapq.py?at=default#cl-258">pypy implementation of the
“heapq”
module</a>
that discusses a performance optimization that takes advantage of the
fact that sub-trees of the heap satisfy the heap invariant. The strategy
is to reduce the number of comparisons that take place when sifting
items into their proper place in the heap.</p>
<p>Below, I demonstrate the time it takes to run our <a href="https://github.com/factor/factor/blob/master/extra/benchmark/heaps/heaps.factor">heaps
benchmark</a>
and to sort 1 million random numbers using
<a href="https://github.com/factor/factor/blob/master/extra/sorting/heap/heap.factor">heapsort</a>,
before and after making the change.</p>
<p>Before:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ heaps-benchmark ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.224253523 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1,000,000 </span>random-units gc [ heapsort <span class="nb">drop </span>] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">2.210408992 </span>seconds
</span></span></code></pre></div><p>After:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ heaps-benchmark ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.172660576 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1,000,000 </span>random-units gc [ heapsort <span class="nb">drop </span>] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">1.688299185 </span>seconds
</span></span></code></pre></div><p>Not a bad improvement!</p>
Prime Sextuplets
https://re.factorcode.org/2014/11/prime-sextuplets.html
Fri, 28 Nov 2014 14:40:00 -0800https://re.factorcode.org/2014/11/prime-sextuplets.html<p>A couple of days ago, the <a href="https://riecoin.org/">Riecoin project</a> (a
virtual currency and distributed computing platform) posted a <a href="https://riecoin.org/Press%20release%202014-11-26.pdf">press
release</a> announcing
they have quietly broken the record for the largest <a href="https://en.wikipedia.org/wiki/Prime_quadruplet#Prime_sextuplets">prime number
sextuplet</a>:</p>
<blockquote>
<p><em>A prime sextuplet consists of six prime numbers packed together as
tightly as possible. For sextuplets, “as tightly as possible” means
that the largest is 16 plus the smallest of the numbers.</em></p>
</blockquote>
<p>The smallest prime sextuplet is {7, 11, 13, 17, 19, 23} and generally
they take the form of a prime number <code>N</code> such that these six numbers are
all prime: {N+0, N+4, N+6, N+10, N+12, N+16}.</p>
<p>It’s kind of neat that you can use <a href="https://factorcode.org">Factor</a> to
confirm their result:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USE:</span> <span class="nn">math.primes</span>
</span></span><span class="line"><span class="cl"><span class="m">689702036532655186685581028503873005405874329363269153979622096014346785019088707220301256048568366498602811964467654774670820091972463194208186476882699386082393716593309811371422836387527549653095824492750394092045532275098135652952423078356472379653908988713872759020566218763497459878106775183203857648413997381256598543877696056491021898353604500233203798629403923570165634119564742536549584121471881689569379964364152289494693118199337926886001843460903637314310532482306798517536171711379098711480663572269535063407688377687623951196977582998449120940358830276897328119483620011984713125859631603652231485340570118364685553782567043880668996080767
</span></span></span><span class="line"><span class="cl">{ <span class="m">0 4 6 10 12 16 </span>} [ <span class="nb">+ </span>] <span class="nb">with map </span>[ prime? ] <span class="nb">all? </span><span class="m">.
</span></span></span></code></pre></div><p>Factor uses an implementation of the probabilistic <a href="https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test">Miller-Rabin
primality
test</a>
in the
<a href="https://docs.factorcode.org/content/vocab-math.primes.miller-rabin.html">math.primes.miller-rabin</a>
vocabulary, which on my laptop takes just over 3 seconds.</p>
Factor Tutorial
https://re.factorcode.org/2014/11/factor-tutorial.html
Sat, 22 Nov 2014 08:13:00 -0800https://re.factorcode.org/2014/11/factor-tutorial.html<p><a href="https://github.com/andreaferretti">Andrea Ferretti</a> has posted a <a href="https://andreaferretti.github.io/factor-tutorial">great
tutorial about Factor</a>!</p>
<p>From the
<a href="https://www.mail-archive.com/[email protected]/msg06959.html">announcement</a>
on the mailing list:</p>
<pre tabindex="0"><code>Factor has a lot of documentation in the listener, but I have
tried to cover some topics that are present in the official
docs, but scattered throughout it, so that they were not clear
to me at the beginning.
These include for instance:
- the central concept is function composition, the stack is more
of a detail
- how simple is to deploy program and scripts
- what tools are there: linter, inspector, unit testing support,
reverse lookup of function uses...
- what model of multithreading and async I/O are used
- how to make use of multiple cores
- in what sense Factor has an object system
and more
</code></pre><p>Check it out!</p>
Factor 0.97 now available
https://re.factorcode.org/2014/11/factor-0-97-now-available.html
Sun, 02 Nov 2014 16:02:00 -0800https://re.factorcode.org/2014/11/factor-0-97-now-available.html<p><em>“If birds can glide for long periods of time, then… why can’t I?” -
Orville Wright</em></p>
<p>I’m very pleased to announce the release of
<a href="https://factorcode.org">Factor</a> 0.97!</p>
<table class="downloads" cellspacing="0">
<colgroup>
<col style="width: 25%" />
<col style="width: 25%" />
<col style="width: 25%" />
<col style="width: 25%" />
</colgroup>
<thead>
<tr class="header">
<th class="nobg" style="text-align: center;">OS/CPU</th>
<th class="bg" style="text-align: center;" scope="col">Windows</th>
<th class="bg" style="text-align: center;" scope="col">Mac OS</th>
<th class="bg" style="text-align: center;" scope="col">Linux</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<th class="bg" style="text-align: center;" scope="row">x86</th>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.97/factor-windows-x86-32-0.97.zip">0.97</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.97/factor-macosx-x86-32-0.97.dmg">0.97</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.97/factor-linux-x86-32-0.97.tar.gz">0.97</a>
</td>
</tr>
<tr class="even">
<th class="bg" style="text-align: center;" scope="row">x86-64</th>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.97/factor-windows-x86-64-0.97.zip">0.97</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.97/factor-macosx-x86-64-0.97.dmg">0.97</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.97/factor-linux-x86-64-0.97.tar.gz">0.97</a>
</td>
</tr>
</tbody>
</table>
<p><strong>Source code</strong>: <a
href="https://downloads.factorcode.org/releases/0.97/factor-src-0.97.zip"
class="release">0.97</a></p>
<p>This release is brought to you with over 1,400 commits by the following
individuals:</p>
<blockquote>
<p>Alex Vondrak, Andrew Pennebaker, Benjamin Pollack, Björn Lindqvist, CW
Alston, Doug Coleman, Erik Charlebois, Fred Alger, Iskander Sitdikov,
John Benediktsson, Jon Harper, Loryn Jenkins, Paul Woolcock, Roc King,
Samuel Tardieu, Steven Stewart-Gallus, and
<a href="https://github.com/Profpatsch">@Profpatsch</a></p>
</blockquote>
<p>Besides some bug fixes and library improvements, I want to highlight the
following changes:</p>
<ul>
<li>Support for more <a href="https://re.factorcode.org/2013/10/color-support.html">color
spaces</a>
including LAB, LCH, LUV, RYB, xyY, XYZ, and YUV</li>
<li>Support for SSL on windows</li>
<li>Some new <a href="https://re.factorcode.org/2014/04/checksum-improvements.html">checksum implementations and performance
improvements</a></li>
<li>Cross-platform C library finder</li>
<li>Client for IMAP protocol</li>
<li>Support for <a href="https://re.factorcode.org/2013/11/messagepack.html">MessagePack encoding and
decoding</a></li>
<li>Support for <a href="https://re.factorcode.org/2013/12/uu-encoding.html">uuencoding and
uudecoding</a></li>
<li>Support for <a href="https://re.factorcode.org/2013/04/terminfo.html">terminfo database
files</a></li>
<li>Support for <a href="https://re.factorcode.org/2013/11/tzfile.html">tzinfo
files</a></li>
<li>Support for in-place sorting (using
<a href="https://re.factorcode.org/2014/06/quicksort.html">Quicksort</a>)</li>
<li>Bindings for
<a href="https://re.factorcode.org/2013/09/zeromq.html">ZeroMQ</a> and
Python</li>
<li>Runner for
<a href="https://re.factorcode.org/2013/10/applescript.html">Applescript</a>
(including definition support)</li>
<li>Performance improvement to <code>printf</code> and support for <code>vprintf</code></li>
<li>Support saving images in GTK, Cocoa, and Windows backends</li>
<li>Generative Modeling Language</li>
<li>Lots of documentation added to the compiler</li>
<li>Many, many improvements to the FUEL emacs mode</li>
</ul>
<p>Some possible backwards compatibility issues:</p>
<ul>
<li>Fixed <code>mask?</code> in
<a href="https://docs.factorcode.org/content/vocab-math.bitwise.html">math.bitwise</a>
to be more correct</li>
<li>Fixed bias in Mersenne Twister random number generator</li>
<li>Better support for shebang (no longer need a space after <code>#!</code>)</li>
<li><code>io-error</code> now lives in the
<a href="https://docs.factorcode.org/content/vocab-libc.html">libc</a>
vocabulary</li>
<li>sender stubs in
<a href="https://docs.factorcode.org/content/vocab-cocoa.messages.html">cocoa.messages</a>
now named by method signature</li>
<li><code>filter</code> now allocates length of <code>seq</code>, not <code>exemplar</code>.</li>
<li>Removed <code>make-assoc</code> in favor of explicit <code>get</code>’s.</li>
</ul>
<p>Some of the improvements to FUEL, Factor’s emacs mode:</p>
<ul>
<li>Modernize for emacs 24.3</li>
<li>Prepare FUEL to be uploaded to MELPA</li>
<li>Change font locking and syntax highlighting</li>
<li>Make fuel-help work for vocabularies also</li>
<li>New minor mode: fuel-autohelp-mode</li>
<li>Fix word help to use correct vocabulary using list</li>
<li>Variable controlling whether fuel-mode is loaded automatically</li>
<li>Fixes to table rendering</li>
</ul>
<h3 id="what-is-factor">What is Factor</h3>
<p>Factor is a <a href="https://www.concatenative.org/">concatenative</a>, stack-based
programming language with <a href="https://concatenative.org/wiki/view/Factor/Features/The%20language">high-level
features</a>
including dynamic types, extensible syntax, macros, and garbage
collection. On a practical side, Factor has a <a href="https://docs.factorcode.org/content/article-vocab-index.html">full-featured
library</a>,
supports many different platforms, and has been extensively documented.</p>
<p>The implementation is <a href="https://concatenative.org/wiki/view/Factor/Optimizing%20compiler">fully
compiled</a>
for performance, while still supporting <a href="https://concatenative.org/wiki/view/Factor/Interactive%20development">interactive
development</a>.
Factor applications are portable between all common platforms. Factor
can <a href="https://concatenative.org/wiki/view/Factor/Deployment">deploy stand-alone
applications</a> on
all platforms. Full source code for the Factor project is available
under a BSD license.</p>
<h3 id="new-libraries">New libraries:</h3>
<ul>
<li><a href="https://docs.factorcode.org/content/vocab-alien.libraries.finder.html">alien.libraries.finder</a>:
cross-platform C library finder</li>
<li><a href="https://docs.factorcode.org/content/vocab-checksums.fletcher.html">checksums.fletcher</a>:
Fletcher in 16/32/64-bit</li>
<li><a href="https://docs.factorcode.org/content/vocab-checksums.murmur.html">checksums.murmur</a>:
MurmurHash3 in 32-bit</li>
<li><a href="https://docs.factorcode.org/content/vocab-checksums.superfast.html">checksums.superfast</a>:
SuperFastHash</li>
<li><a href="https://docs.factorcode.org/content/vocab-checksums.xxhash.html">checksums.xxhash</a>:
xxHash in 32-bit</li>
<li><a href="https://docs.factorcode.org/content/vocab-cocoa.apple-script.html">cocoa.apple-script</a>:
runner for AppleScript</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.distances.html">colors.distances</a>:
CIE76, CIE94, CIEDE2000, and CMC l:c color differences</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.lab.html">colors.lab</a>:
support for CIE 1976 LAB colors</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.lch.html">colors.lch</a>:
support for CIELCH colors</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.luv.html">colors.luv</a>:
support for CIE 1976 LUV colors</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.ryb.html">colors.ryb</a>:
support for RYB colors</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.xyy.html">colors.xyy</a>:
support for CIE 1931 xyY colors</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.xyz.html">colors.xyz</a>:
support for CIE 1931 XYZ colors</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.yuv.html">colors.yuv</a>:
support for YUV colors</li>
<li><a href="https://docs.factorcode.org/content/vocab-compression.snappy.html">compression.snappy</a>:
bindings to snappy compression library</li>
<li><a href="https://docs.factorcode.org/content/vocab-crypto.aes.html">crypto.aes</a>:
lookup-table AES implementation</li>
<li><a href="https://docs.factorcode.org/content/vocab-cuesheet.html">cuesheet</a>:
parser for CUE files</li>
<li><a href="https://docs.factorcode.org/content/vocab-curl.html">curl</a>: bindings
for libcurl</li>
<li><a href="https://docs.factorcode.org/content/vocab-editors.atom.html">editors.atom</a>:
support for Atom text editor</li>
<li><a href="https://docs.factorcode.org/content/vocab-fftw.html">fftw</a>: bindings
to FFTW library</li>
<li><a href="https://docs.factorcode.org/content/vocab-gml.html">gml</a>: support
for Generative Modeling Language</li>
<li><a href="https://docs.factorcode.org/content/vocab-grouping.extras.html">grouping.extras</a>:
helpful words</li>
<li><a href="https://docs.factorcode.org/content/vocab-html.entities.html">html.entities</a>:
escape and unescape HTML5 entities</li>
<li><a href="https://docs.factorcode.org/content/vocab-imap.html">imap</a>: client
for IMAP servers</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.encodings.utf7.html">io.encodings.utf7</a>:
support for UTF7 encodings</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.ansi.html">io.streams.ansi</a>:
support for ANSI color streams</li>
<li><a href="https://docs.factorcode.org/content/vocab-magic.html">magic</a>:
bindings to libmagic</li>
<li><a href="https://docs.factorcode.org/content/vocab-marvel.html">marvel</a>:
support for Marvel.com API</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.combinators.html">math.combinators</a>:
some convenience words</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.matrices.laplace.html">matrices.laplace</a>:
adding Laplace expansion</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.unicode.html">math.unicode</a>:
implement unicode math symbols</li>
<li><a href="https://docs.factorcode.org/content/vocab-pcre.html">pcre</a>: bindings
for PCRE</li>
<li><a href="https://docs.factorcode.org/content/vocab-python.html">python</a>:
bindings for Python</li>
<li><a href="https://docs.factorcode.org/content/vocab-random.rdrand.html">random.rdrand</a>:
random number generator using <code>RDRAND</code> instruction</li>
<li><a href="https://docs.factorcode.org/content/vocab-rosetta-code.josephus-problem.html">rosetta-code.josephus-problem</a>:
solved</li>
<li><a href="https://docs.factorcode.org/content/vocab-rosetta-code.metronome.html">rosetta-code.metronome</a>:
solved</li>
<li><a href="https://docs.factorcode.org/content/vocab-sorting.heap.html">sorting.heap</a>:
implement Heapsort</li>
<li><a href="https://docs.factorcode.org/content/vocab-sorting.quick.html">sorting.quick</a>:
implement Quicksort</li>
<li><a href="https://docs.factorcode.org/content/vocab-spotlight.html">spotlight</a>:
adding Spotlight search wrapper for Mac OS</li>
<li><a href="https://docs.factorcode.org/content/vocab-terminfo.html">terminfo</a>:
parser for terminfo database files</li>
<li><a href="https://docs.factorcode.org/content/vocab-tzinfo.html">tzinfo</a>:
parser for timezone files</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.dns.public.html">tools.dns.public</a>:
adding well-known public DNS servers</li>
<li><a href="https://docs.factorcode.org/content/vocab-uu.html">uu</a>: support for
uuencoding and uudecoding</li>
<li><a href="https://docs.factorcode.org/content/vocab-yaml.html">yaml</a>: reading
and writing the YAML format</li>
<li><a href="https://docs.factorcode.org/content/vocab-youtube.html">youtube</a>:
downloader for youtube.com</li>
<li><a href="https://docs.factorcode.org/content/vocab-wikipedia.html">wikipedia</a>:
support historical events and article printing</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.ntdll.html">windows.ntdll</a>:
process listing on Windows</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.winmm.html">windows.winmm</a>:
to play MP3 files</li>
<li><a href="https://docs.factorcode.org/content/vocab-zeromq.html">zeromq</a>:
bindings for ZeroMQ</li>
</ul>
<h3 id="improved-libraries">Improved libraries:</h3>
<ul>
<li><a href="https://docs.factorcode.org/content/vocab-arrays.shaped.html">arrays.shaped</a>:
iteration with indices, mapping operations</li>
<li><a href="https://docs.factorcode.org/content/vocab-bson.reader.html">bson.reader</a>:
support reading sequence of BSON objects from a file</li>
<li><a href="https://docs.factorcode.org/content/vocab-calendar.format.html">calendar.format</a>:
fix timestamp parsing without a timezone (fixes #861)</li>
<li><a href="https://docs.factorcode.org/content/vocab-calendar.threads.html">calendar.threads</a>:
support <code>M\ timestamp sleep-until</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-circular.html">circular</a>:
adding <code>circular-loop</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-color-picker.html">color-picker</a>:
show hex value of color</li>
<li><a href="https://docs.factorcode.org/content/vocab-combinators.extras.html">combinators.extras</a>:
more words</li>
<li><a href="https://docs.factorcode.org/content/vocab-concurrency.combinators.html">concurrency.combinators</a>:
adding parallel versions of cartesian-{map,each}</li>
<li><a href="https://docs.factorcode.org/content/vocab-destructors.html">destructors</a>:
faster <code>dispose-each</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-dlists.html">dlists</a>:
adding <code>push-before</code> and <code>push-sorted</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-dns.html">dns</a>: support
custom DNS server lookups</li>
<li><a href="https://docs.factorcode.org/content/vocab-environment.html">environment</a>:
adding <code>with-os-env</code> for using temporary environment variables</li>
<li><a href="https://docs.factorcode.org/content/vocab-formatting.html">formatting</a>:
performance improvements, added <code>vprintf</code> for runtime formatting</li>
<li><a href="https://docs.factorcode.org/content/vocab-google.translate.html">google.translate</a>:
adding <code>translate-tts</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-graphviz.html">graphviz</a>:
miscellaneous improvements by @ajvondrak</li>
<li><a href="https://docs.factorcode.org/content/vocab-grouping.extras.html">grouping.extras</a>:
more words</li>
<li><a href="https://docs.factorcode.org/content/vocab-hash-sets.html">hash-sets</a>:
minor cleanup</li>
<li><a href="https://docs.factorcode.org/content/vocab-hashtables.html">hashtables</a>:
minor cleanup</li>
<li><a href="https://docs.factorcode.org/content/vocab-help.html.html">help.html</a>:
use tools.completions for searching</li>
<li><a href="https://docs.factorcode.org/content/vocab-html.parser.analyzer.html">html.parser.analyzer</a>:
make <code>find-between*</code> work on nested tags</li>
<li><a href="https://docs.factorcode.org/content/vocab-html.parser.printer.html">html.parser.printer</a>:
better <code>html-text.</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-http.client.html">http.client</a>:
versions of <code>http-get</code> and friends that don’t check response code</li>
<li><a href="https://docs.factorcode.org/content/vocab-infix.html">infix</a>: adding
<code>INFIX::</code> word definitions</li>
<li><a href="https://docs.factorcode.org/content/vocab-interpolate.html">interpolate</a>:
adding generalized numbered string interpolation</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.binary.html">io.binary</a>:
faster <code>le></code> and <code>signed-be></code> and <code>signed></code></li>
<li><a href="https://docs.factorcode.org/content/vocab-io.binary.fast.html">io.binary.fast</a>:
generic <code>be></code> and <code>le></code> that try to be fast</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.directories.html">io.directories</a>:
adding <code>with-resource-directory</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-io.encodings.string.html">io.encodings.string</a>:
faster <code>decode</code> and <code>encode</code> for <code>ascii</code> and <code>utf8</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-io.sockets.secure.openssl.html">io.sockets.secure.openssl</a>:
fix subject name checks</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.byte-array.html">io.streams.byte-array</a>:
performance improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.random.html">io.streams.random</a>:
performance improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.string.html">io.streams.string</a>:
performance improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-ip-parser.html">ip-parser</a>:
adding <code>ntoa</code> and <code>aton</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.bitwise.html">math.bitwise</a>:
adding <code>bit-length</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.combinatorics.html">math.combinatorics</a>:
performance improvements, <code>k-permutations</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.extras.html">math.extras</a>:
more words</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.factorials.html">math.factorials</a>:
adding <code>factorials</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.functions.html">math.functions</a>:
implement <code>ldexp</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-math.matrices.html">math.matrices</a>:
performance improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-ranges.html">ranges</a>:
implement <code>sum</code> on <code>range</code> objects</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.statistics.html">math.statistics</a>:
fix out-of-bounds in <code>quantiles</code>, more words</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.text.english.html">math.text.english</a>:
support ratio, float, complex</li>
<li><a href="https://docs.factorcode.org/content/vocab-mirrors.html">mirrors</a>:
strings can be <code>inspected-sequence</code> (fixes #857)</li>
<li><a href="https://docs.factorcode.org/content/vocab-openal.examples.html">openal.examples</a>:
adding <code>play-waveform</code> and <code>play-sine</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-pack.html">pack</a>: support
numbered format strings (e.g., <code>"4ci"</code>)</li>
<li><a href="https://docs.factorcode.org/content/vocab-random.html">random</a>:
performance improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-random.mersenne-twister.html">random.mersenne-twister</a>:
bug fixes</li>
<li><a href="https://docs.factorcode.org/content/vocab-redis.html">redis</a>: adding
hash commands, upgraded to modern protocol</li>
<li><a href="https://docs.factorcode.org/content/vocab-semantic-versioning.html">semantic-versioning</a>:
support for Semantic Versioning 2.0.0</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.html">sequences</a>:
implement <code>sum</code> on <code>iota-tuple</code> objects</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.extras.html">sequences.extras</a>:
more words</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.product.html">sequences.product</a>:
performance improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.windowed.html">sequences.windowed</a>:
performance improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-sets.extras.html">sets.extras</a>:
more words</li>
<li><a href="https://docs.factorcode.org/content/vocab-sorting.insertion.html">sorting.insertion</a>:
performance improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-spelling.html">spelling</a>:
minor fixes and performance improvements</li>
<li><a href="https://docs.factorcode.org/content/vocab-spider.html">spider</a>:
ignore non-html files</li>
<li><a href="https://docs.factorcode.org/content/vocab-system-info.html">system-info</a>:
implement <code>system-report.</code> on Linux.</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.deploy.config.html">tools.deploy.config</a>:
specific <code>deploy-directory</code></li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.dns.html">tools.dns</a>:
adding <code>dns-host</code> to lookup from a specific host</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.test.html">tools.test</a>:
allow testing from command-line</li>
<li><a href="https://docs.factorcode.org/content/vocab-twitter.html">twitter</a>:
updates</li>
<li><a href="https://docs.factorcode.org/content/vocab-units.imperial.html">units.imperial</a>:
more units</li>
<li><a href="https://docs.factorcode.org/content/vocab-unix.users.html">unix.users</a>:
add utility word to find a user’s home directory</li>
<li><a href="https://docs.factorcode.org/content/vocab-unix.utmpx.html">unix.utmpx</a>:
support on Linux and fixes on Mac OS</li>
<li><a href="https://docs.factorcode.org/content/vocab-webbrowser.html">webbrowser</a>:
support <code>open-file</code> on <code>pathname</code> objects</li>
</ul>
cURL
https://re.factorcode.org/2014/10/curl.html
Thu, 23 Oct 2014 11:52:00 -0700https://re.factorcode.org/2014/10/curl.html<p>The <a href="https://curl.haxx.se/">cURL</a> project is a command-line tool and
library for transferring data using URL syntax supporting many (many!)
protocols. I recently contributed a simple wrapper for libcurl to
<a href="https://factorcode.org">Factor</a> and wanted to show a little bit about
how it was made.</p>
<p>We have a
<a href="https://docs.factorcode.org/content/word-download-to,http.client.html">download-to</a>
word that uses our HTTP client to download resources from the web. I
wanted to show how to build a similar word to download resources using
libcurl.</p>
<h3 id="ffi">FFI</h3>
<p>We will use the
<a href="https://docs.factorcode.org/content/article-alien.html">alien</a>
vocabulary to interface with the libcurl C library, defining words to
initialize, perform a request, and cleanup</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">TYPEDEF:</span> <span class="nf">void</span> <span class="nf">CURL</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">FUNCTION: CURL* curl_easy_init <span class="nf">( )
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> int <span class="nf">curl_easy_perform</span> ( CURL* curl )
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> void <span class="nf">curl_easy_cleanup</span> ( CURL* curl )
</span></span></code></pre></div><p>Before we perform the request, we will want to set various options to
control what request is made, using function aliases to allow passing
different types of values based on the numeric key:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">FUNCTION-ALIAS:</span> <span class="nf">curl_easy_setopt_long</span>
</span></span><span class="line"><span class="cl">int <span class="nf">curl_easy_setopt</span> ( CURL* curl, int option, long value )
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION-ALIAS:</span> <span class="nf">curl_easy_setopt_string</span>
</span></span><span class="line"><span class="cl">int <span class="nf">curl_easy_setopt</span> ( CURL* curl, int option, c-string value )
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION-ALIAS:</span> <span class="nf">curl_easy_setopt_pointer</span>
</span></span><span class="line"><span class="cl">int <span class="nf">curl_easy_setopt</span> ( CURL* curl, int option, void* value )
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">TYPEDEF:</span> <span class="nf">int64_t</span> <span class="nf">curl_off_t</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION-ALIAS:</span> <span class="nf">curl_easy_setopt_curl_off_t</span>
</span></span><span class="line"><span class="cl">int <span class="nf">curl_easy_setopt</span> ( CURL* curl, int option, curl_off_t value )
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">curl_easy_setopt</span> <span class="nf">( </span><span class="nv">curl</span> <span class="nv">option</span> <span class="nv">value</span> <span class="nf">-- </span><span class="nv">code</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">over </span>enum>number {
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">30000 </span><span class="nb">> </span>] [ <span class="nb">drop </span>curl_easy_setopt_curl_off_t ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">20000 </span><span class="nb">> </span>] [ <span class="nb">drop </span>curl_easy_setopt_pointer ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">10000 </span><span class="nb">> </span>] [ <span class="nb">drop </span>curl_easy_setopt_string ] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>curl_easy_setopt_long ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="k">;
</span></span></span></code></pre></div><h3 id="factor">Factor</h3>
<p>We can then begin to use libcurl in a few simple Factor words that allow
us to present a nice interface to the user. Starting with initializing
the library, and registering a destructor the cleanup after we are done:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">DESTRUCTOR: curl_easy_cleanup
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">curl-init</span> <span class="nf">( -- </span><span class="nv">CURL</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> curl_easy_init &curl_easy_cleanup <span class="k">;
</span></span></span></code></pre></div><p>Some of the functions produce an error code that we should check.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">CURLE_OK</span> <span class="m">0
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">check-code</span> <span class="nf">( </span><span class="nv">code</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> CURLE_OK <span class="nb">assert= </span><span class="k">;
</span></span></span></code></pre></div><p>We can set options using the <code>curl_easy_setopt</code> words we defined
earlier:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">curl-set-opt</span> <span class="nf">( </span><span class="nv">CURL</span> <span class="nv">key</span> <span class="nv">value</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> curl_easy_setopt check-code <span class="k">;
</span></span></span></code></pre></div><p>Using these we can set file (opening and registering a destructor to
close) and URL options:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">CURLOPT_FILE</span> <span class="m">10001
</span></span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">CURLOPT_URL</span> <span class="m">10002
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">DESTRUCTOR: fclose
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">curl-set-file</span> <span class="nf">( </span><span class="nv">CURL</span> <span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> CURLOPT_FILE <span class="nb">swap </span><span class="s">"wb"</span> fopen &fclose curl-set-opt <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">curl-set-url</span> <span class="nf">( </span><span class="nv">CURL</span> <span class="nv">url</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> CURLOPT_URL <span class="nb">swap </span>present curl-set-opt <span class="k">;
</span></span></span></code></pre></div><p>And a word to perform the “curl”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">curl-perform</span> <span class="nf">( </span><span class="nv">CURL</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> curl_easy_perform check-code <span class="k">;
</span></span></span></code></pre></div><p>Putting all of that together, we can finally download a URL to a
specified local file path:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">curl-download-to</span> <span class="nf">( </span><span class="nv">url</span> <span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> curl-init
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap </span>curl-set-file ]
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap </span>curl-set-url ]
</span></span><span class="line"><span class="cl"> [ curl-perform ] <span class="nb">tri
</span></span></span><span class="line"><span class="cl"> ] with-destructors <span class="k">;
</span></span></span></code></pre></div><p>Using it is pretty simple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"https://factorcode.org"</span> <span class="s">"/tmp/factor.html"</span>
</span></span><span class="line"><span class="cl"> curl-download-to
</span></span></code></pre></div>Quicksort
https://re.factorcode.org/2014/06/quicksort.html
Wed, 25 Jun 2014 09:59:00 -0700https://re.factorcode.org/2014/06/quicksort.html<p><a href="https://en.wikipedia.org/wiki/Sorting_algorithm">Sorting algorithms</a>
are a frequent element to computer science education, conversation
amongst programmers, and job interviews. There are many different
versions with varying tradeoffs of performance and technique.</p>
<p>I noticed that <a href="https://rosettacode.org">Rosetta Code</a> has a page on
<a href="https://rosettacode.org/wiki/Sorting_algorithms/Quicksort">Quicksort
implementations</a>.
I thought it might make a nice example of translating pseudocode to
<a href="https://factorcode.org">Factor</a>.</p>
<h3 id="simple-quicksort">simple quicksort</h3>
<p>The “simple quicksort algorithm” has the following pseudocode:</p>
<pre tabindex="0"><code>function quicksort(array)
less, equal, greater := three empty arrays
if length(array) > 1
pivot := select any element of array
for each x in array
if x < pivot then add x to less
if x = pivot then add x to equal
if x > pivot then add x to greater
quicksort(less)
quicksort(greater)
array := concatenate(less, equal, greater)
</code></pre><p>We can copy it verbatim using the ability to have named <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">quicksort</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">sorted-seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> seq <span class="nb">length </span><span class="m">1 </span><span class="nb">> </span>[
</span></span><span class="line"><span class="cl"> V{ } <span class="nb">clone </span>:> less
</span></span><span class="line"><span class="cl"> V{ } <span class="nb">clone </span>:> equal
</span></span><span class="line"><span class="cl"> V{ } <span class="nb">clone </span>:> greater
</span></span><span class="line"><span class="cl"> seq <span class="nb">first </span>:> pivot
</span></span><span class="line"><span class="cl"> seq [| x |
</span></span><span class="line"><span class="cl"> x pivot <=> {
</span></span><span class="line"><span class="cl"> { +lt+ [ x less <span class="nb">push </span>] }
</span></span><span class="line"><span class="cl"> { +eq+ [ x equal <span class="nb">push </span>] }
</span></span><span class="line"><span class="cl"> { +gt+ [ x greater <span class="nb">push </span>] }
</span></span><span class="line"><span class="cl"> } <span class="nb">case
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> less quicksort equal greater quicksort <span class="nb">3append
</span></span></span><span class="line"><span class="cl"> ] [ seq ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Even though local variables can be convenient, we discourage using them
if library words or simpler concepts can express the same logic.
Noticing that this partitions the sequence, and then joins the parts, we
can make it a bit shorter using some available library words:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">quicksort</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">sorted-seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup empty? </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">unclip </span>[
</span></span><span class="line"><span class="cl"> '[ _ before? ] <span class="nb">partition </span>[ quicksort ] <span class="nb">bi@
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">keep prefix append
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">unless </span><span class="k">;
</span></span></span></code></pre></div><p>Neither of these is particularly fast, since they involve the creation
of a lot of temporary sequences. There is a better (meaning faster and
not really more complex) version available.</p>
<h3 id="better-quicksort">better quicksort</h3>
<p>The “better quicksort algorithm” is an in-place version that uses swaps
to move items into a sorted order. It has the following pseudocode:</p>
<pre tabindex="0"><code>function quicksort(array)
if length(array) > 1
pivot := select any element of array
left := first index of array
right := last index of array
while left ≤ right
while array[left] < pivot
left := left + 1
while array[right] > pivot
right := right - 1
if left ≤ right
swap array[left] with array[right]
left := left + 1
right := right - 1
quicksort(array from first index to right)
quicksort(array from left to last index)
</code></pre><p>We can take a similar translation approach to the first example (using
some unsafe words to avoid bounds-checking and mutable local variables)
to create this version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">(quicksort)</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">from</span> <span class="nv">to</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> from to <span class="nb">< </span>[
</span></span><span class="line"><span class="cl"> from to <span class="nb">+ 2/ </span>seq nth-unsafe :> pivot
</span></span><span class="line"><span class="cl"> from :> left!
</span></span><span class="line"><span class="cl"> to :> right!
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ left right <span class="nb"><= </span>] [
</span></span><span class="line"><span class="cl"> [ left seq nth-unsafe pivot before? ]
</span></span><span class="line"><span class="cl"> [ left <span class="m">1 </span><span class="nb">+ </span>left! ] <span class="nb">while
</span></span></span><span class="line"><span class="cl"> [ right seq nth-unsafe pivot after? ]
</span></span><span class="line"><span class="cl"> [ right <span class="m">1 </span><span class="nb">- </span>right! ] <span class="nb">while
</span></span></span><span class="line"><span class="cl"> left right <span class="nb"><= </span>[
</span></span><span class="line"><span class="cl"> left right seq exchange-unsafe
</span></span><span class="line"><span class="cl"> left <span class="m">1 </span><span class="nb">+ </span>left!
</span></span><span class="line"><span class="cl"> right <span class="m">1 </span><span class="nb">- </span>right!
</span></span><span class="line"><span class="cl"> ] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> seq from right (quicksort)
</span></span><span class="line"><span class="cl"> seq left to (quicksort)
</span></span><span class="line"><span class="cl"> ] <span class="nb">when </span><span class="k">; inline recursive
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">quicksort</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span><span class="nb">over length </span><span class="m">1 </span><span class="nb">- </span>(quicksort) <span class="k">;
</span></span></span></code></pre></div><p>This is faster, although about 3x slower than our current <a href="https://docs.factorcode.org/content/article-sequences-sorting.html">merge
sort</a>
algorithm. There are probably ways we could make it faster (one I
noticed and <a href="https://github.com/factor/factor/issues/1070">filed an issue to
track</a> that also
makes merge sort faster).</p>
<p>I have committed a version of this in the
<a href="https://github.com/factor/factor/blob/master/extra/sorting/quick/quick.factor">sorting.quick</a>
vocabulary that I hope to use for faster in-place sorting in the
standard library.</p>
World Cup
https://re.factorcode.org/2014/06/world-cup.html
Sun, 22 Jun 2014 07:53:00 -0700https://re.factorcode.org/2014/06/world-cup.html<p>Many people are watching the <a href="https://www.fifa.com/worldcup/index.html">FIFA World Cup
2014</a> that is going on right
now in Brazil. A few days ago, someone posted a gist for following the
<a href="https://gist.github.com/fmasanori/1288160dad16cc473a53">World Cup in six lines of Python
3</a>. Several
people tried to improve it, down to four lines, then down to one or two
lines of code.</p>
<p>Without worrying too much about lines of code, here is something similar
in <a href="https://factorcode.org">Factor</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">worldcup.</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://worldcup.sfg.io/matches"</span> http-get <span class="nb">nip </span>json
</span></span><span class="line"><span class="cl"> [ <span class="s">"status"</span> <span class="nb">of </span><span class="s">"completed"</span> <span class="nb">= </span>] <span class="nb">filter
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ <span class="s">"home_team"</span> <span class="nb">of </span>] [ <span class="s">"away_team"</span> <span class="nb">of </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> [ [ <span class="s">"country"</span> <span class="nb">of </span>] [ <span class="s">"goals"</span> <span class="nb">of </span>] <span class="nb">bi </span>] <span class="nb">bi@
</span></span></span><span class="line"><span class="cl"> <span class="s">"%s %s x %s %s\n"</span> printf
</span></span><span class="line"><span class="cl"> ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>And if you run it, you’ll get something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> worldcup.
</span></span><span class="line"><span class="cl">Brazil <span class="m">3 </span>x Croatia <span class="m">1
</span></span></span><span class="line"><span class="cl">Mexico <span class="m">1 </span>x Cameroon <span class="m">0
</span></span></span><span class="line"><span class="cl">Spain <span class="m">1 </span>x Netherlands <span class="m">5
</span></span></span><span class="line"><span class="cl">Chile <span class="m">3 </span>x Australia <span class="m">1
</span></span></span><span class="line"><span class="cl">Colombia <span class="m">3 </span>x Greece <span class="m">0
</span></span></span><span class="line"><span class="cl">Ivory Coast <span class="m">2 </span>x Japan <span class="m">1
</span></span></span><span class="line"><span class="cl">Uruguay <span class="m">1 </span>x Costa Rica <span class="m">3
</span></span></span><span class="line"><span class="cl">England <span class="m">1 </span>x Italy <span class="m">2
</span></span></span><span class="line"><span class="cl">Switzerland <span class="m">2 </span>x Ecuador <span class="m">1
</span></span></span><span class="line"><span class="cl">France <span class="m">3 </span>x Honduras <span class="m">0
</span></span></span><span class="line"><span class="cl">Argentina <span class="m">2 </span>x Bosnia <span class="nb">and </span>Herzegovina <span class="m">1
</span></span></span><span class="line"><span class="cl">Iran <span class="m">0 </span>x Nigeria <span class="m">0
</span></span></span><span class="line"><span class="cl">Germany <span class="m">4 </span>x Portugal <span class="m">0
</span></span></span><span class="line"><span class="cl">Ghana <span class="m">1 </span>x USA <span class="m">2
</span></span></span><span class="line"><span class="cl">Belgium <span class="m">2 </span>x Algeria <span class="m">1
</span></span></span><span class="line"><span class="cl">Russia <span class="m">1 </span>x Korea Republic <span class="m">1
</span></span></span><span class="line"><span class="cl">Brazil <span class="m">0 </span>x Mexico <span class="m">0
</span></span></span><span class="line"><span class="cl">Cameroon <span class="m">0 </span>x Croatia <span class="m">4
</span></span></span><span class="line"><span class="cl">Spain <span class="m">0 </span>x Chile <span class="m">2
</span></span></span><span class="line"><span class="cl">Australia <span class="m">2 </span>x Netherlands <span class="m">3
</span></span></span><span class="line"><span class="cl">Colombia <span class="m">2 </span>x Ivory Coast <span class="m">1
</span></span></span><span class="line"><span class="cl">Japan <span class="m">0 </span>x Greece <span class="m">0
</span></span></span><span class="line"><span class="cl">Uruguay <span class="m">2 </span>x England <span class="m">1
</span></span></span><span class="line"><span class="cl">Italy <span class="m">0 </span>x Costa Rica <span class="m">1
</span></span></span><span class="line"><span class="cl">Switzerland <span class="m">2 </span>x France <span class="m">5
</span></span></span><span class="line"><span class="cl">Honduras <span class="m">1 </span>x Ecuador <span class="m">2
</span></span></span><span class="line"><span class="cl">Argentina <span class="m">1 </span>x Iran <span class="m">0
</span></span></span><span class="line"><span class="cl">Nigeria <span class="m">1 </span>x Bosnia <span class="nb">and </span>Herzegovina <span class="m">0
</span></span></span><span class="line"><span class="cl">Germany <span class="m">2 </span>x Ghana <span class="m">2
</span></span></span></code></pre></div><h3 id="extra-credit">Extra Credit</h3>
<p>If we wanted to engineer this a bit more, we could start adding to the
example.</p>
<p>First, we could define a tuple class to hold the result of each game.
This isn’t really necessary, but it can be nice to see all the fields
that are available, and to represent it as an object rather than just a
<a href="https://docs.factorcode.org/content/article-hashtables.html">hashtable</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">game</span> <span class="nv">home_team</span> <span class="nv">home_team_events</span> <span class="nv">home_team_tbd</span>
</span></span><span class="line"><span class="cl"><span class="nv">away_team</span> <span class="nv">away_team_events</span> <span class="nv">away_team_tbd</span> <span class="nv">winner</span> <span class="nv">match_number</span>
</span></span><span class="line"><span class="cl"><span class="nv">datetime</span> <span class="nv">location</span> <span class="nv">status</span> <span class="k">;
</span></span></span></code></pre></div><p>Then we could get all the game results as tuples, using
<a href="https://docs.factorcode.org/content/word-from-slots,classes.tuple.html">from-slots</a>
to convert from an array of hashtable of attributes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">worldcup</span> <span class="nf">( -- </span><span class="nv">games</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://worldcup.sfg.io/matches"</span> http-get <span class="nb">nip </span>json
</span></span><span class="line"><span class="cl"> [ game from-slots ] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>Next, having fun with colors, we use <a href="https://docs.factorcode.org/content/article-character-styles.html">character
styles</a>
to print the winner in bold green text.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">winner-style</span> H{
</span></span><span class="line"><span class="cl"> { foreground COLOR: MediumSeaGreen }
</span></span><span class="line"><span class="cl"> { font-style bold }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>And then, using more code than is probably necessary, we print out each
team, making sure to format the winner using the style we just defined
(using <a href="https://docs.factorcode.org/content/article-locals.html">locals</a>
for convenience):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">game.</span> <span class="nf">( </span><span class="nv">game</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [let
</span></span><span class="line"><span class="cl"> [ home_team>> ] [ away_team>> ] [ winner>> ] <span class="nb">tri
</span></span></span><span class="line"><span class="cl"> :> <span class="nf">( </span><span class="nv">home</span> <span class="nv">away</span> <span class="nv">winner</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> home <span class="s">"country"</span> <span class="nb">of dup </span>winner <span class="nb">=
</span></span></span><span class="line"><span class="cl"> [ winner-style format ] [ <span class="nb">write </span>] <span class="nb">if bl
</span></span></span><span class="line"><span class="cl"> home <span class="s">"goals"</span> <span class="nb">of </span>number>string <span class="nb">write
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">" x "</span> <span class="nb">write
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> away <span class="s">"country"</span> <span class="nb">of dup </span>winner <span class="nb">=
</span></span></span><span class="line"><span class="cl"> [ winner-style format ] [ <span class="nb">write </span>] <span class="nb">if bl
</span></span></span><span class="line"><span class="cl"> away <span class="s">"goals"</span> <span class="nb">of </span>number>string <span class="nb">write nl
</span></span></span><span class="line"><span class="cl"> ] <span class="k">;
</span></span></span></code></pre></div><p>We want to see the completed games, so we can make a word to filter the
list of games.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">completed-games</span> <span class="nf">( </span><span class="nv">games</span> <span class="nf">-- </span><span class="nv">games'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ status>> <span class="s">"completed"</span> <span class="nb">= </span>] <span class="nb">filter </span><span class="k">;
</span></span></span></code></pre></div><p>Finally, putting all this together, we make one word to print out all
the completed games:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">worldcup.</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> worldcup completed-games [ game. ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/worldcup/worldcup.factor">GitHub</a>.</p>
Filename Sanitization
https://re.factorcode.org/2014/06/filename-sanitization.html
Fri, 13 Jun 2014 14:03:00 -0700https://re.factorcode.org/2014/06/filename-sanitization.html<p>I came across the <a href="https://github.com/madrobby/zaru">Zaru</a> project that
provides filename sanitization for Ruby. You can learn a bit about
filenames reading the <a href="https://en.wikipedia.org/wiki/Filename">article on
Wikipedia</a>. I thought it might be
a nice feature to implement something like this for
<a href="https://factorcode.org">Factor</a>.</p>
<p>The rules for sanitization are relatively simple, so I will list and
then implement each one:</p>
<p>1. Certain characters generally don’t mix well with certain file
systems, so we filter them:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">filter-special</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"/\\?*:|\"<>"</span> <span class="nb">member? not </span>] <span class="nb">filter </span><span class="k">;
</span></span></span></code></pre></div><p>2. <a href="https://en.wikipedia.org/wiki/ASCII#ASCII_control_characters">ASCII control
characters</a>
(<code>0x00</code> to <code>0x1f</code>) are not usually a good idea, either:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">filter-control</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ control? <span class="nb">not </span>] <span class="nb">filter </span><span class="k">;
</span></span></span></code></pre></div><p>3. <a href="https://en.wikipedia.org/wiki/Whitespace_character#Unicode">Unicode
whitespace</a>
is trimmed from the beginning and end of the filename and collapsed to a
single space within the filename:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">filter-blanks</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ blank? ] split-when <span class="nb">harvest </span><span class="s">" "</span> <span class="nb">join </span><span class="k">;
</span></span></span></code></pre></div><p>4. Certain filenames are <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx">reserved on
Windows</a>
and are filtered (substituting a “<code>file</code>” placeholder name):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">filter-windows-reserved</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>>upper {
</span></span><span class="line"><span class="cl"> <span class="s">"CON"</span> <span class="s">"PRN"</span> <span class="s">"AUX"</span> <span class="s">"NUL"</span> <span class="s">"COM1"</span> <span class="s">"COM2"</span> <span class="s">"COM3"</span> <span class="s">"COM4"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"COM5"</span> <span class="s">"COM6"</span> <span class="s">"COM7"</span> <span class="s">"COM8"</span> <span class="s">"COM9"</span> <span class="s">"LPT1"</span> <span class="s">"LPT2"</span> <span class="s">"LPT3"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"LPT4"</span> <span class="s">"LPT5"</span> <span class="s">"LPT6"</span> <span class="s">"LPT7"</span> <span class="s">"LPT8"</span> <span class="s">"LPT9"</span>
</span></span><span class="line"><span class="cl"> } <span class="nb">member? </span>[ <span class="nb">drop </span><span class="s">"file"</span> ] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><p>5. Empty filenames are not allowed, replaced instead with <code>file</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">filter-empty</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"file"</span> ] <span class="nb">when-empty </span><span class="k">;
</span></span></span></code></pre></div><p>6. Filenames that begin with only a “dot” character are replaced with
<code>file</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">filter-dots</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup first </span><span class="sc">CHAR: . </span><span class="nb">= </span>[ <span class="s">"file"</span> <span class="nb">prepend </span>] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><p>Putting it all together, and requiring the filename to be no more than
255 characters:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">sanitize-path</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">path'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> filter-special
</span></span><span class="line"><span class="cl"> filter-control
</span></span><span class="line"><span class="cl"> filter-blanks
</span></span><span class="line"><span class="cl"> filter-windows-reserved
</span></span><span class="line"><span class="cl"> filter-empty
</span></span><span class="line"><span class="cl"> filter-dots
</span></span><span class="line"><span class="cl"> <span class="m">255 </span><span class="nb">short head </span><span class="k">;
</span></span></span></code></pre></div><p>The code for this (and some tests) is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/sanitize-paths/sanitize-paths.factor">GitHub</a>.</p>
Swift Ranges
https://re.factorcode.org/2014/06/swift-ranges.html
Tue, 10 Jun 2014 18:11:00 -0700https://re.factorcode.org/2014/06/swift-ranges.html<p>Looking at the documentation for the <a href="https://developer.apple.com/swift/">Swift programming
language</a> recently released by
<a href="https://apple.com">Apple</a>, I noticed they have support for integer
ranges, similar to how the
<a href="https://docs.factorcode.org/content/article-ranges.html">range</a>
objects work in <a href="https://factorcode.org">Factor</a>.</p>
<p>In Swift, you can get a range of the integers 2 through 6 by doing
<code>2...6</code> and the integers 2 through 5 by doing <code>2..6</code>. Notice the use of
three or two dots to indicate whether the range includes the last
number, or not, respectively.</p>
<p>I thought it would be fun to implement a similar syntax for Factor.</p>
<p>First, you can show that:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2 6 </span>[a..b) <span class="nb">>array </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">2 3 4 5 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2 6 </span>[a..b] <span class="nb">>array </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">2 3 4 5 6 </span>}
</span></span></code></pre></div><p>Similar to how we implemented <a href="https://re.factorcode.org/2010/08/fat-arrows.html">fat
arrows</a> (also
known as “pair rockets” or “hash rockets”), we can define the following
syntax words:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYNTAX: </span>.. <span class="nb">dup pop </span>scan-object [a..b) <span class="nb">suffix! </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">SYNTAX: </span>... <span class="nb">dup pop </span>scan-object [a..b] <span class="nb">suffix! </span><span class="k">;
</span></span></span></code></pre></div><p>And then use them:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2 </span>.. <span class="m">6 </span><span class="nb">>array </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">2 3 4 5 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2 </span>... <span class="m">6 </span><span class="nb">>array </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">2 3 4 5 6 </span>}
</span></span></code></pre></div>Comparing k-NN in Factor
https://re.factorcode.org/2014/06/comparing-k-nn-in-factor.html
Tue, 10 Jun 2014 11:23:00 -0700https://re.factorcode.org/2014/06/comparing-k-nn-in-factor.html<p>Recently a
<a href="https://philtomson.github.io/blog/2014/05/29/comparing-a-machine-learning-algorithm-implemented-in-f-number-and-ocaml/">pair</a>
of <a href="https://philtomson.github.io/blog/2014/05/30/stop-the-presses-ocaml-wins/">blog
posts</a>
compared implementations of a k-nearest neighbour (k-NN) classifier in
<a href="https://fsharp.org/">F#</a> and <a href="https://ocaml.org/">OCaml</a>. Subsequently an
<a href="https://huonw.github.io/2014/06/10/knn-rust.html">implementation</a>
showing performance in <a href="https://www.rust-lang.org/">Rust</a> got my
attention and I thought it might be nice to demonstrate a version in
<a href="https://factorcode.org">Factor</a>.</p>
<p>The first OCaml version is 30 lines of code and takes 21 seconds on my
laptop:</p>
<pre tabindex="0"><code>$ sloccount classifyDigits.ml
ml: 30 (100.00%)
$ time ./classifyDigits
Percentage correct:94.400000
real 0m21.292s
user 0m21.152s
sys 0m0.120s
</code></pre><p>The second OCaml version is 47 lines of code and takes 12 seconds:</p>
<pre tabindex="0"><code>$ sloccount classifyDigitsArray.ml
ml: 47 (100.00%)
$ time ./classifyDigitsArray
Percentage correct:94.400000
real 0m12.563s
user 0m12.434s
sys 0m0.120s
</code></pre><p><em>Note: I couldn’t get the parallel version to run, but would assume it
to have the same 2x speedup that the author saw.</em></p>
<h3 id="simple">Simple</h3>
<p>It is often useful to start with the simplest possible code before
trying to optimize for performance. I decided to parse the training and
validation files (containing comma-separated values, the first of which
is the label and the subsequent values are observations) into an array
of arrays.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">slurp-file</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">{pixels,label}</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> ascii file-lines <span class="nb">rest </span>[
</span></span><span class="line"><span class="cl"> <span class="s">","</span> split [ string>number ] <span class="nb">map unclip 2array
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">classify</span> <span class="nf">( </span><span class="nv">training</span> <span class="nv">pixels</span> <span class="nf">-- </span><span class="nv">label</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ <span class="nb">first </span>_ distance ] <span class="nb">infimum-by second </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">k-nn</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"~/trainingsample.csv"</span> slurp-file
</span></span><span class="line"><span class="cl"> <span class="s">"~/validationsample.csv"</span> slurp-file
</span></span><span class="line"><span class="cl"> [ [ <span class="nb">first2 </span>[ classify ] [ <span class="nb">= </span>] <span class="nb">bi* </span>] <span class="nb">with count </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>] <span class="nb">bi / </span><span class="m">100.0 </span><span class="nb">* </span><span class="s">"Percentage correct: %.1f\n"</span> printf <span class="k">;
</span></span></span></code></pre></div><p>You can see that it produces the desired output of 94.4% correct, and
takes about 40 seconds on my laptop.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ k-nn ] time
</span></span><span class="line"><span class="cl">Percentage correct: <span class="m">94.4
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">40.283777984 </span>seconds
</span></span></code></pre></div><p>Not too bad for 11 lines of simple code, but slower than it could be.
Much of the performance penalty in this version is due to the large
amount of generic dispatch, which is something we hope to reduce in
future versions of Factor.</p>
<h3 id="faster">Faster</h3>
<p>I noticed all the observed values were in the range [0-255], so
thought a simple speedup might be to store them in a
<a href="https://docs.factorcode.org/content/article-byte-arrays.html">byte-array</a>,
and instead of using the builtin
<a href="https://docs.factorcode.org/content/word-distance,math.vectors.html">distance</a>
word, make my own that specializes on byte-arrays.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">slurp-file</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">{pixels,label}</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> ascii file-lines <span class="nb">rest </span>[
</span></span><span class="line"><span class="cl"> <span class="s">","</span> split [ string>number ] B{ } <span class="nb">map-as unclip 2array
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">distance</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nf">-- </span><span class="nv">z</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { byte-array byte-array } declare <span class="m">0 </span>[ <span class="nb">- sq + </span>] <span class="nb">2reduce </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">classify</span> <span class="nf">( </span><span class="nv">training</span> <span class="nv">pixels</span> <span class="nf">-- </span><span class="nv">label</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ <span class="nb">first </span>_ distance ] <span class="nb">infimum-by second </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">k-nn</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"~/trainingsample.csv"</span> slurp-file
</span></span><span class="line"><span class="cl"> <span class="s">"~/validationsample.csv"</span> slurp-file
</span></span><span class="line"><span class="cl"> [ [ <span class="nb">first2 </span>[ classify ] [ <span class="nb">= </span>] <span class="nb">bi* </span>] <span class="nb">with count </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>] <span class="nb">bi / </span><span class="m">100.0 </span><span class="nb">* </span><span class="s">"Percentage correct: %f\n"</span> printf <span class="k">;
</span></span></span></code></pre></div><p>With that simple change, we get 7x faster than our previous version and
roughly as fast as the fastest parallel OCaml version!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ k-nn ] time
</span></span><span class="line"><span class="cl">Percentage correct: <span class="m">94.400000
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">5.708627884 </span>seconds
</span></span></code></pre></div><p>The Rust version requires a nightly build and I haven’t had a chance to
test it, but I assume it is a bit faster, and discussions on
<a href="https://www.reddit.com/r/rust/comments/27s7ei/comparing_knn_in_rust/">r/rust</a>,
<a href="https://www.reddit.com/r/programming/comments/27s7g6/comparing_knn_in_rust/">r/programming</a>,
and <a href="https://news.ycombinator.com/item?id=7872398">Hacker News</a> show
some fast versions in C++ and D as well.</p>
<p>The code for this is in my
<a href="https://github.com/mrjbq7/re-factor/blob/master/k-nn/k-nn.factor">GitHub</a>.</p>
2 + 2 = 5
https://re.factorcode.org/2014/06/2-2-5.html
Thu, 05 Jun 2014 11:07:00 -0700https://re.factorcode.org/2014/06/2-2-5.html<p>There is an old programmer joke that wonders if <a href="https://www.straightdope.com/columns/read/1382/does-2-2-5-for-very-large-values-of-2">2 + 2 = 5 for very
large values of
2</a>
(someone even made it into a <a href="https://www.zazzle.com/2_2_5_for_extremely_large_values_of_2_tshirts-235313471775664182">fun
T-shirt</a>).</p>
<p>Well, a challenge on <a href="https://codegolf.stackexchange.com/">StackExchange</a>
to <a href="https://codegolf.stackexchange.com/questions/28786/write-a-program-that-makes-2-2-5">write a program that makes 2 + 2 =
5</a>
caught my eye. I wondered what a solution might look like in
<a href="https://factorcode.org">Factor</a>.</p>
<p>If you run this code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> << <span class="s">"\x32"</span> create-word-in 5/2 define-constant >>
</span></span></code></pre></div><p>Then… whoa!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2 2 </span><span class="nb">+ </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">5
</span></span></span></code></pre></div><p>As it turns out, a little bit ago I noticed that you can redefine
numbers this way and <a href="https://github.com/factor/factor/issues/1053">filed a
bug</a> to start a
conversation about this “feature”.</p>
<p>This exploits the parser, particularly the
<a href="https://docs.factorcode.org/content/word-parse-datum%2Cparser.html">parse-datum</a>
word which searches a token for an already defined
<a href="https://docs.factorcode.org/content/article-words.html">word</a>, then if
not found, tries to parse it as a number. Usually, we disallow words
from being defined by a number using
<a href="https://docs.factorcode.org/content/word-scan-word-name,parser.html">scan-word-name</a>,
but that doesn’t prevent you from doing it yourself as in the example
above.</p>
<p>P.S., in the spirit of the <a href="https://codegolf.stackexchange.com/a/28794">Haskell
solution</a> (and anyone else
that craves <a href="https://docs.factorcode.org/content/article-infix.html">infix
notation</a>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">2+2</span> <span class="m">5
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> 2+2 <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">5
</span></span></span></code></pre></div>Pagination
https://re.factorcode.org/2014/06/pagination.html
Mon, 02 Jun 2014 19:33:00 -0700https://re.factorcode.org/2014/06/pagination.html<p>Most of you have used the
<a href="https://en.wikipedia.org/wiki/Pagination">pagination</a> on various
websites, usually in the context of search results or forum posts. I
thought it would be fun to build a simple “paginator”, using
<a href="https://factorcode.org">Factor</a>.</p>
<p>For example, if you are on page 23 of 28 total pages, you might see
something like this, where you show the selected page and other pages
that you can quickly link to:</p>
<pre tabindex="0"><code><< 1 2 ... 21 22 [23] 24 25 ... 27 28 >
</code></pre><p>Creating a specification from this, our goal will be to show:</p>
<ul>
<li>the first two pages</li>
<li>the selected page (with two pages before and after)</li>
<li>the last two pages</li>
</ul>
<p>Using the
<a href="https://docs.factorcode.org/content/word-output__gt__array,combinators.smart.html">output>array</a>
smart combinator (and <a href="https://docs.factorcode.org/content/article-locals.html">lexical
variables</a>), we
can generate a sequence of page numbers, filtered to make sure we only
allow valid page numbers between <code>1</code> and <code>#pages</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">pages-to-show</span> <span class="nf">( </span><span class="nv">page</span> <span class="nv">#pages</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="m">1 2 </span>page {
</span></span><span class="line"><span class="cl"> [ <span class="m">2 </span><span class="nb">- </span>]
</span></span><span class="line"><span class="cl"> [ <span class="m">1 </span><span class="nb">- </span>]
</span></span><span class="line"><span class="cl"> [ ]
</span></span><span class="line"><span class="cl"> [ <span class="m">1 </span><span class="nb">+ </span>]
</span></span><span class="line"><span class="cl"> [ <span class="m">2 </span><span class="nb">+ </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span>#pages [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> ] output>array members
</span></span><span class="line"><span class="cl"> [ <span class="m">1 </span>#pages between? ] <span class="nb">filter </span><span class="k">;
</span></span></span></code></pre></div><p>Some <a href="https://docs.factorcode.org/content/article-tools.test.html">unit
tests</a>
demonstrate that this works for our “spec” pretty well:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ { <span class="m">1 2 3 99 100 </span>} } [ <span class="m">1 100 </span>pages-to-show ] unit-test
</span></span><span class="line"><span class="cl">{ { <span class="m">1 2 21 22 23 24 25 27 28 </span>} } [ <span class="m">23 28 </span>pages-to-show ] unit-test
</span></span><span class="line"><span class="cl">{ { <span class="m">1 2 3 </span>} } [ <span class="m">1 3 </span>pages-to-show ] unit-test
</span></span></code></pre></div><p>Lastly, we can split the page numbers to display ellipsis on gaps, and
print something like our original goal above:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">pages-to-show.</span> <span class="nf">( </span><span class="nv">page</span> <span class="nv">#pages</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> page #pages pages-to-show
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap - </span><span class="m">1 </span><span class="nb">= </span>] monotonic-split { <span class="no">f </span>} <span class="nb">join
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ number>string ]
</span></span><span class="line"><span class="cl"> [ page <span class="nb">= </span>[ <span class="s">"["</span> <span class="s">"]"</span> <span class="nb">surround </span>] <span class="nb">when </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] [ <span class="s">"..."</span> ] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map </span><span class="s">" "</span> <span class="nb">join </span><span class="s">"<< "</span> <span class="s">" >>"</span> <span class="nb">surround print </span><span class="k">;
</span></span></span></code></pre></div><p>See, it works!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1 100 </span>pages-to-show.
</span></span><span class="line"><span class="cl"><< [1] <span class="m">2 3 </span>... <span class="m">99 100 </span><span class="nb">>
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">23 28 </span>pages-to-show.
</span></span><span class="line"><span class="cl"><< <span class="m">1 2 </span>... <span class="m">21 22 </span>[23] <span class="m">24 25 </span>... <span class="m">27 28 </span><span class="nb">>
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1 3 </span>pages-to-show.
</span></span><span class="line"><span class="cl"><< [1] <span class="m">2 3 </span><span class="nb">>
</span></span></span></code></pre></div><p>Using this in a web application is left as an exercise for the reader,
although it might be nice to create a <code>furnace.pagination</code> vocabulary
that automatically handles this in our <a href="https://docs.factorcode.org/content/article-furnace.html">web
framework</a>.</p>
<p>You can find this code on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/pagination/pagination.factor">GitHub</a>.</p>
Instant-runoff Voting
https://re.factorcode.org/2014/04/instant-runoff-voting.html
Thu, 24 Apr 2014 10:34:00 -0700https://re.factorcode.org/2014/04/instant-runoff-voting.html<p>Recently, I had a conversation with a friend in Australia who told me
about the voting system used in most of their elections: <a href="https://en.wikipedia.org/wiki/Instant-runoff_voting">instant-runoff
voting</a>.</p>
<p>Instead of voting for a single candidate, you rank candidates in the
order of preference. This ranking system is used to choose a best
candidate.</p>
<ol>
<li>Count each person’s most preferred candidate.</li>
<li>The winning candidate must have more than 50% of the votes.</li>
<li>Otherwise, remove the candidate with the least number of overall votes, and try again.</li>
</ol>
<p>Let’s implement a voting system like this in
<a href="https://factorcode.org">Factor</a>.</p>
<p>Assuming voters provide an ordered list of candidates, we can count
everyone’s top candidate:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">count-votes</span> <span class="nf">( </span><span class="nv">votes</span> <span class="nf">-- </span><span class="nv">total</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">first </span>] histogram-by sort-values <span class="k">;
</span></span></span></code></pre></div><p>A candidate wins the election if he has a simple majority (more than
50%) of the votes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">choose-winner</span> <span class="nf">( </span><span class="nv">votes</span> <span class="nv">total</span> <span class="nf">-- </span><span class="nv">winner/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">last first2 rot length 2/ > </span>[ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">unless </span><span class="k">;
</span></span></span></code></pre></div><p>If the candidate with the most votes did not achieve a majority of the
votes, we remove all votes for the candidate with the least number of
votes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">remove-loser</span> <span class="nf">( </span><span class="nv">votes</span> <span class="nv">total</span> <span class="nf">-- </span><span class="nv">newvotes</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">first first swap </span>[ <span class="nb">remove </span>] <span class="nb">with map </span><span class="k">;
</span></span></span></code></pre></div><p>The full implementation of our instant-runoff voting system:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">instant-runoff</span> <span class="nf">( </span><span class="nv">votes</span> <span class="nf">-- </span><span class="nv">winner</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>count-votes <span class="nb">2dup </span>choose-winner
</span></span><span class="line"><span class="cl"> [ <span class="nb">2nip </span>] [ remove-loser instant-runoff ] <span class="nb">if* </span><span class="k">;
</span></span></span></code></pre></div><p>One improvement we could make would be to support versions of this model
that do not require voters to rank all the candidates (an assumption
that the code above makes).</p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/voting/voting.factor">GitHub</a>.</p>
Checksum Improvements
https://re.factorcode.org/2014/04/checksum-improvements.html
Tue, 22 Apr 2014 19:58:00 -0700https://re.factorcode.org/2014/04/checksum-improvements.html<p>Just a quick update, some checksum improvements have been contributed to
<a href="https://factorcode.org">Factor</a>.</p>
<p>Some new checksums have been implemented:</p>
<ul>
<li><a href="https://docs.factorcode.org/content/article-checksums.murmur.html">checksums.murmur</a>:
MurmurHash3 in 32-bit</li>
<li><a href="https://docs.factorcode.org/content/article-checksums.fletcher.html">checksums.fletcher</a>:
Fletcher in 16/32/64-bits</li>
<li><a href="https://docs.factorcode.org/content/article-checksums.superfast.html">checksums.superfast</a>:
SuperFastHash</li>
<li><a href="https://docs.factorcode.org/content/article-checksums.xxhash.html">checksums.xxhash</a>:
xxHash in 32-bit</li>
</ul>
<p>And some checksum performance has been improved:</p>
<ul>
<li><a href="https://docs.factorcode.org/content/article-checksums.md5.html">checksums.md5</a>
is a lot faster (benchmark is 0.080 vs 0.583 seconds)</li>
<li><a href="https://docs.factorcode.org/content/article-checksums.sha.html">checksums.sha</a>
is a bit faster (benchmark is 0.418 vs 0.686 seconds)</li>
</ul>
<p>You can find these changes (and more!) in the <a href="https://github.com/factor/factor">development
version</a> of
<a href="https://factorcode.org">Factor</a>.</p>
Scraping Re: Factor
https://re.factorcode.org/2014/04/scraping-re-factor.html
Mon, 21 Apr 2014 17:35:00 -0700https://re.factorcode.org/2014/04/scraping-re-factor.html<p>For today’s post, I thought it would be fun to build a little interface
to my blog, <a href="">Re: Factor</a>. In addition,
you can use this to easily scrape any <a href="https://www.blogger.com">Blogger</a>
website.</p>
<p>
<img src="https://re.factorcode.org/images/2014-04-21-scraping-re-factor.png" alt="" width="391" height="230" />
</p>
<h3 id="scraping">Scraping</h3>
<p>A simple way to make
<a href="https://en.wikipedia.org/wiki/Uniform_resource_locator">URLs</a> that are
relative to my blogs domain:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">re-factor-url</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">url</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"/"</span> <span class="nb">prepend </span><span class="k">;
</span></span></span></code></pre></div><p>Using that to get a URL that returns all of the posts as
<a href="https://en.wikipedia.org/wiki/JSON">JSON</a> objects.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">posts-url</span> <span class="nf">( -- </span><span class="nv">url</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"feeds/posts/default?alt=json&max-results=200"</span> re-factor-url <span class="k">;
</span></span></span></code></pre></div><blockquote>
<p><em>Note: we limit the results to 200 posts. Since this is my 190th post,
that will work for a little while longer but that limit might need to
be bumped up in the future. <code>:-)</code></em></p>
</blockquote>
<p>Retrieving all of the posts is easy using our <a href="https://docs.factorcode.org/content/article-http.client.html">HTTP
client</a> and
parsing the response. Since my posts don’t change <em>that</em> frequently, for
convenience we will
<a href="https://docs.factorcode.org/content/article-memoize.html">memoize</a> the
list result.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MEMO:</span> <span class="nf">all-posts</span> <span class="nf">( -- </span><span class="nv">posts</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> posts-url http-get <span class="nb">nip </span>json> { <span class="s">"feed"</span> <span class="s">"entry"</span> } [ <span class="nb">of </span>] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><h3 id="displaying">Displaying</h3>
<p>A simple way to display a list of posts is to display the title of each
post and link it to the URL of each post (allowing us to right-click
<a href="https://re.factorcode.org/2011/05/open-url-in-listener.html">open URLs in the
listener</a>).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">post-style</span> H{ { foreground COLOR: blue } }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">posts.</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> all-posts [
</span></span><span class="line"><span class="cl"> [ <span class="s">"title"</span> <span class="nb">of </span><span class="s">"$t"</span> <span class="nb">of </span>] [ <span class="s">"link"</span> <span class="nb">of </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> <span class="nb">over </span>'[ <span class="s">"title"</span> <span class="nb">of </span>_ <span class="nb">= </span>] <span class="nb">find nip </span><span class="s">"href"</span> <span class="nb">of
</span></span></span><span class="line"><span class="cl"> >url post-style [ write-object ] with-style <span class="nb">nl
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>For individual posts, we will use the
<a href="https://docs.factorcode.org/content/vocab-html.parser.printer.html">html.parser.printer</a>
vocabulary to parse the HTML content and display it as text. The
conversion to text right now is not perfect, but works okay for most
things.</p>
<p>We print the title of the post and a dashed line underneath:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">post-title.</span> <span class="nf">( </span><span class="nv">post</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> { <span class="s">"title"</span> <span class="s">"$t"</span> } [ <span class="nb">of </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">print </span>] [ <span class="nb">length </span><span class="sc">CHAR: - </span><span class="nb"><string> print </span>] <span class="nb">bi nl </span><span class="k">;
</span></span></span></code></pre></div><p>We print the content by rendering the HTML into a string of text, then
cleaning up extra whitespace and HTML escapes (using the new
<a href="https://docs.factorcode.org/content/vocab-html.entities.html">html.entities</a>
vocabulary), and wrapping the paragraphs.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">post-content.</span> <span class="nf">( </span><span class="nv">post</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> { <span class="s">"content"</span> <span class="s">"$t"</span> } [ <span class="nb">of </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> parse-html html-text html-unescape string-lines [
</span></span><span class="line"><span class="cl"> [ blank? <span class="nb">not </span>] cut-when
</span></span><span class="line"><span class="cl"> [ <span class="nb">write </span>] [ <span class="m">70 </span>wrap-string <span class="nb">print </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>Putting those together, to display a post:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">post.</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> all-posts <span class="nb">nth </span>[ post-title. ] [ post-content. ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/re-factor/re-factor.factor">GitHub</a>.</p>
Speedtest
https://re.factorcode.org/2014/04/speedtest.html
Sat, 05 Apr 2014 14:27:00 -0700https://re.factorcode.org/2014/04/speedtest.html<p>Many people are familiar with <a href="https://speedtest.net">Speedtest.net</a>,
which is used to test a network connection, displaying download speeds,
upload speeds, and server latency. Implemented as a Flash-based
interface, it can be used from a web browser to verify your internet
provider is giving you what you pay for.</p>
<p>You might not be aware that the
<a href="https://github.com/sivel/speedtest-cli">speedtest-cli</a> project provides
a way to <a href="https://xmodulo.com/2014/01/check-internet-speed-command-line-linux.html">check internet speed from the command
line</a>
in a similar manner.</p>
<p>I thought it might be fun to implement an interface to
<a href="https://speedtest.net">Speedtest.net</a> using
<a href="https://factorcode.org">Factor</a>:</p>
<p>
<img src="https://re.factorcode.org/images/2014-04-05-speedtest-speedtest.png" alt="" width="536" height="256" />
</p>
<h3 id="closest-servers">Closest Servers</h3>
<p>Speedtest provides a list of available servers all over the world that
can be used for testing, returned as
<a href="https://en.wikipedia.org/wiki/XML">XML</a>. After parsing the XML document,
we use a utility method to extract attributes for each server into an
array:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">attr-map</span> <span class="nf">( </span><span class="nv">tag</span> <span class="nf">-- </span><span class="nv">attrs</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> attrs>> [ [ main>> ] <span class="nb">dip </span>] H{ } <span class="nb">assoc-map-as </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">speedtest-servers</span> <span class="nf">( -- </span><span class="nv">servers</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://www.speedtest.net/speedtest-servers.php"</span>
</span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>string>xml
</span></span><span class="line"><span class="cl"> <span class="s">"server"</span> deep-tags-named [ attr-map ] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>Calculating the <a href="https://en.wikipedia.org/wiki/Geographical_distance">geographical
distance</a> between
two points, specified by latitude and longitude:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">radians</span> <span class="nf">( </span><span class="nv">degrees</span> <span class="nf">-- </span><span class="nv">radians</span> <span class="nf">) </span>pi <span class="nb">* </span><span class="m">180 </span><span class="nb">/f </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">geo-distance</span> <span class="nf">( </span><span class="nv">lat1</span> <span class="nv">lon1</span> <span class="nv">lat2</span> <span class="nv">lon2</span> <span class="nf">-- </span><span class="nv">distance</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">6371 </span>:> radius <span class="c">! km</span>
</span></span><span class="line"><span class="cl"> lat2 lat1 <span class="nb">- </span>radians :> dlat
</span></span><span class="line"><span class="cl"> lon2 lon1 <span class="nb">- </span>radians :> dlon
</span></span><span class="line"><span class="cl"> dlat <span class="m">2 </span><span class="nb">/ </span>sin <span class="nb">sq </span>dlon <span class="m">2 </span><span class="nb">/ </span>sin <span class="nb">sq
</span></span></span><span class="line"><span class="cl"> lat1 radians cos lat2 radians cos <span class="nb">* * + </span>:> a
</span></span><span class="line"><span class="cl"> a sqrt <span class="m">1 </span>a <span class="nb">- </span>sqrt fatan2 <span class="m">2 </span><span class="nb">* </span>:> c
</span></span><span class="line"><span class="cl"> radius c <span class="nb">* </span><span class="k">;
</span></span></span></code></pre></div><p>This lets us find the closest server to a given geographic location:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">lat/lon</span> <span class="nf">( </span><span class="nv">assoc</span> <span class="nf">-- </span><span class="nv">lat</span> <span class="nv">lon</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"lat"</span> <span class="nb">of </span>] [ <span class="s">"lon"</span> <span class="nb">of </span>] <span class="nb">bi </span>[ string>number ] <span class="nb">bi@ </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">server-distance</span> <span class="nf">( </span><span class="nv">server</span> <span class="nv">lat</span> <span class="nv">lon</span> <span class="nf">-- </span><span class="nv">server</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ lat/lon _ _ geo-distance <span class="s">"distance"</span> ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">set-at </span>] <span class="nb">keep </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">closest-servers-to</span> <span class="nf">( </span><span class="nv">lat</span> <span class="nv">lon</span> <span class="nf">-- </span><span class="nv">servers</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ speedtest-servers ] <span class="nb">2dip </span>'[ _ _ server-distance ] <span class="nb">map
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"distance"</span> <span class="nb">of </span>] sort-with <span class="k">;
</span></span></span></code></pre></div><p>The available Speedtest configuration provides our latitude and
longitude, allowing us to sort the server list by geographic distance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">config</span> <span class="nv">client</span> <span class="nv">times</span> <span class="nv">download</span> <span class="nv">upload</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">C:</span> <span class="nf"><config></span> <span class="nc">config</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">speedtest-config</span> <span class="nf">( -- </span><span class="nv">config</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://www.speedtest.net/speedtest-config.php"</span>
</span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>string>xml {
</span></span><span class="line"><span class="cl"> [ <span class="s">"client"</span> deep-tag-named attr-map ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"times"</span> deep-tag-named attr-map ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"download"</span> deep-tag-named attr-map ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"upload"</span> deep-tag-named attr-map ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span><config> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">closest-servers</span> <span class="nf">( -- </span><span class="nv">servers</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> speedtest-config client>> lat/lon closest-servers-to <span class="k">;
</span></span></span></code></pre></div><h3 id="best-server">Best Server</h3>
<p>We can calculate latency by downloading a small <code>latency.txt</code> file and
timing how long it takes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(server-latency)</span> <span class="nf">( </span><span class="nv">server</span> <span class="nf">-- </span><span class="nv">ms</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"url"</span> <span class="nb">of </span>>url <span class="s">URL" latency.txt"</span> derive-url
</span></span><span class="line"><span class="cl"> [ http-get <span class="nb">nip </span><span class="s">"test=test\n"</span> <span class="nb">= </span>] benchmark <span class="m">1,000,000 </span><span class="nb">/f
</span></span></span><span class="line"><span class="cl"> <span class="m">3,600,000 </span><span class="nb">? </span><span class="k">;
</span></span></span></code></pre></div><p>After calculating latency, we save it for later use:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">server-latency</span> <span class="nf">( </span><span class="nv">server</span> <span class="nf">-- </span><span class="nv">server</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ (server-latency) <span class="s">"latency"</span> ] <span class="nb">keep </span>[ <span class="nb">set-at </span>] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div><p>The “best” server that we will use for testing is the one with the
lowest latency, checking the five closest servers to our location:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">best-server</span> <span class="nf">( -- </span><span class="nv">server</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> closest-servers <span class="m">5 </span><span class="nb">short head
</span></span></span><span class="line"><span class="cl"> [ server-latency ] parallel-map
</span></span><span class="line"><span class="cl"> [ <span class="s">"latency"</span> <span class="nb">of </span>] sort-with <span class="nb">first </span><span class="k">;
</span></span></span></code></pre></div><h3 id="upload-speed">Upload Speed</h3>
<p>To calculate upload speed, we upload several document sizes (filling the
content with zeroes) and time how long it takes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">upload-data</span> <span class="nf">( </span><span class="nv">size</span> <span class="nf">-- </span><span class="nv">data</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">9 </span><span class="nb">- </span><span class="sc">CHAR: 0 </span><span class="nb"><string> </span><span class="s">"content1="</span> <span class="nb">prepend </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(upload-speed)</span> <span class="nf">( </span><span class="nv">server</span> <span class="nf">-- </span><span class="nv">Mbps</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"url"</span> <span class="nb">of </span>>url { <span class="m">250,000 500,000 </span>} [
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> upload-data [ <span class="nb">swap </span>http-put <span class="nb">2drop </span>] <span class="nb">keep length
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">with map-sum
</span></span></span><span class="line"><span class="cl"> ] benchmark <span class="m">1,000,000,000 </span><span class="nb">/f / </span><span class="m">8 </span><span class="nb">* </span><span class="m">1,000,000 </span><span class="nb">/ </span><span class="k">;
</span></span></span></code></pre></div><p>After calculating upload speed, we save it for later use:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">upload-speed</span> <span class="nf">( </span><span class="nv">server</span> <span class="nf">-- </span><span class="nv">server</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ (upload-speed) <span class="s">"upload"</span> ] <span class="nb">keep </span>[ <span class="nb">set-at </span>] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div><h3 id="download-speed">Download Speed</h3>
<p>To calculate download speed, we download several files with varying
sizes in parallel and time how long it takes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">download-urls</span> <span class="nf">( </span><span class="nv">server</span> <span class="nf">-- </span><span class="nv">urls</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"url"</span> { <span class="m">350 500 750 1000 </span>}
</span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span><span class="s">"random%sx%s.jpg"</span> sprintf >url derive-url ] <span class="nb">with map </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(download-speed)</span> <span class="nf">( </span><span class="nv">server</span> <span class="nf">-- </span><span class="nv">Mbps</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> download-urls <span class="m">4 </span><span class="nb">swap <array> </span>[
</span></span><span class="line"><span class="cl"> [ [ http-get <span class="nb">nip length </span>] <span class="nb">map-sum </span>] parallel-map <span class="nb">sum
</span></span></span><span class="line"><span class="cl"> ] benchmark <span class="m">1,000,000,000 </span><span class="nb">/f / </span><span class="m">8 </span><span class="nb">* </span><span class="m">1,000,000 </span><span class="nb">/ </span><span class="k">;
</span></span></span></code></pre></div><p>After calculating download speed, we save it for later use:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">download-speed</span> <span class="nf">( </span><span class="nv">server</span> <span class="nf">-- </span><span class="nv">server</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ (download-speed) <span class="s">"download"</span> ] <span class="nb">keep </span>[ <span class="nb">set-at </span>] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div><h3 id="text-results">Text Results</h3>
<p>With all of that built, we can build a word to run a Speedtest, printing
out the results as text:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">run-speedtest</span> <span class="nf">( -- </span><span class="nv">server</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"Selecting best server based on ping..."</span> <span class="nb">print flush
</span></span></span><span class="line"><span class="cl"> best-server <span class="nb">dup </span>{
</span></span><span class="line"><span class="cl"> [ <span class="s">"sponsor"</span> <span class="nb">of </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"name"</span> <span class="nb">of </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"distance"</span> <span class="nb">of </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"latency"</span> <span class="nb">of </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span><span class="s">"Hosted by %s (%s) [%0.2f km]: %s ms\n"</span> printf
</span></span><span class="line"><span class="cl"> <span class="s">"Testing download speed"</span> <span class="nb">print flush </span>download-speed
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"download"</span> <span class="nb">of </span><span class="s">"Download: %0.2f Mbit/s\n"</span> printf
</span></span><span class="line"><span class="cl"> <span class="s">"Testing upload speed"</span> <span class="nb">print flush </span>upload-speed
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"upload"</span> <span class="nb">of </span><span class="s">"Upload: %0.2f Mbit/s\n"</span> printf <span class="k">;
</span></span></span></code></pre></div><h3 id="graphic-results">Graphic Results</h3>
<p>It would be nice if we could show the reports graphically, and as it
turns out, its not too hard. We just have to upload the results to
speedtest.net in the same way their Flash application does, and then
display the image that is created for you.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">make-result</span> <span class="nf">( </span><span class="nv">server</span> <span class="nf">-- </span><span class="nv">result</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ <span class="s">"download"</span> <span class="nb">of </span><span class="m">1,000 </span><span class="nb">* >integer </span><span class="s">"download"</span> ,, ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"latency"</span> <span class="nb">of >integer </span><span class="s">"ping"</span> ,, ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"upload"</span> <span class="nb">of </span><span class="m">1,000 </span><span class="nb">* >integer </span><span class="s">"upload"</span> ,, ]
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">""</span> <span class="s">"promo"</span> ,, ]
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">"pingselect"</span> <span class="s">"startmode"</span> ,, ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"id"</span> <span class="nb">of </span><span class="s">"recommendedserverid"</span> ,, ]
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">"1"</span> <span class="s">"accuracy"</span> ,, ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"id"</span> <span class="nb">of </span><span class="s">"serverid"</span> ,, ]
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ <span class="s">"latency"</span> <span class="nb">of </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"upload"</span> <span class="nb">of </span><span class="m">1,000 </span><span class="nb">* </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"download"</span> <span class="nb">of </span><span class="m">1,000 </span><span class="nb">* </span>] <span class="nb">tri
</span></span></span><span class="line"><span class="cl"> <span class="s">"%d-%d-%d-297aae72"</span> sprintf md5 checksum-bytes
</span></span><span class="line"><span class="cl"> hex-string <span class="s">"hash"</span> ,,
</span></span><span class="line"><span class="cl"> ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave
</span></span></span><span class="line"><span class="cl"> ] { } make <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">submit-result</span> <span class="nf">( </span><span class="nv">server</span> <span class="nf">-- </span><span class="nv">result-id</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> make-result <span class="s">"https://www.speedtest.net/api/api.php"</span>
</span></span><span class="line"><span class="cl"> <post-request> [
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="s">"https://c.speedtest.net/flash/speedtest.swf"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"referer"</span>
</span></span><span class="line"><span class="cl"> ] <span class="nb">dip </span>header>> <span class="nb">set-at
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">keep </span>http-request <span class="nb">nip </span>query>assoc <span class="s">"resultid"</span> <span class="nb">of </span><span class="k">;
</span></span></span></code></pre></div><h3 id="speedtest">Speedtest</h3>
<p>Putting this all together, we can run the Speedtest, submit the results,
then display the test results as an image.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">speedtest</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> run-speedtest submit-result <span class="s">"Share results: "</span> <span class="nb">write
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://www.speedtest.net/result/%s.png"</span> sprintf
</span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>>url write-object <span class="nb">nl </span>] [ http-image. ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>Some things that I would like to improve:</p>
<ul>
<li>The Speedtest configuration actually specifies the details of
download and upload sizes, the amount of parallelism, and the
duration of the test, we should use it.</li>
<li>The
<a href="https://docs.factorcode.org/content/word-http-get,http.client.html">http-get</a>
word needs an overall timeout so we can scale between very slow and
very fast connection speeds.</li>
<li>The Speedtest graphical result images are “retina” when viewed in
the web browser, but are not when downloaded from
<a href="https://factorcode.org">Factor</a> or
<a href="https://www.gnu.org/software/wget/">wget</a>.</li>
<li>Factor needs an easier way to create a queue of work that is
processed by several worker threads, for convenience I just used one
of the <a href="https://docs.factorcode.org/content/article-concurrency.combinators.html">concurrent
combinators</a>.</li>
</ul>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/speedtest/speedtest.factor">GitHub</a>.</p>
inet_ntoa and inet_aton
https://re.factorcode.org/2014/02/inet-ntoa-and-inet-aton.html
Sun, 09 Feb 2014 15:48:00 -0800https://re.factorcode.org/2014/02/inet-ntoa-and-inet-aton.html<p>I was reading an article about <a href="https://tapoueh.org/blog/2013/10/03-micro-optimizing-int-to-ip-address">micro optimizing int to IP
address</a>
conversions. The author was trying to convert a 32-bit integer
representation of an IP address into the more typical string
representation using <a href="https://en.wikipedia.org/wiki/Common_Lisp">Common
Lisp</a>.</p>
<p>This is basically what the standard C library functions
<a href="https://linux.die.net/man/3/inet_ntoa">inet_ntoa</a> and
<a href="https://linux.die.net/man/3/inet_aton">inet_aton</a> do. I thought it
might be fun to implement this in <a href="https://factorcode.org">Factor</a> and
compare performance with the C versions.</p>
<h3 id="alien">alien</h3>
<p>First, lets use the
<a href="https://docs.factorcode.org/content/article-alien.html">alien</a> FFI
vocabulary to allow the C functions to be called from Factor:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> c-string <span class="nf">inet_ntoa</span> ( uint32_t addr )
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> int <span class="nf">inet_aton</span> ( c-string s, uint32_t *addr )
</span></span></code></pre></div><p>We can call <code>inet_ntoa</code> directly, but to call <code>inet_aton</code>, we need a
simple wrapper that calls it, preserves the result, and checks for
success or failure:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">inet-aton</span> <span class="nf">( </span><span class="nv">x</span> <span class="nf">-- </span><span class="nv">y</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { uint32_t } [ inet_aton <span class="m">1 </span><span class="nb">assert= </span>] with-out-parameters <span class="k">;
</span></span></span></code></pre></div><p>We can test to see that it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">81952074 </span>inet_ntoa <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"74.125.226.4"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"74.125.226.4"</span> inet-aton <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">81952074
</span></span></span></code></pre></div><p>Parsing 1 million integers with <code>inet_ntoa</code> takes <strong>1.016 seconds</strong>.</p>
<p>Parsing 1 million IP addresses with <code>inet_aton</code> takes <strong>0.626 seconds</strong>.</p>
<h3 id="simple">simple</h3>
<p>Okay, what if we want to implement these ourselves?</p>
<blockquote>
<p><em>Note: Unlike <code>inet_ntoa</code> (which is in network byte order), we will
assume <a href="https://en.wikipedia.org/wiki/Endianness">little endian</a> like
the original author that spawned this adventure.</em></p>
</blockquote>
<p>Converting an integer into an IP address string by taking each
<a href="https://en.wikipedia.org/wiki/Octet_(computing)">octet</a> of the 32-bit
number (based on the
<a href="https://dev.maxmind.com/geoip/legacy/csv/">pseudocode</a> used in the
original article):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">ipv4-ntoa</span> <span class="nf">( </span><span class="nv">integer</span> <span class="nf">-- </span><span class="nv">ip</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { <span class="m">0x1000000 0x10000 0x100 0x1 </span>}
</span></span><span class="line"><span class="cl"> [ <span class="nb">/i </span><span class="m">8 </span>bits number>string ] <span class="nb">with map </span><span class="s">"."</span> <span class="nb">join </span><span class="k">;
</span></span></span></code></pre></div><p>Converting an IP address into an integer is as easy as splitting on the
dots and performing the reverse operation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">ipv4-aton</span> <span class="nf">( </span><span class="nv">ip</span> <span class="nf">-- </span><span class="nv">integer</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"."</span> split [ string>number ] <span class="nb">map
</span></span></span><span class="line"><span class="cl"> { <span class="m">0x1000000 0x10000 0x100 0x1 </span>} v. <span class="k">;
</span></span></span></code></pre></div><p>We can test that it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1249763844 </span>ipv4-ntoa <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"74.125.226.4"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"74.125.226.4"</span> ipv4-aton <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">1249763844
</span></span></span></code></pre></div><p>Parsing 1 million integers with <code>ipv4-ntoa</code> takes <strong>0.653 seconds</strong>.</p>
<p>Parsing 1 million IP addresses with <code>ipv4-aton</code> takes <strong>0.738 seconds</strong>.</p>
<h3 id="faster">faster</h3>
<p>In the spirit of the original article, we will try some
micro-optimizations (with some corresponding loss in readability)
including <a href="https://docs.factorcode.org/content/article-typed.html">type
annotations</a>.</p>
<p>Our versions use the generalized number parsing words,
<a href="https://docs.factorcode.org/content/word-string__gt__number,math.parser.html">string>number</a>
and
<a href="https://docs.factorcode.org/content/word-number__gt__string,math.parser.html">number>string</a>.
Specialized (less general) versions can give us additional performance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TYPED:</span> <span class="nf">byte>string</span> <span class="nf">( </span><span class="nv">byte:</span> <span class="nv">fixnum</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> $[ <span class="m">256 </span><iota> [ number>string ] <span class="nb">map </span>] <span class="nb">nth </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">TYPED:</span> <span class="nf">string>byte</span> <span class="nf">( </span><span class="nv">str:</span> <span class="nv">string</span> <span class="nf">-- </span><span class="nv">byte</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>[ [ <span class="m">10 </span><span class="nb">* </span>] <span class="nb">dip </span><span class="sc">CHAR: 0 </span><span class="nb">- + </span>] <span class="nb">reduce </span><span class="k">;
</span></span></span></code></pre></div><p>We make a few other changes to use shifting and a slightly different
approach:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TYPED:</span> <span class="nf">ipv4-ntoa2</span> <span class="nf">( </span><span class="nv">integer:</span> <span class="nv">fixnum</span> <span class="nf">-- </span><span class="nv">ip</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { <span class="m">-24 -16 -8 0 </span>} [ <span class="m">8 </span>shift-mod byte>string ] <span class="nb">with map
</span></span></span><span class="line"><span class="cl"> <span class="s">"."</span> <span class="nb">join </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">TYPED:</span> <span class="nf">ipv4-aton2</span> <span class="nf">( </span><span class="nv">ip:</span> <span class="nv">string</span> <span class="nf">-- </span><span class="nv">integer</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"."</span> split { <span class="m">24 16 8 0 </span>}
</span></span><span class="line"><span class="cl"> [ [ string>byte ] <span class="nb">dip shift </span>] [ <span class="nb">+ </span>] <span class="nb">2map-reduce </span><span class="k">;
</span></span></span></code></pre></div><p>Parsing 1 million integers with <code>ipv4-ntoa2</code> takes <strong>0.436 seconds</strong>!</p>
<p>Parsing 1 million IP addresses with <code>ipv4-aton2</code> takes <strong>0.496
seconds</strong>!</p>
<h3 id="fastest">fastest</h3>
<p>If we really want to do more micro-optimizations, and produce some ugly
but fast code, then we can change the code to ensure more
<a href="https://docs.factorcode.org/content/word-fixnum,math.html">fixnum</a>
operations:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TYPED:</span> <span class="nf">byte>string2</span> <span class="nf">( </span><span class="nv">byte:</span> <span class="nv">fixnum</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> $[ <span class="m">256 </span><iota> [ number>string ] <span class="nb">map </span>] nth-unsafe <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">TYPED:</span> <span class="nf">string>byte2</span> <span class="nf">( </span><span class="nv">str:</span> <span class="nv">string</span> <span class="nf">-- </span><span class="nv">byte</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span><iota> <span class="m">0 </span>] <span class="nb">keep </span>[
</span></span><span class="line"><span class="cl"> string-nth-fast
</span></span><span class="line"><span class="cl"> [ <span class="m">10 </span>fixnum*fast ] <span class="nb">dip </span><span class="sc">CHAR: 0 </span>fixnum-fast fixnum+fast
</span></span><span class="line"><span class="cl"> ] <span class="nb">curry reduce </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">TYPED:</span> <span class="nf">ipv4-ntoa3</span> <span class="nf">( </span><span class="nv">integer:</span> <span class="nv">fixnum</span> <span class="nf">-- </span><span class="nv">ip</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> $[ { <span class="m">-24 -16 -8 0 </span>} [ [ <span class="m">8 </span>shift-mod ] <span class="nb">curry </span>] <span class="nb">map </span>] <span class="nb">cleave
</span></span></span><span class="line"><span class="cl"> [ byte>string2 ] <span class="m">4 </span>napply <span class="nb">4array </span><span class="s">"."</span> <span class="nb">join </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">TYPED:</span> <span class="nf">ipv4-aton3</span> <span class="nf">( </span><span class="nv">ip:</span> <span class="nv">string</span> <span class="nf">-- </span><span class="nv">integer</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"."</span> split <span class="nb">first4 </span>[ string>byte2 ] <span class="m">4 </span>napply
</span></span><span class="line"><span class="cl"> [ <span class="m">24 16 8 </span>[ fixnum-shift-fast ] <span class="nb">tri-curry@ tri* </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> [ fixnum+fast ] <span class="nb">tri@ </span><span class="k">;
</span></span></span></code></pre></div><p>Parsing 1 million integers with <code>ipv4-ntoa3</code> takes <strong>0.285 seconds</strong>!</p>
<p>Parsing 1 million IP addresses with <code>ipv4-aton3</code> takes <strong>0.355
seconds</strong>!</p>
<p>I have committed more complete versions of <code>ipv4-ntoa</code> and <code>ipv4-aton</code>
(with support for <a href="https://re.factorcode.org/2012/10/parsing-ipv4-addresses.html">parsing IPv4
addresses</a>)
to the <a href="https://github.com/factor/factor/blob/master/extra/ip-parser/ip-parser.factor">development
version</a>
of Factor.</p>
Caesar Cipher
https://re.factorcode.org/2014/01/caesar-cipher.html
Wed, 22 Jan 2014 21:17:00 -0800https://re.factorcode.org/2014/01/caesar-cipher.html<p>A <a href="https://en.wikipedia.org/wiki/Caesar_cipher">Caesar cipher</a> is a very
simple encryption technique where each letter is shifted a fixed number
of characters in the alphabet. It is named after <a href="https://en.wikipedia.org/wiki/Julius_Caesar">Julius
Caesar</a>, who apparently used
this technique in some of his letters.</p>
<p>For example, if we were to encode <code>"FACTOR"</code> by shifting each character
to the right by three letters, we would get <code>"IDFWRU"</code>. The <code>"F"</code> shifts
to <code>"I"</code>, the <code>"A"</code> shifts to <code>"D"</code>, the <code>"C"</code> shifts to <code>"F"</code>, etc.</p>
<p>Let’s implement this in <a href="https://factorcode.org">Factor</a>!</p>
<p>First, we implement a word to shift a character (uppercase by
convention) a specified number of letters. Using <code>"A"</code> as our “zero”
point by subtracting, shifting modulo 26 character ascii alphabet, then
re-adding the ascii value for <code>"A"</code> (65):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">caesar-shift</span> <span class="nf">( </span><span class="nv">ch</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">ch'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="sc">CHAR: A </span><span class="nb">- </span>] <span class="nb">dip + </span><span class="m">26 </span><span class="nb">rem </span><span class="sc">CHAR: A </span><span class="nb">+ </span><span class="k">;
</span></span></span></code></pre></div><p>Next, a word for shifting every letter in a string (preserving numbers
and punctuation):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">caesar-map</span> <span class="nf">( </span><span class="nv">str</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ <span class="nb">dup </span><span class="sc">CHAR: A CHAR: Z </span>between? [ _ caesar-shift ] <span class="nb">when </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>This lets us implement the encrypt and decrypt words. Encrypting is
simple and decrypting is mapping with a negative shift number:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">caesar-encrypt</span> <span class="nf">( </span><span class="nv">plain</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">encrypted</span> <span class="nf">) </span>caesar-map <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">caesar-decrypt</span> <span class="nf">( </span><span class="nv">encrypted</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">plain</span> <span class="nf">) </span><span class="nb">neg </span>caesar-map <span class="k">;
</span></span></span></code></pre></div><p>Trying it out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"HELLO, WORLD!"</span> <span class="m">3 </span>caesar-encrypt <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"KHOOR, ZRUOG!"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"KHOOR, ZRUOG!"</span> <span class="m">3 </span>caesar-decrypt <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"HELLO, WORLD!"</span>
</span></span></code></pre></div>Monte Carlo
https://re.factorcode.org/2013/12/monte-carlo.html
Fri, 13 Dec 2013 10:46:00 -0800https://re.factorcode.org/2013/12/monte-carlo.html<p>The <a href="https://en.wikipedia.org/wiki/Monte_Carlo_method">Monte Carlo
method</a> is a method of
estimation that uses random simulation to solve physical or mathematical
problems. Named after the <a href="https://en.wikipedia.org/wiki/Monte_Carlo_Casino">Monte Carlo
Casino</a>, you can think
of the method as playing a game of chance many times and recording the
outcomes (such as how frequently one wins or loses).</p>
<p>One classic example from the Wikipedia article is estimating the value
of <a href="https://en.wikipedia.org/wiki/Pi">π</a> (although there are many other
ways to <a href="https://en.wikipedia.org/wiki/Approximations_of_%CF%80">approximate the value of
π</a>):</p>
<p>For example, consider a circle inscribed in a unit square. Given that
the circle and the square have a ratio of areas that is π/4, the value
of π can be approximated using a Monte Carlo method:</p>
<ol>
<li>Draw a square on the ground, then inscribe a circle within it.</li>
<li>Uniformly scatter some objects of uniform size (grains of rice or
sand) over the square.</li>
<li>Count the number of objects inside the circle and the total number
of objects.</li>
<li>The ratio of the two counts is an estimate of the ratio of the two
areas, which is π/4. Multiply the result by 4 to estimate π.</li>
</ol>
<p>You can visualize how this works by seeing the estimate get more correct
as you increase the number of points:</p>
<p>
<img src="https://re.factorcode.org/images/2013-12-13-monte-carlo-Pi_30K.gif" alt="" width="250" height="250" />
</p>
<p>We can generate a random point inside the unit circle by generating two
<a href="https://docs.factorcode.org/content/word-random-unit,random.html">random-unit</a>
values:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-point</span> <span class="nf">( -- </span><span class="nv">x</span> <span class="nv">y</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> random-unit random-unit <span class="k">;
</span></span></span></code></pre></div><p>Using the <a href="https://en.wikipedia.org/wiki/Pythagorean_theorem">Pythagorean
theorem</a>, we can
calculate the distance from the zero point. If the distance is less than
or equal to 1.0, then it is inside the circle:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">inside-circle?</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">sq </span>] <span class="nb">bi@ + </span>sqrt <span class="m">1.0 </span><span class="nb"><= </span><span class="k">;
</span></span></span></code></pre></div><p>We can then estimate the value of π by computing a number of points,
taking the percentage that are inside the circle and multiplying by 4:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">estimate-pi</span> <span class="nf">( </span><span class="nv">points</span> <span class="nf">-- </span><span class="nv">pi-estimate</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span><span class="nb">swap </span>[
</span></span><span class="line"><span class="cl"> [ random-point inside-circle? [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">when </span>] <span class="nb">times
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">keep /f </span><span class="m">4 </span><span class="nb">* </span><span class="k">;
</span></span></span></code></pre></div><p>We can run this for varying numbers of points and see how it gets more
accurate:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">100 1,000 10,000 100,000 1,000,000 10,000,000 </span>}
</span></span><span class="line"><span class="cl"> [ estimate-pi <span class="m">. </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"><span class="m">3.2
</span></span></span><span class="line"><span class="cl"><span class="m">3.168
</span></span></span><span class="line"><span class="cl"><span class="m">3.162
</span></span></span><span class="line"><span class="cl"><span class="m">3.15176
</span></span></span><span class="line"><span class="cl"><span class="m">3.14288
</span></span></span><span class="line"><span class="cl"><span class="m">3.1418212
</span></span></span></code></pre></div>UU Encoding
https://re.factorcode.org/2013/12/uu-encoding.html
Tue, 10 Dec 2013 18:04:00 -0800https://re.factorcode.org/2013/12/uu-encoding.html<p>Just a quick note, as of a couple months ago,
<a href="https://factorcode.org">Factor</a> has support for
<a href="https://en.wikipedia.org/wiki/Uuencoding">uuencoding</a> (and uudecoding)!</p>
<p>You can perform a uuencode:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Factor"</span> string>uu <span class="nb">print
</span></span></span><span class="line"><span class="cl">begin
</span></span><span class="line"><span class="cl">&1F%C=&]R
</span></span><span class="line"><span class="cl">end
</span></span></code></pre></div><p>…and also a uudecode:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"""
</span></span></span><span class="line"><span class="cl"><span class="s"> begin 644 factor.txt
</span></span></span><span class="line"><span class="cl"><span class="s"> &1F%C=&]R
</span></span></span><span class="line"><span class="cl"><span class="s"> end
</span></span></span><span class="line"><span class="cl"><span class="s"> """</span> uu>string <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"Factor"</span>
</span></span></code></pre></div><p>Right now, it operates on text directly and doesn’t preserve the file
name and permissions in the <code>begin</code> header, but that would be an easy
improvement.</p>
<p>The code for this is available in the <a href="https://github.com/factor/factor/blob/master/extra/uu/uu.factor">development
version</a>
of Factor.</p>
Humanhash
https://re.factorcode.org/2013/12/humanhash.html
Thu, 05 Dec 2013 15:42:00 -0800https://re.factorcode.org/2013/12/humanhash.html<p><a href="https://github.com/zacharyvoase">Zachary Voase</a> published the
<a href="https://github.com/zacharyvoase/humanhash">humanhash project</a> on
GitHub, making “human-readable representations of digests”. Below is a
compatible implementation in <a href="https://factorcode.org">Factor</a>.</p>
<p>To show how it will work, we use the example from his <a href="https://github.com/zacharyvoase/humanhash/blob/master/README.md">humanhash
README</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="k">CONSTANT:</span> <span class="nf">digest</span> <span class="s">"7528880a986c40e78c38115e640da2a1"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> digest humanhash <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"three-georgia-xray-jig"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> digest <span class="m">6 </span>humanhash-words <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"high-mango-white-oregon-purple-charlie"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> human-uuid4 <span class="nb">2array </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> <span class="s">"28129036-75a7-4c87-984b-4b32231e0a0d"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"nineteen-bluebird-oxygen-edward"</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><h3 id="implementation">Implementation</h3>
<p>We need a list of 256 words, one to represent each possible byte:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">default-wordlist</span> {
</span></span><span class="line"><span class="cl"> <span class="s">"ack"</span> <span class="s">"alabama"</span> <span class="s">"alanine"</span> <span class="s">"alaska"</span> <span class="s">"alpha"</span> <span class="s">"angel"</span> <span class="s">"apart"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"april"</span> <span class="s">"arizona"</span> <span class="s">"arkansas"</span> <span class="s">"artist"</span> <span class="s">"asparagus"</span> <span class="s">"aspen"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"august"</span> <span class="s">"autumn"</span> <span class="s">"avocado"</span> <span class="s">"bacon"</span> <span class="s">"bakerloo"</span> <span class="s">"batman"</span> <span class="s">"beer"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"berlin"</span> <span class="s">"beryllium"</span> <span class="s">"black"</span> <span class="s">"blossom"</span> <span class="s">"blue"</span> <span class="s">"bluebird"</span> <span class="s">"bravo"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"bulldog"</span> <span class="s">"burger"</span> <span class="s">"butter"</span> <span class="s">"california"</span> <span class="s">"carbon"</span> <span class="s">"cardinal"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"carolina"</span> <span class="s">"carpet"</span> <span class="s">"cat"</span> <span class="s">"ceiling"</span> <span class="s">"charlie"</span> <span class="s">"chicken"</span> <span class="s">"coffee"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"cola"</span> <span class="s">"cold"</span> <span class="s">"colorado"</span> <span class="s">"comet"</span> <span class="s">"connecticut"</span> <span class="s">"crazy"</span> <span class="s">"cup"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"dakota"</span> <span class="s">"december"</span> <span class="s">"delaware"</span> <span class="s">"delta"</span> <span class="s">"diet"</span> <span class="s">"don"</span> <span class="s">"double"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"early"</span> <span class="s">"earth"</span> <span class="s">"east"</span> <span class="s">"echo"</span> <span class="s">"edward"</span> <span class="s">"eight"</span> <span class="s">"eighteen"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"eleven"</span> <span class="s">"emma"</span> <span class="s">"enemy"</span> <span class="s">"equal"</span> <span class="s">"failed"</span> <span class="s">"fanta"</span> <span class="s">"fifteen"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fillet"</span> <span class="s">"finch"</span> <span class="s">"fish"</span> <span class="s">"five"</span> <span class="s">"fix"</span> <span class="s">"floor"</span> <span class="s">"florida"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"football"</span> <span class="s">"four"</span> <span class="s">"fourteen"</span> <span class="s">"foxtrot"</span> <span class="s">"freddie"</span> <span class="s">"friend"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fruit"</span> <span class="s">"gee"</span> <span class="s">"georgia"</span> <span class="s">"glucose"</span> <span class="s">"golf"</span> <span class="s">"green"</span> <span class="s">"grey"</span> <span class="s">"hamper"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"happy"</span> <span class="s">"harry"</span> <span class="s">"hawaii"</span> <span class="s">"helium"</span> <span class="s">"high"</span> <span class="s">"hot"</span> <span class="s">"hotel"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"hydrogen"</span> <span class="s">"idaho"</span> <span class="s">"illinois"</span> <span class="s">"india"</span> <span class="s">"indigo"</span> <span class="s">"ink"</span> <span class="s">"iowa"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"island"</span> <span class="s">"item"</span> <span class="s">"jersey"</span> <span class="s">"jig"</span> <span class="s">"johnny"</span> <span class="s">"juliet"</span> <span class="s">"july"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"jupiter"</span> <span class="s">"kansas"</span> <span class="s">"kentucky"</span> <span class="s">"kilo"</span> <span class="s">"king"</span> <span class="s">"kitten"</span> <span class="s">"lactose"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"lake"</span> <span class="s">"lamp"</span> <span class="s">"lemon"</span> <span class="s">"leopard"</span> <span class="s">"lima"</span> <span class="s">"lion"</span> <span class="s">"lithium"</span> <span class="s">"london"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"louisiana"</span> <span class="s">"low"</span> <span class="s">"magazine"</span> <span class="s">"magnesium"</span> <span class="s">"maine"</span> <span class="s">"mango"</span> <span class="s">"march"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"mars"</span> <span class="s">"maryland"</span> <span class="s">"massachusetts"</span> <span class="s">"may"</span> <span class="s">"mexico"</span> <span class="s">"michigan"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"mike"</span> <span class="s">"minnesota"</span> <span class="s">"mirror"</span> <span class="s">"mississippi"</span> <span class="s">"missouri"</span> <span class="s">"mobile"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"mockingbird"</span> <span class="s">"monkey"</span> <span class="s">"montana"</span> <span class="s">"moon"</span> <span class="s">"mountain"</span> <span class="s">"muppet"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"music"</span> <span class="s">"nebraska"</span> <span class="s">"neptune"</span> <span class="s">"network"</span> <span class="s">"nevada"</span> <span class="s">"nine"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"nineteen"</span> <span class="s">"nitrogen"</span> <span class="s">"north"</span> <span class="s">"november"</span> <span class="s">"nuts"</span> <span class="s">"october"</span> <span class="s">"ohio"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"oklahoma"</span> <span class="s">"one"</span> <span class="s">"orange"</span> <span class="s">"oranges"</span> <span class="s">"oregon"</span> <span class="s">"oscar"</span> <span class="s">"oven"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"oxygen"</span> <span class="s">"papa"</span> <span class="s">"paris"</span> <span class="s">"pasta"</span> <span class="s">"pennsylvania"</span> <span class="s">"pip"</span> <span class="s">"pizza"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"pluto"</span> <span class="s">"potato"</span> <span class="s">"princess"</span> <span class="s">"purple"</span> <span class="s">"quebec"</span> <span class="s">"queen"</span> <span class="s">"quiet"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"red"</span> <span class="s">"river"</span> <span class="s">"robert"</span> <span class="s">"robin"</span> <span class="s">"romeo"</span> <span class="s">"rugby"</span> <span class="s">"sad"</span> <span class="s">"salami"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"saturn"</span> <span class="s">"september"</span> <span class="s">"seven"</span> <span class="s">"seventeen"</span> <span class="s">"shade"</span> <span class="s">"sierra"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"single"</span> <span class="s">"sink"</span> <span class="s">"six"</span> <span class="s">"sixteen"</span> <span class="s">"skylark"</span> <span class="s">"snake"</span> <span class="s">"social"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"sodium"</span> <span class="s">"solar"</span> <span class="s">"south"</span> <span class="s">"spaghetti"</span> <span class="s">"speaker"</span> <span class="s">"spring"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"stairway"</span> <span class="s">"steak"</span> <span class="s">"stream"</span> <span class="s">"summer"</span> <span class="s">"sweet"</span> <span class="s">"table"</span> <span class="s">"tango"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"ten"</span> <span class="s">"tennessee"</span> <span class="s">"tennis"</span> <span class="s">"texas"</span> <span class="s">"thirteen"</span> <span class="s">"three"</span> <span class="s">"timing"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"triple"</span> <span class="s">"twelve"</span> <span class="s">"twenty"</span> <span class="s">"two"</span> <span class="s">"uncle"</span> <span class="s">"undress"</span> <span class="s">"uniform"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"uranus"</span> <span class="s">"utah"</span> <span class="s">"vegan"</span> <span class="s">"venus"</span> <span class="s">"vermont"</span> <span class="s">"victor"</span> <span class="s">"video"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"violet"</span> <span class="s">"virginia"</span> <span class="s">"washington"</span> <span class="s">"west"</span> <span class="s">"whiskey"</span> <span class="s">"white"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"william"</span> <span class="s">"winner"</span> <span class="s">"winter"</span> <span class="s">"wisconsin"</span> <span class="s">"wolfram"</span> <span class="s">"wyoming"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"xray"</span> <span class="s">"yankee"</span> <span class="s">"yellow"</span> <span class="s">"zebra"</span> <span class="s">"zulu"</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>One of the inputs is the number of words to produce. An error is
produced if fewer bytes are provided than the number of words requested:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">ERROR:</span> <span class="nc">too-few-bytes</span> <span class="nv">seq</span> <span class="nv">#words</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">check-bytes</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">#words</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nv">#words</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">2dup </span>[ <span class="nb">length </span>] [ <span class="nb">< </span>] <span class="nb">bi* </span>[ too-few-bytes ] <span class="nb">when </span><span class="k">; inline
</span></span></span></code></pre></div><p>Input is grouped into subsequences, where the number of subsequences is
the number of words requested in the output. It’s a little bit odd, but
basically makes groups and then puts any remainder in the last group:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">group-words</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">#words</span> <span class="nf">-- </span><span class="nv">groups</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dupd </span>[ <span class="nb">length </span>] [ <span class="nb">/i </span>] <span class="nb">bi* </span>group ]
</span></span><span class="line"><span class="cl"> [ <span class="m">1 </span><span class="nb">- cut concat suffix </span>] <span class="nb">bi </span><span class="k">; inline
</span></span></span></code></pre></div><p>Our input bytes are compressed, first by grouping them into words, then
by <a href="https://en.wikipedia.org/wiki/Exclusive_or">XORing</a> the bytes in each
word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">compress-bytes</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">#words</span> <span class="nf">-- </span><span class="nv">newseq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> check-bytes group-words [ <span class="m">0 </span>[ <span class="nb">bitxor </span>] <span class="nb">reduce </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>Our input will either be a
<a href="https://docs.factorcode.org/content/article-byte-arrays.html">byte-array</a>,
or a
<a href="https://docs.factorcode.org/content/word-hex-string,checksums.html">hex-string</a>
with every two characters representing the hexadecimal value of each
byte:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">byte-string</span> <span class="nf">( </span><span class="nv">hexdigest</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>byte-array? [ <span class="m">2 </span><groups> [ hex> ] <span class="nb">map </span>] <span class="nb">unless </span><span class="k">;
</span></span></span></code></pre></div><p>Making a humanhash is simply converting the input, compressing into
bytes representing each word, looking up the word from the word list,
and joining with a requested separator:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">make-humanhash</span> <span class="nf">( </span><span class="nv">hexdigest</span> <span class="nv">#words</span> <span class="nv">wordlist</span> <span class="nv">sep</span> <span class="nf">-- </span><span class="nv">hash</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { [ byte-string ] [ compress-bytes ] [ <span class="nb">nths </span>] [ <span class="nb">join </span>] } <span class="nb">spread </span><span class="k">;
</span></span></span></code></pre></div><p>We provide a way to hash into a requested number of words, or four by
default:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">humanhash-words</span> <span class="nf">( </span><span class="nv">hexdigest</span> <span class="nv">#words</span> <span class="nf">-- </span><span class="nv">hash</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> default-wordlist <span class="s">"-"</span> make-humanhash <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">humanhash</span> <span class="nf">( </span><span class="nv">hexdigest</span> <span class="nf">-- </span><span class="nv">hash</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">4 </span>humanhash-words <span class="k">;
</span></span></span></code></pre></div><p>And since the humanhash project includes a way to create humanhash’d
<a href="https://docs.factorcode.org/content/article-uuid.html">uuids</a>, we do
also:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">human-uuid4</span> <span class="nf">( -- </span><span class="nv">uuid</span> <span class="nv">hash</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> uuid4 <span class="nb">dup </span>[ <span class="sc">CHAR: - </span><span class="nb">= not </span>] <span class="nb">filter </span>humanhash <span class="k">;
</span></span></span></code></pre></div>tzfile
https://re.factorcode.org/2013/11/tzfile.html
Thu, 28 Nov 2013 19:33:00 -0800https://re.factorcode.org/2013/11/tzfile.html<p>I have wanted to parse <a href="https://man7.org/linux/man-pages/man5/tzfile.5.html">timezone information
files</a> (also known
as “tzfile”) for awhile. In particular, so that
<a href="https://factorcode.org">Factor</a> can begin to support named timezones in
a smarter way.</p>
<h3 id="parsing">Parsing</h3>
<p>The tzfile is a binary format file from the <a href="https://en.wikipedia.org/wiki/Tz_database">tz
database</a> (also known as the
“zoneinfo database”). Each tzfile starts with the four magic bytes
“<code>TZif</code>”, which we can check:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">ERROR:</span> <span class="nc">bad-magic</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">check-magic</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span><span class="s">"TZif"</span> <span class="nb">sequence= </span>[ bad-magic ] <span class="nb">unless </span><span class="k">;
</span></span></span></code></pre></div><p>The tzfile then contains a header followed by a series of <code>ttinfo</code>
structures and other information:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">STRUCT:</span> <span class="nc">tzhead</span>
</span></span><span class="line"><span class="cl"> { tzh_reserved char[16] }
</span></span><span class="line"><span class="cl"> { tzh_ttisgmtcnt be32 }
</span></span><span class="line"><span class="cl"> { tzh_ttisstdcnt be32 }
</span></span><span class="line"><span class="cl"> { tzh_leapcnt be32 }
</span></span><span class="line"><span class="cl"> { tzh_timecnt be32 }
</span></span><span class="line"><span class="cl"> { tzh_typecnt be32 }
</span></span><span class="line"><span class="cl"> { tzh_charcnt be32 } <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">PACKED-STRUCT: ttinfo
</span></span><span class="line"><span class="cl"> { tt_gmtoff be32 }
</span></span><span class="line"><span class="cl"> { tt_isdst uchar }
</span></span><span class="line"><span class="cl"> { tt_abbrind uchar } <span class="k">;
</span></span></span></code></pre></div><p>We can store all the information parsed from the tzfile in a tuple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">tzfile</span> <span class="nv">header</span> <span class="nv">transition-times</span> <span class="nv">local-times</span> <span class="nv">types</span> <span class="nv">abbrevs</span>
</span></span><span class="line"><span class="cl"><span class="nv">leaps</span> <span class="nv">is-std</span> <span class="nv">is-gmt</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">C:</span> <span class="nf"><tzfile></span> <span class="nc">tzfile</span>
</span></span></code></pre></div><p>With a helper word to read 32-bit big-endian numbers, we can parse the
entire file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-be32</span> <span class="nf">( -- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>be32 deref <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-tzfile</span> <span class="nf">( -- </span><span class="nv">tzfile</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> check-magic tzhead read-struct <span class="nb">dup </span>{
</span></span><span class="line"><span class="cl"> [ tzh_timecnt>> [ read-be32 ] <span class="nb">replicate </span>]
</span></span><span class="line"><span class="cl"> [ tzh_timecnt>> [ <span class="nb">read1 </span>] <span class="nb">replicate </span>]
</span></span><span class="line"><span class="cl"> [ tzh_typecnt>> [ ttinfo read-struct ] <span class="nb">replicate </span>]
</span></span><span class="line"><span class="cl"> [ tzh_charcnt>> <span class="nb">read </span>]
</span></span><span class="line"><span class="cl"> [ tzh_leapcnt>> [ read-be32 read-be32 <span class="nb">2array </span>] <span class="nb">replicate </span>]
</span></span><span class="line"><span class="cl"> [ tzh_ttisstdcnt>> <span class="nb">read </span>]
</span></span><span class="line"><span class="cl"> [ tzh_ttisgmtcnt>> <span class="nb">read </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span><tzfile> <span class="k">;
</span></span></span></code></pre></div><p>All of that data specifies a series of local time types and transition
times:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">local-time</span> <span class="nv">gmt-offset</span> <span class="nv">dst?</span> <span class="nv">abbrev</span> <span class="nv">std?</span> <span class="nv">gmt?</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">C:</span> <span class="nf"><local-time></span> <span class="nc">local-time</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">transition</span> <span class="nv">seconds</span> <span class="nv">timestamp</span> <span class="nv">local-time</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">C:</span> <span class="nf"><transition></span> <span class="nc">transition</span>
</span></span></code></pre></div><p>The abbreviated local time names are stored in a flattened array. It
would be helpful to parse them out into a hashtable where the key is the
starting character index in the flattened array:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">tznames</span> <span class="nf">( </span><span class="nv">abbrevs</span> <span class="nf">-- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>[
</span></span><span class="line"><span class="cl"> <span class="m">0 </span><span class="nb">over </span>abbrevs <span class="nb">index-from dup
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> [ <span class="nb">dupd </span>abbrevs <span class="nb">subseq >string 2array </span>] <span class="nb">keep </span><span class="m">1 </span><span class="nb">+ swap
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">produce 2nip </span>>hashtable <span class="k">;
</span></span></span></code></pre></div><p>We can now construct an array of all the transition times and the local
time types they represent. This is a lot of logic for a typical Factor
word, so we use <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a> to
make it easier to understand:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">tzfile>transitions</span> <span class="nf">( </span><span class="nv">tzfile</span> <span class="nf">-- </span><span class="nv">transitions</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> tzfile abbrevs>> tznames :> abbrevs
</span></span><span class="line"><span class="cl"> tzfile is-std>> :> is-std
</span></span><span class="line"><span class="cl"> tzfile is-gmt>> :> is-gmt
</span></span><span class="line"><span class="cl"> tzfile types>> [
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ tt_gmtoff>> seconds ]
</span></span><span class="line"><span class="cl"> [ tt_isdst>> <span class="m">1 </span><span class="nb">= </span>]
</span></span><span class="line"><span class="cl"> [ tt_abbrind>> abbrevs <span class="nb">at </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> [ is-std <span class="nb">?nth dup </span>[ <span class="m">1 </span><span class="nb">= </span>] <span class="nb">when </span>]
</span></span><span class="line"><span class="cl"> [ is-gmt <span class="nb">?nth dup </span>[ <span class="m">1 </span><span class="nb">= </span>] <span class="nb">when </span>] <span class="nb">bi </span><local-time>
</span></span><span class="line"><span class="cl"> ] <span class="nb">map-index </span>:> local-times
</span></span><span class="line"><span class="cl"> tzfile transition-times>>
</span></span><span class="line"><span class="cl"> tzfile local-times>> [
</span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>unix-time>timestamp ] [ local-times <span class="nb">nth </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> <transition>
</span></span><span class="line"><span class="cl"> ] <span class="nb">2map </span><span class="k">;
</span></span></span></code></pre></div><p>We want to wrap the <code>tzfile</code> parsed structure and the transitions in a
<code>tzinfo</code> object that can be used later with timestamps. These <code>tzinfo</code>
objects are created by parsing from specific files by path or by their
zoneinfo name:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">tzinfo</span> <span class="nv">tzfile</span> <span class="nv">transitions</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">C:</span> <span class="nf"><tzinfo></span> <span class="nc">tzinfo</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">file>tzinfo</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">tzinfo</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> binary [
</span></span><span class="line"><span class="cl"> read-tzfile <span class="nb">dup </span>tzfile>transitions <tzinfo>
</span></span><span class="line"><span class="cl"> ] with-file-reader <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">load-tzinfo</span> <span class="nf">( </span><span class="nv">name</span> <span class="nf">-- </span><span class="nv">tzinfo</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"/usr/share/zoneinfo/"</span> <span class="nb">prepend </span>file>tzinfo <span class="k">;
</span></span></span></code></pre></div><h3 id="timestamps">Timestamps</h3>
<p>Now that we have the <code>tzinfo</code>, we can convert a UTC timestamp into the
timezone specified by our tzfile. This is accomplished by finding the
transition time that affects the requested timestamp and adjusting by
the GMT offset that it represents:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-transition</span> <span class="nf">( </span><span class="nv">timestamp</span> <span class="nv">tzinfo</span> <span class="nf">-- </span><span class="nv">transition</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ timestamp>unix-time ] [ transitions>> ] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> [ [ seconds>> before? ] <span class="nb">with find drop </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap </span>[ <span class="m">1 </span>[-] <span class="nb">swap nth </span>] [ <span class="nb">last </span>] <span class="nb">if* </span>] <span class="nb">bi </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">from-utc</span> <span class="nf">( </span><span class="nv">timestamp</span> <span class="nv">tzinfo</span> <span class="nf">-- </span><span class="nv">timestamp'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>instant >>gmt-offset ]
</span></span><span class="line"><span class="cl"> [ find-transition local-time>> gmt-offset>> ] <span class="nb">2bi
</span></span></span><span class="line"><span class="cl"> convert-timezone <span class="k">;
</span></span></span></code></pre></div><p>Or normalize a timestamp that might be in a different timezone into the
timezone specified by our tzfile (converting into and then out of UTC):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">normalize</span> <span class="nf">( </span><span class="nv">timestamp</span> <span class="nv">tzinfo</span> <span class="nf">-- </span><span class="nv">timestamp'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ instant convert-timezone ] [ from-utc ] <span class="nb">bi* </span><span class="k">;
</span></span></span></code></pre></div><h3 id="example">Example</h3>
<p>An example of it working, taking a date in
<a href="https://en.wikipedia.org/wiki/Pacific_Standard_Time">PST</a> that is after
a daylight savings transition, printing it out then subtracting 10
minutes and normalizing to the “US/Pacific” zoneinfo file, printing it
out showing the time in
<a href="https://en.wikipedia.org/wiki/Pacific_Daylight_Time">PDT</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="c">! Take a time in PST</span>
</span></span><span class="line"><span class="cl"> <span class="m">2002 10 27 1 0 0 -8 </span>hours <timestamp>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! Print it out</span>
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"%c"</span> strftime <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"Sun Oct 27 01:00:00 2002"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="c">! Subtract 10 minutes</span>
</span></span><span class="line"><span class="cl"> <span class="m">10 </span>minutes time-
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! Normalize to US-Pacific timezone</span>
</span></span><span class="line"><span class="cl"> <span class="s">"US/Pacific"</span> load-tzinfo normalize
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">! Print it out</span>
</span></span><span class="line"><span class="cl"> <span class="s">"%c"</span> strftime <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"Sun Oct 27 01:50:00 2002"</span>
</span></span></code></pre></div><p>The code for this is available in the <a href="https://github.com/factor/factor/blob/master/extra/tzinfo/tzinfo.factor">development
version</a>
of Factor.</p>
N-Numbers
https://re.factorcode.org/2013/11/n-numbers.html
Mon, 25 Nov 2013 10:00:00 -0800https://re.factorcode.org/2013/11/n-numbers.html<p>In the United States, “N-Numbers” are the name given to <a href="https://en.wikipedia.org/wiki/Aircraft_registration">aircraft
registrations</a>. Some
of the services that the <a href="https://www.faa.gov/">FAA</a> provides include the
ability to <a href="https://registry.faa.gov/aircraftinquiry/nnum_inquiry.aspx">lookup aircraft by
N-Number</a> or
<a href="https://aircraft.faa.gov/e.gov/NN/reserve.aspx">reserve an N-Number</a>.</p>
<p>Below we implement the rules to detect if a string is a valid N-Number
in <a href="https://factorcode.org">Factor</a>.</p>
<ul>
<li>may not begin with zero.</li>
<li>may not be the letters “I” or “O” to avoid confusion with the
numbers one or zero.</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(n-number?)</span> <span class="nf">( </span><span class="nv">digits</span> <span class="nv">letters</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup first </span><span class="sc">CHAR: 0 </span><span class="nb">= </span>[ <span class="nb">drop </span><span class="no">f </span>] [ [ digit? ] <span class="nb">all? </span>] <span class="nb">if </span>]
</span></span><span class="line"><span class="cl"> [ [ [ Letter? ] [ <span class="s">"IiOo"</span> <span class="nb">member? not </span>] <span class="nb">bi and </span>] <span class="nb">all? </span>]
</span></span><span class="line"><span class="cl"> <span class="nb">bi* and </span><span class="k">;
</span></span></span></code></pre></div><ul>
<li>may be one to five numbers (e.g., N12345).</li>
<li>may be one to four numbers and one suffix letter (e.g., N1A and
N1234Z).</li>
<li>may be one to three numbers and two suffix letters (e.g., N24BY and
N123AZ).</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">n-number?</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"N"</span> ?head <span class="nb">drop </span>{
</span></span><span class="line"><span class="cl"> [ { [ <span class="nb">length </span><span class="m">1 5 </span>between? ] [ <span class="no">f </span>(n-number?) ] } 1&& ]
</span></span><span class="line"><span class="cl"> [ { [ <span class="nb">length </span><span class="m">2 5 </span>between? ] [ <span class="m">1 </span><span class="nb">cut* </span>(n-number?) ] } 1&& ]
</span></span><span class="line"><span class="cl"> [ { [ <span class="nb">length </span><span class="m">3 5 </span>between? ] [ <span class="m">2 </span><span class="nb">cut* </span>(n-number?) ] } 1&& ]
</span></span><span class="line"><span class="cl"> } 1|| <span class="k">;
</span></span></span></code></pre></div><p>Registration numbers N1 through N99 are reserved for Federal Aviation
Administration (FAA) internal use and are not available.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">reserved?</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"N"</span> ?head <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> { [ <span class="nb">length </span><span class="m">1 2 </span>between? ] [ [ digit? ] <span class="nb">all? </span>] } 1&& <span class="k">;
</span></span></span></code></pre></div><p>The code and some tests for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/n-numbers/n-numbers.factor">GitHub</a>.</p>
tree
https://re.factorcode.org/2013/11/tree.html
Fri, 22 Nov 2013 11:54:00 -0800https://re.factorcode.org/2013/11/tree.html<p>I’ve used <a href="https://factorcode.org">Factor</a> to build several common unix
programs including
<a href="https://re.factorcode.org/2012/02/copy.html">copy</a>,
<a href="https://re.factorcode.org/2010/08/building-cat.html">cat</a>,
<a href="https://re.factorcode.org/2010/09/fortune-telling.html">fortune</a>,
<a href="https://re.factorcode.org/2011/11/wc-l.html">wc</a>,
<a href="https://re.factorcode.org/2013/04/move.html">move</a>,
<a href="https://re.factorcode.org/2013/08/uniq.html">uniq</a>, and
<a href="https://github.com/mrjbq7/re-factor/tree/master/unix-tools">others</a>.</p>
<p>Today, I wanted to show how to build the <code>tree</code> program. If we look at
the <a href="https://linux.die.net/man/1/tree">man page</a>, we can see that it is
used to:</p>
<blockquote>
<p><em><code>Tree</code> is a recursive directory listing program that produces a depth
indented listing of files. With no arguments, <code>tree</code> lists the files
in the current directory. When directory arguments are given, <code>tree</code>
lists all the files and/or directories found in the given directories
each in turn. Upon completion of listing all files/directories found,
<code>tree</code> returns the total number of files and/or directories listed.</em></p>
</blockquote>
<p>We need to keep track of files and directories that are traversed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">#files</span>
</span></span><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">#directories</span>
</span></span></code></pre></div><p>Each file name is indented according to a specified depth:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">indent</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ [ <span class="s">"| "</span> <span class="nb">write </span>] <span class="nb">times </span>] <span class="nb">unless-zero </span><span class="s">"+-- "</span> <span class="nb">write </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-name</span> <span class="nf">( </span><span class="nv">indent</span> <span class="nv">entry</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ indent ] [ name>> <span class="nb">write </span>] <span class="nb">bi* </span><span class="k">;
</span></span></span></code></pre></div><p>File names are written and increment the <code>#files</code> counter:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-file</span> <span class="nf">( </span><span class="nv">indent</span> <span class="nv">entry</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> write-name #files [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">change-global </span><span class="k">;
</span></span></span></code></pre></div><p>Directory names are written, increase the indent, recurse writing their
directory tree, and increment the <code>#directories</code> counter:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">DEFER:</span> <span class="nf">write-tree</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-dir</span> <span class="nf">( </span><span class="nv">indent</span> <span class="nv">entry</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ write-name ] [
</span></span><span class="line"><span class="cl"> [ [ <span class="m">1 </span><span class="nb">+ </span>] [ name>> ] <span class="nb">bi* </span>write-tree ]
</span></span><span class="line"><span class="cl"> [ <span class="nb">3drop </span><span class="s">" [error opening dir]"</span> <span class="nb">write </span>] <span class="nb">recover
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">2bi </span>#directories [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">change-global </span><span class="k">;
</span></span></span></code></pre></div><p>Using <code>write-file</code> and <code>write-dir</code>, we can implement <code>write-tree</code> to
sort the entries and then write each according to their type (e.g., file
or directory):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-tree</span> <span class="nf">( </span><span class="nv">indent</span> <span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ name>> ] sort-with [
</span></span><span class="line"><span class="cl"> <span class="nb">nl </span>[ <span class="nb">dup </span>] <span class="nb">bi@ </span>type>> +directory+ <span class="nb">=
</span></span></span><span class="line"><span class="cl"> [ write-dir ] [ write-file ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each drop
</span></span></span><span class="line"><span class="cl"> ] with-directory-entries <span class="k">;
</span></span></span></code></pre></div><p>Finally, we can implement the <code>tree</code> command, initializing the file and
directory count, recursing on the path specified, and then printing out
the number of files and directories observed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">tree</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>#directories <span class="nb">set-global </span><span class="m">0 </span>#files <span class="nb">set-global
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">write </span>] [ <span class="m">0 </span><span class="nb">swap </span>write-tree ] <span class="nb">bi nl
</span></span></span><span class="line"><span class="cl"> #directories <span class="nb">get-global </span>#files <span class="nb">get-global
</span></span></span><span class="line"><span class="cl"> <span class="s">"\n%d directories, %d files\n"</span> printf <span class="k">;
</span></span></span></code></pre></div><p>Our command-line word will either operate on the arguments specifying a
list of directories to process, or the current directory if none are
provided:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">run-tree</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> command-line <span class="nb">get </span>[
</span></span><span class="line"><span class="cl"> current-directory <span class="nb">get </span>tree
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> [ tree ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-empty </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">run-tree</span>
</span></span></code></pre></div><p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/unix-tools/tree/tree.factor">GitHub</a>.</p>
MessagePack
https://re.factorcode.org/2013/11/messagepack.html
Mon, 18 Nov 2013 07:14:00 -0800https://re.factorcode.org/2013/11/messagepack.html<p><a href="https://msgpack.org">MessagePack</a> is an “efficient binary serialization
format” designed to be faster and smaller than
<a href="https://www.json.org/">JSON</a> and has support in many programming
languages.</p>
<p>Less than 150 lines of code!</p>
<p>What is that, you ask? Why thats the number of lines of code it took to
implement a <a href="https://msgpack.org">MessagePack</a> encoder and decoder in
<a href="https://factorcode.org">Factor</a>.</p>
<h3 id="reading">Reading</h3>
<p>For decoding, our strategy will be to create a <code>read-msgpack</code> word that
can operate on the current input-stream (allowing reuse for MessagePack
objects read from files, strings, or the network).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">DEFER:</span> <span class="nf">read-msgpack</span>
</span></span></code></pre></div><p>Aside from support for basic data types such as integers, floating-point
numbers, and strings, we also need to support arrays of objects, maps of
key/value pairs, and so-called “extended” object types:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-array</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">obj</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ read-msgpack ] <span class="nb">replicate </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-map</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">obj</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">2 </span><span class="nb">* </span>read-array <span class="m">2 </span>group >hashtable <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-ext</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">obj</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">read </span>be> [ <span class="m">1 </span><span class="nb">read </span>signed-be> ] <span class="nb">dip read 2array </span><span class="k">;
</span></span></span></code></pre></div><p>We need a way to specify a “nil” (or “null”) object since we use <code>t</code> and
<code>f</code> for booleans:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SINGLETON:</span> <span class="nc">+msgpack-nil+</span>
</span></span></code></pre></div><p>And, of course, an error to indicate when a requested format is not
supported:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">ERROR:</span> <span class="nc">unknown-format</span> <span class="nv">n</span> <span class="k">;
</span></span></span></code></pre></div><p>With those definitions done, we can build a word to read a single
MessagePack object from a stream:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-msgpack</span> <span class="nf">( -- </span><span class="nv">obj</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">read1 </span>{
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xc0 </span><span class="nb">= </span>] [ <span class="nb">drop </span>+msgpack-nil+ ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xc2 </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="no">f </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xc3 </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="no">t </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0x00 0x7f </span>between? ] [ ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xe0 </span>mask? ] [ <span class="nb">1array </span>signed-be> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xcc </span><span class="nb">= </span>] [ <span class="nb">drop read1 </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xcd </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">2 </span><span class="nb">read </span>be> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xce </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">4 </span><span class="nb">read </span>be> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xcf </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">8 </span><span class="nb">read </span>be> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xd0 </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">1 </span><span class="nb">read </span>signed-be> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xd1 </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">2 </span><span class="nb">read </span>signed-be> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xd2 </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">4 </span><span class="nb">read </span>signed-be> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xd3 </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">8 </span><span class="nb">read </span>signed-be> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xca </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">4 </span><span class="nb">read </span>be> <span class="nb">bits>float </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xcb </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">8 </span><span class="nb">read </span>be> <span class="nb">bits>double </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xe0 </span>mask <span class="m">0xa0 </span><span class="nb">= </span>] [ <span class="m">0x1f </span>mask <span class="nb">read </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xd9 </span><span class="nb">= </span>] [ <span class="nb">drop read1 read </span><span class="s">""</span> <span class="nb">like </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xda </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">2 </span><span class="nb">read </span>be> <span class="nb">read </span><span class="s">""</span> <span class="nb">like </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xdb </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">4 </span><span class="nb">read </span>be> <span class="nb">read </span><span class="s">""</span> <span class="nb">like </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xc4 </span><span class="nb">= </span>] [ <span class="nb">drop read1 read </span>B{ } <span class="nb">like </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xc5 </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">2 </span><span class="nb">read </span>be> <span class="nb">read </span>B{ } <span class="nb">like </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xc6 </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">4 </span><span class="nb">read </span>be> <span class="nb">read </span>B{ } <span class="nb">like </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xf0 </span>mask <span class="m">0x90 </span><span class="nb">= </span>] [ <span class="m">0x0f </span>mask read-array ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xdc </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">2 </span><span class="nb">read </span>be> read-array ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xdd </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">4 </span><span class="nb">read </span>be> read-array ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xf0 </span>mask <span class="m">0x80 </span><span class="nb">= </span>] [ <span class="m">0x0f </span>mask read-map ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xde </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">2 </span><span class="nb">read </span>be> read-map ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xdf </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">4 </span><span class="nb">read </span>be> read-map ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xd4 </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">1 </span>read-ext ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xd5 </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">2 </span>read-ext ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xd6 </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">4 </span>read-ext ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xd7 </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">8 </span>read-ext ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xd8 </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">16 </span>read-ext ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xc7 </span><span class="nb">= </span>] [ <span class="nb">drop read1 </span>read-ext ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xc8 </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">2 </span><span class="nb">read </span>be> read-ext ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xc9 </span><span class="nb">= </span>] [ <span class="nb">drop </span><span class="m">4 </span><span class="nb">read </span>be> read-ext ] }
</span></span><span class="line"><span class="cl"> [ unknown-format ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="k">;
</span></span></span></code></pre></div><p>Pretty simple!</p>
<h3 id="writing">Writing</h3>
<p>For encoding, our strategy will be to define a generic <code>write-msgpack</code>
word that will dispatch off the type of object being encoded and operate
on the current output-stream (allowing reuse for MessagePack objects
written to files, strings, or the network).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">GENERIC:</span> <span class="nf">write-msgpack</span> <span class="nf">( </span><span class="nv">obj</span> <span class="nf">-- )
</span></span></span></code></pre></div><p>And, of course, an error to indicate when a requested object type isn’t
supported:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">ERROR:</span> <span class="nc">cannot-convert</span> <span class="nv">obj</span> <span class="k">;
</span></span></span></code></pre></div><p>Writing the “nil” (or “null” object) and boolean values true and false:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">+msgpack-nil+</span> <span class="nf">write-msgpack</span> <span class="nb">drop </span><span class="m">0xc0 </span><span class="nb">write1 </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">f</span> <span class="nf">write-msgpack</span> <span class="nb">drop </span><span class="m">0xc2 </span><span class="nb">write1 </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">t</span> <span class="nf">write-msgpack</span> <span class="nb">drop </span><span class="m">0xc3 </span><span class="nb">write1 </span><span class="k">;
</span></span></span></code></pre></div><p>Support for integers and floating point numbers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">integer</span> <span class="nf">write-msgpack</span>
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb">>= </span>[
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0x7f </span><span class="nb"><= </span>] [ <span class="nb">write1 </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xff </span><span class="nb"><= </span>] [ <span class="m">0xcc </span><span class="nb">write1 </span><span class="m">1 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xffff </span><span class="nb"><= </span>] [ <span class="m">0xcd </span><span class="nb">write1 </span><span class="m">2 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xffffffff </span><span class="nb"><= </span>] [ <span class="m">0xce </span><span class="nb">write1 </span><span class="m">4 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xffffffffffffffff </span><span class="nb"><= </span>]
</span></span><span class="line"><span class="cl"> [ <span class="m">0xcf </span><span class="nb">write1 </span><span class="m">8 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> [ cannot-convert ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>-0x1f <span class="nb">>= </span>] [ <span class="m">1 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>-0x80 <span class="nb">>= </span>] [ <span class="m">0xd0 </span><span class="nb">write1 </span><span class="m">1 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>-0x8000 <span class="nb">>= </span>] [ <span class="m">0xd1 </span><span class="nb">write1 </span><span class="m">2 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>-0x80000000 <span class="nb">>= </span>] [ <span class="m">0xd2 </span><span class="nb">write1 </span><span class="m">4 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>-0x8000000000000000 <span class="nb">>= </span>]
</span></span><span class="line"><span class="cl"> [ <span class="m">0xd3 </span><span class="nb">write1 </span><span class="m">8 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> [ cannot-convert ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">float</span> <span class="nf">write-msgpack</span>
</span></span><span class="line"><span class="cl"> <span class="m">0xcb </span><span class="nb">write1 double>bits </span><span class="m">8 </span>>be <span class="nb">write </span><span class="k">;
</span></span></span></code></pre></div><p>Support for strings and byte-arrays:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">string</span> <span class="nf">write-msgpack</span>
</span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>{
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0x1f </span><span class="nb"><= </span>] [ <span class="m">0xa0 </span><span class="nb">bitor write1 </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xff </span><span class="nb"><= </span>] [ <span class="m">0xd9 </span><span class="nb">write1 write1 </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xffff </span><span class="nb"><= </span>] [ <span class="m">0xda </span><span class="nb">write1 </span><span class="m">2 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xffffffff </span><span class="nb"><= </span>] [ <span class="m">0xdb </span><span class="nb">write1 </span><span class="m">4 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> [ cannot-convert ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond write </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">byte-array</span> <span class="nf">write-msgpack</span>
</span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>{
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xff </span><span class="nb"><= </span>] [ <span class="m">0xc4 </span><span class="nb">write1 write1 </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xffff </span><span class="nb"><= </span>] [ <span class="m">0xc5 </span><span class="nb">write1 </span><span class="m">2 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xffffffff </span><span class="nb"><= </span>] [ <span class="m">0xc6 </span><span class="nb">write1 </span><span class="m">4 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> [ cannot-convert ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond write </span><span class="k">;
</span></span></span></code></pre></div><p>Support for arrays of MessagePack objects:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-array-header</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xf </span><span class="nb"><= </span>] [ <span class="m">0x90 </span><span class="nb">bitor write1 </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xffff </span><span class="nb"><= </span>] [ <span class="m">0xdc </span><span class="nb">write1 </span><span class="m">2 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xffffffff </span><span class="nb"><= </span>] [ <span class="m">0xdd </span><span class="nb">write1 </span><span class="m">4 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> [ cannot-convert ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">sequence</span> <span class="nf">write-msgpack</span>
</span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>write-array-header [ write-msgpack ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>Support for maps of key/value pairs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-map-header</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xf </span><span class="nb"><= </span>] [ <span class="m">0x80 </span><span class="nb">bitor write1 </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xffff </span><span class="nb"><= </span>] [ <span class="m">0xde </span><span class="nb">write1 </span><span class="m">2 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">0xffffffff </span><span class="nb"><= </span>] [ <span class="m">0xdf </span><span class="nb">write1 </span><span class="m">4 </span>>be <span class="nb">write </span>] }
</span></span><span class="line"><span class="cl"> [ cannot-convert ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">assoc</span> <span class="nf">write-msgpack</span>
</span></span><span class="line"><span class="cl"> <span class="nb">dup assoc-size </span>write-map-header
</span></span><span class="line"><span class="cl"> [ [ write-msgpack ] <span class="nb">bi@ </span>] <span class="nb">assoc-each </span><span class="k">;
</span></span></span></code></pre></div><h3 id="convenience">Convenience</h3>
<p>To conveniently convert into and out of the MessagePack format, we can
make words to read from and write to strings:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">msgpack></span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">obj</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ read-msgpack ] with-string-reader <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>msgpack</span> <span class="nf">( </span><span class="nv">obj</span> <span class="nf">-- </span><span class="nv">string</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ write-msgpack ] with-string-writer <span class="k">;
</span></span></span></code></pre></div><p>Not too hard, was it!</p>
<p>The code for this (including some documentation and tests) is available
in the <a href="https://github.com/factor/factor/blob/master/extra/msgpack/msgpack.factor">development
version</a>
of Factor.</p>
ChuckNorris is a Color
https://re.factorcode.org/2013/11/chucknorris-is-a-color.html
Sat, 09 Nov 2013 16:42:00 -0800https://re.factorcode.org/2013/11/chucknorris-is-a-color.html<p>I just came across a fun question asking <a href="https://stackoverflow.com/questions/8318911/why-does-html-think-chucknorris-is-a-color">why does HTML think
‘chucknorris’ is a
color</a>
on <a href="https://stackoverflow.com">StackOverflow</a>. This is called “Flex Hex”
and is described in detail in a <a href="https://scrappy-do.blogspot.com/2004/08/little-rant-about-microsoft-internet.html">little rant about Microsoft Internet
Explorer’s color
parsing</a>.</p>
<p>It turns out these are all valid colors:</p>
<table>
<tbody>
<tr class="odd">
<td style="text-align: center;" bgcolor="chucknorris" width="150">chucknorris</td>
<td style="text-align: center;" bgcolor="sick" width="150">sick</td>
<td style="text-align: center;" bgcolor="crap" width="150">crap</td>
</tr>
</tbody>
</table>
<p>I thought it would be fun to support parsing colors in this (arguably
broken) format in <a href="https://factorcode.org">Factor</a>. The algorithm
basically breaks down into three parts:</p>
<p>1. Convert non-hexadecimal digits to zero.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">hex-only</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>hex-digit? [ <span class="nb">drop </span><span class="sc">CHAR: 0 </span>] <span class="nb">unless </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>2. Group into three equal groups, padding on the right with zero if necessary.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pad-length</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">length dup </span><span class="m">3 </span><span class="nb">mod </span>[ <span class="m">3 </span><span class="nb">swap - + </span>] <span class="nb">unless-zero </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">three-groups</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">array</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>pad-length [ <span class="sc">CHAR: 0 </span><span class="nb">pad-tail </span>] [ <span class="m">3 </span><span class="nb">/ </span>group ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>3. Convert each segment into a two-digit hexadecimal value, shortening each
segment first to eight chars <em>from the right</em>, padding <em>on the left</em> if only
one character.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">hex-rgb</span> <span class="nf">( </span><span class="nv">array</span> <span class="nf">-- </span><span class="nv">array'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="m">8 </span><span class="nb">short tail*
</span></span></span><span class="line"><span class="cl"> <span class="m">2 </span><span class="nb">short head
</span></span></span><span class="line"><span class="cl"> <span class="m">2 </span><span class="sc">CHAR: 0 </span><span class="nb">pad-head
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>Putting that together, we have this word to parse “flex hex” colors
(removing hash-marks from the left if present):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">flex-hex</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">hex</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"#"</span> ?head <span class="nb">drop </span>hex-only three-groups hex-rgb <span class="s">""</span> <span class="nb">join </span><span class="k">;
</span></span></span></code></pre></div><p>And, of course, some tests to verify that we handle lots of different
cases:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="s">"00b000"</span> } [ <span class="s">"#zqbttv"</span> flex-hex ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="s">"0f0000"</span> } [ <span class="s">"f"</span> flex-hex ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"000f00"</span> } [ <span class="s">"0f"</span> flex-hex ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"000f00"</span> } [ <span class="s">"0f0"</span> flex-hex ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"0f0f00"</span> } [ <span class="s">"0f0f"</span> flex-hex ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"0ff000"</span> } [ <span class="s">"0f0f0f0"</span> flex-hex ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="s">"ad0e0e"</span> } [ <span class="s">"adamlevine"</span> flex-hex ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"000000"</span> } [ <span class="s">"MrT"</span> flex-hex ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"00c000"</span> } [ <span class="s">"sick"</span> flex-hex ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"c0a000"</span> } [ <span class="s">"crap"</span> flex-hex ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"c00000"</span> } [ <span class="s">"chucknorris"</span> flex-hex ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="s">"6ecde0"</span> } [
</span></span><span class="line"><span class="cl"> <span class="s">"6db6ec49efd278cd0bc92d1e5e072d68"</span> flex-hex
</span></span><span class="line"><span class="cl">] unit-test
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/colors/flex-hex/flex-hex.factor">GitHub</a>.</p>
Fast Factorial
https://re.factorcode.org/2013/11/fast-factorial.html
Mon, 04 Nov 2013 15:31:00 -0800https://re.factorcode.org/2013/11/fast-factorial.html<p>You might remember that I implemented a <a href="https://re.factorcode.org/2013/04/factorial.html">bunch of factorial
words</a> several
months ago.</p>
<p>Recently, I was looking at Python’s latest
<a href="https://hg.python.org/cpython/file/6fdbb81b4020/Modules/mathmodule.c#l1218">mathmodule.c</a>,
and noticed an implementation of a “divide-and-conquer factorial
algorithm”. The algorithm is based on one described by Peter Luschny as
the <a href="https://www.luschny.de/math/factorial/binarysplitfact.html">binary-split formula of the factorial of
n</a>. It seemed
like a fun thing to implement and benchmark in
<a href="https://factorcode.org">Factor</a>.</p>
<p>It turns out this was <a href="https://bugs.python.org/issue8692">one of the
improvements</a> made during the Python
3.2 development cycle. Calculating the 50,000th factorial takes <strong>0.724
seconds</strong> with Python 2.7.5 and only <strong>0.064 seconds</strong> with Python
3.3.2 - a nice improvement!</p>
<h3 id="slow-factorial">slow-factorial</h3>
<p>The most basic factorial would just multiply the numbers from <code>1</code> to
<code>n</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">slow-factorial</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">n!</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">1 </span><span class="nb">> </span>[ [1..b] <span class="m">1 </span>[ <span class="nb">* </span>] <span class="nb">reduce </span>] [ <span class="nb">drop </span><span class="m">1 </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Calculating the 50,000 factorial this way takes <strong>0.915 seconds</strong>.</p>
<h3 id="factorial">factorial</h3>
<p>It turns out that doing that causes a lot of arbitrary-precision
<a href="https://docs.factorcode.org/content/word-bignum,math.html">bignum</a>
multiplication. We can instead use
<a href="https://docs.factorcode.org/content/word-binary-reduce,sequences.html">binary-reduce</a>
(used by the standard library
<a href="https://docs.factorcode.org/content/word-product,sequences.html">product</a>
and <a href="https://docs.factorcode.org/content/word-sum,sequences.html">sum</a>
words), which is a generic algorithm that recursively multiplies groups
of numbers which allows for more
<a href="https://docs.factorcode.org/content/word-fixnum,math.html">fixnum</a>
multiplications.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">factorial</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">n!</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">1 </span><span class="nb">> </span>[ [1..b] <span class="m">1 </span>[ <span class="nb">* </span>] <span class="nb">binary-reduce </span>] [ <span class="nb">drop </span><span class="m">1 </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Calculating the 50,000 factorial takes <strong>0.217 seconds</strong>.</p>
<h3 id="fast-factorial">fast-factorial</h3>
<p>We can implement Peter Luschny’s algorithm, keeping the structure very
close to his sample code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">part-product</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">m</span> <span class="nf">-- </span><span class="nv">x</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ m n <span class="m">1 </span><span class="nb">+ <= </span>] [ n ] }
</span></span><span class="line"><span class="cl"> { [ m n <span class="m">2 </span><span class="nb">+ = </span>] [ n m <span class="nb">* </span>] }
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> n m <span class="nb">+ </span><span class="m">2 </span><span class="nb">/i dup even? </span>[ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">when </span>:> k
</span></span><span class="line"><span class="cl"> n k k <span class="m">2 </span><span class="nb">+ </span>m [ part-product ] <span class="nb">2bi@ *
</span></span></span><span class="line"><span class="cl"> ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">factorial-loop</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">p</span> <span class="nv">r</span> <span class="nf">-- </span><span class="nv">p'</span> <span class="nv">r'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> n <span class="m">2 </span><span class="nb">> </span>[
</span></span><span class="line"><span class="cl"> n <span class="m">2 </span><span class="nb">/i </span>:> mid
</span></span><span class="line"><span class="cl"> mid p r factorial-loop
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> mid <span class="m">1 </span><span class="nb">+ </span>mid <span class="m">1 </span><span class="nb">bitand +
</span></span></span><span class="line"><span class="cl"> n <span class="m">1 </span><span class="nb">- </span>n <span class="m">1 </span><span class="nb">bitand + </span>part-product <span class="nb">*
</span></span></span><span class="line"><span class="cl"> ] [ <span class="nb">dupd * </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> ] [ p r ] <span class="nb">if </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fast-factorial</span> <span class="nf">( </span><span class="nv">x</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">1 1 </span>factorial-loop <span class="nb">nip </span>] [ <span class="nb">dup </span>bit-count <span class="nb">- </span>] <span class="nb">bi shift </span><span class="k">;
</span></span></span></code></pre></div><p>Calculating the 50,000 factorial now only takes <strong>0.171 seconds</strong>!</p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/fast-factorial/fast-factorial.factor">GitHub</a>.</p>
<p><em>Note: there are <a href="https://www.luschny.de/math/factorial/index.html">other
algorithms</a> that are
likely faster, but for this purpose I wanted to implement a similar
algorithm to Python. Their implementation has a few modifications
including a tight loop for multiplications that fit into an unsigned
long and is over 150 lines of code.</em></p>
Text Summary
https://re.factorcode.org/2013/10/text-summary.html
Mon, 28 Oct 2013 19:41:00 -0700https://re.factorcode.org/2013/10/text-summary.html<p>Several months ago, Shlomi Babluki wrote about how to <a href="https://thetokenizer.com/2013/04/28/build-your-own-summary-tool/">build your own
[text] summary
tool</a>
in response to <a href="https://yahoo.com">Yahoo</a>’s purchase of
<a href="https://summly.com/">Summly</a>. It turns out to be a nice introduction to
basic <a href="https://en.wikipedia.org/wiki/Automatic_summarization">automatic
summarization</a>
techniques. You can download and read Shlomi’s <a href="https://gist.github.com/shlomibabluki/5473521">Python
implementation</a> on
GitHub.</p>
<p>Below, I show the simple text summary implemented in
<a href="https://factorcode.org">Factor</a>.</p>
<h3 id="tokenization">tokenization</h3>
<p>We need a way to split text into sentences. I chose a simple regular
expression, but there are <a href="https://stackoverflow.com/search?q=split+text+into+sentences">many other
approaches</a>
which might be useful for handling more complicated sentence patterns,
including the <a href="https://nltk.googlecode.com/svn/trunk/doc/api/nltk.tokenize.punkt-pysrc.html">Punkt sentence
tokenizer</a>
used by <a href="https://nltk.org/">NLTK</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">split-sentences</span> <span class="nf">( </span><span class="nv">content</span> <span class="nf">-- </span><span class="nv">sentences</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ blank? ] split-when <span class="nb">harvest </span><span class="s">" "</span> <span class="nb">join
</span></span></span><span class="line"><span class="cl"> R/ (?<=[.!?]|[.!?][\'"])\s+/ re-split <span class="k">;
</span></span></span></code></pre></div><p>We also need to split text into paragraphs. We simply look for an empty
line between blocks of text, and then split each block of text into
sentences.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">split-paragraphs</span> <span class="nf">( </span><span class="nv">content</span> <span class="nf">-- </span><span class="nv">paragraphs</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"\n\n"</span> split-subseq [ split-sentences ] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><h3 id="ranking">ranking</h3>
<p>We score two sentences by a simple formula that counts the number of
words they share divided by the total number of words in both sentences.
This part could be improved in a variety of ways such as removing <a href="https://en.wikipedia.org/wiki/Stop_words">stop
words</a> or using
<a href="https://en.wikipedia.org/wiki/Stemming">stemming</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">sentence-score</span> <span class="nf">( </span><span class="nv">sentence1</span> <span class="nv">sentence2</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ blank? ] split-when ] <span class="nb">bi@
</span></span></span><span class="line"><span class="cl"> <span class="nb">2dup </span>[ <span class="nb">length </span>] <span class="nb">bi@ + </span>[ <span class="nb">2drop </span><span class="m">0 </span>] [
</span></span><span class="line"><span class="cl"> [ intersect <span class="nb">length </span>] [ <span class="m">2 </span><span class="nb">/ </span>] <span class="nb">bi* /
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-zero </span><span class="k">;
</span></span></span></code></pre></div><p>We are going to build a map of each sentence to its total score against
the other sentences. We iterate
<a href="https://docs.factorcode.org/content/word-all-combinations,math.combinatorics.html">all-combinations</a>,
calculating the sentence scores and adding it to the score for both
sentences.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">sentence-ranks</span> <span class="nf">( </span><span class="nv">paragraphs</span> <span class="nf">-- </span><span class="nv">ranks</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">concat </span><span class="m">2 </span>all-combinations H{ } <span class="nb">clone </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>'[
</span></span><span class="line"><span class="cl"> [ sentence-score ] <span class="nb">2keep
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">nip </span>_ <span class="nb">at+ </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>_ <span class="nb">at+ </span>] <span class="nb">3bi
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">assoc-each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div><p>We use the sentence rankings to choose a best sentence for each
paragraph (ignoring any paragraph with only one sentence):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">best-sentence</span> <span class="nf">( </span><span class="nv">paragraph</span> <span class="nv">ranks</span> <span class="nf">-- </span><span class="nv">sentence</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">over length </span><span class="m">2 </span><span class="nb">< </span>[ <span class="nb">2drop </span><span class="s">""</span> ] [
</span></span><span class="line"><span class="cl"> '[ _ <span class="nb">at </span><span class="m">0 </span><span class="nb">or </span>] <span class="nb">supremum-by
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><h3 id="summarization">summarization</h3>
<p>Calculating the summary is as simple as splitting the text into
paragraphs, ranking sentences by their total scores, and then using that
ranking to choose the best sentence for each paragraph:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">summary</span> <span class="nf">( </span><span class="nv">content</span> <span class="nf">-- </span><span class="nv">summary</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> split-paragraphs <span class="nb">dup </span>sentence-ranks
</span></span><span class="line"><span class="cl"> '[ _ best-sentence ] <span class="nb">map harvest </span><span class="k">;
</span></span></span></code></pre></div><p>And for convenience, we can make a word that builds a summary, wraps
each paragraph to 72 characters, and prints it out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">summary.</span> <span class="nf">( </span><span class="nv">content</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> summary [ <span class="s">""</span> <span class="nb">like </span><span class="m">72 </span>wrap-string <span class="nb">print nl </span>] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>For fun, we can try it out, using the
<a href="https://github.com/factor/factor/blob/master/extra/wikipedia/wikipedia.factor">wikipedia</a>
to summarize the already “simple” article for
<a href="https://simple.wikipedia.org/wiki/Programming">Programming</a> (removing
the links to footnotes that mess up our naive <code>split-sentence</code>
algorithm):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"simple"</span> [
</span></span><span class="line"><span class="cl"> [ <span class="s">"Programming"</span> article. ] with-string-writer
</span></span><span class="line"><span class="cl"> R/ \[\d+\]/ <span class="s">""</span> re-replace summary.
</span></span><span class="line"><span class="cl"> ] with-language
</span></span><span class="line"><span class="cl">These instructions come in different languages; they are called
</span></span><span class="line"><span class="cl">programming languages.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">A program is a <span class="nb">set of </span>instructions for the computer to follow.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Once a program has been compiled, the instructions in <span class="s">"machine form"</span>
</span></span><span class="line"><span class="cl">are written into a file that contains a series <span class="nb">of </span>numbers that the
</span></span><span class="line"><span class="cl">computer can understand.
</span></span></code></pre></div><p>This code for this is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/text-summary/text-summary.factor">GitHub</a>.</p>
Text-to-Speech
https://re.factorcode.org/2013/10/text-to-speech.html
Sat, 19 Oct 2013 17:14:00 -0700https://re.factorcode.org/2013/10/text-to-speech.html<p>To those of us from a certain decade of computing, the phrase
“text-to-speech” reminds us favorably of <a href="https://en.wikipedia.org/wiki/Dr._Sbaitso">Dr.
Sbaitso</a>. A fun take on that
reminiscence is an article titled “<a href="https://www.x-entertainment.com/articles/0952/">Dr. Sbaitso was my only
friend</a>”.</p>
<p>It would be neat if we could have access to text-to-speech functionality
from <a href="https://factorcode.org">Factor</a>. And it would be especially neat if
it was cross-platform!</p>
<p>We’ll start by defining a <code>speak-text</code> word that is a generic word that
dispatches on the value of the <code>os</code> object, so we can provide
platform-specific implementations:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">HOOK:</span> <span class="nf">speak-text</span> <span class="nf">os</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- )
</span></span></span></code></pre></div><h3 id="mac-os">Mac OS</h3>
<p>On Mac OS, we cheat a bit and just call out to the
<a href="https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/say.1.html">say</a>
command-line tool built into Mac OS:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">macosx</span> <span class="nf">speak-text</span>
</span></span><span class="line"><span class="cl"> <span class="s">"say \"%s\""</span> sprintf try-process <span class="k">;
</span></span></span></code></pre></div><p>We just use the default voice set in System Preferences, but changing
the voice is just one of the many options available including adjusting
the number of words spoken per minute. For more information on Mac OS
support for speech, read the <a href="https://developer.apple.com/library/mac/documentation/userexperience/conceptual/SpeechSynthesisProgrammingGuide/Introduction/Introduction.html">Speech Synthesis Programming
Guide</a>.</p>
<h3 id="linux">Linux</h3>
<p>On Linux, text-to-speech is not builtin. Instead, I decided to use the
<a href="https://www.cstr.ed.ac.uk/projects/festival/">Festival Speech Synthesis
System</a>, which includes a
command-line tool that can be configured to speak text:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">linux</span> <span class="nf">speak-text</span>
</span></span><span class="line"><span class="cl"> <span class="s">"festival --tts"</span> utf8 [ <span class="nb">print </span>] with-process-writer <span class="k">;
</span></span></span></code></pre></div><p>In addition to this, you can find a whole host of other features in the
<a href="https://www.cstr.ed.ac.uk/projects/festival/manual/">Festival manual</a>.</p>
<h3 id="windows">Windows</h3>
<p>On Windows, it would probably be cool to bind to the <a href="https://en.wikipedia.org/wiki/Microsoft_Speech_API">Microsoft Speech
API</a>, but that seemed
a little bit harder than the quick-and-dirty approach I took.</p>
<p>Support required two commits to the main <a href="https://factorcode.org">Factor</a>
repository by Doug Coleman and myself:</p>
<ul>
<li><a href="https://github.com/factor/factor/commit/065338aee35de3ce261c344bc3050c897874f012">google.translate: adding
translate-tts</a> -
using <a href="https://translate.google.com/">Google Translate</a> to “speak”
text to an <a href="https://en.wikipedia.org/wiki/MP3">MP3</a></li>
<li><a href="https://github.com/factor/factor/commit/20faf95242fcafa85e7b62b85339c3d8f3687dec">windows.winmm: Add binding to play
mp3s</a> -
using WinMM to play MP3 files</li>
</ul>
<p>Those two commits allow us to implement <code>speak-text</code> on Windows:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">windows</span> <span class="nf">speak-text</span>
</span></span><span class="line"><span class="cl"> translate-tts open-command play-command close-command <span class="k">;
</span></span></span></code></pre></div><p>This code for this is available on my
<a href="https://github.com/mrjbq7/re-factor/tree/master/text-to-speech">GitHub</a>.</p>
Kahan Summation
https://re.factorcode.org/2013/10/kahan-summation.html
Mon, 14 Oct 2013 11:01:00 -0700https://re.factorcode.org/2013/10/kahan-summation.html<p>The <a href="https://en.wikipedia.org/wiki/Kahan_summation_algorithm">Kahan summation
algorithm</a> is a
“compensated summation” that “<em>significantly reduces the numerical error
in the total obtained by adding a sequence of finite precision floating
point numbers, compared to the obvious approach</em>”.</p>
<p><a href="https://github.com/adereth">Matt Adereth</a> wrote a <a href="https://adereth.github.io/blog/2013/10/04/add-it-up/">blog
post</a> a few days
ago demonstrated some advantages of using Kahan, which I show below
using <a href="https://factorcode.org">Factor</a>.</p>
<h3 id="the-problem">The Problem</h3>
<p>To demonstrate the problem, let’s consider the <a href="https://en.wikipedia.org/wiki/Harmonic_series_(mathematics)">harmonic
series</a>
(e.g., <code>1 + 1/2 + 1/3 + 1/4 + ...</code>) as a series of <a href="https://docs.factorcode.org/content/article-rationals.html">rational
numbers</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">harmonic-ratios</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [1..b] [ <span class="nb">recip </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>It gives the first <code>n</code> of the harmonic series:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">6 </span>harmonic-ratios <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">1 </span>1/2 1/3 1/4 1/5 1/6 }
</span></span></code></pre></div><p>We define a “simple sum” as just adding the numbers in order:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">simple-sum</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>[ <span class="nb">+ </span>] <span class="nb">reduce </span><span class="k">;
</span></span></span></code></pre></div><p>The simple sum of the first 10,000 harmonic ratios as a floating point
number is:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10,000 </span>harmonic-ratios simple-sum <span class="nb">>float </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">9.787606036044382
</span></span></span></code></pre></div><p>But, if we use floating points instead of ratios to represent the
harmonic numbers (e.g., <code>1.0 + 0.5 + 0.33333333 + 0.25 + ...</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">harmonic-floats</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> harmonic-ratios [ <span class="nb">>float </span>] <span class="nb">map! </span><span class="k">;
</span></span></span></code></pre></div><p>You can see that an error has been introduced (ending in “<code>48</code>” instead
of “<code>82</code>”):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10,000 </span>harmonic-floats simple-sum <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">9.787606036044348
</span></span></span></code></pre></div><p>If we reverse the sequence and add them from smallest to largest, there
is a slightly different error (ending in “<code>86</code>”):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10,000 </span>harmonic-floats <span class="nb">reverse </span>simple-sum <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">9.787606036044386
</span></span></span></code></pre></div><h3 id="the-solution">The Solution</h3>
<p>The pseudocode for the Kahan algorithm can be seen on the Wikipedia
page, using a running compensation for lost low-order bits:</p>
<pre tabindex="0"><code>function KahanSum(input)
var sum = 0.0
var c = 0.0
for i = 1 to input.length do
var y = input[i] - c
var t = sum + y
c = (t - sum) - y
sum = t
return sum
</code></pre><p>We could translate this directly using <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a> to
hold state, but instead I tried to make it a bit more
<a href="https://concatenative.org/">concatenative</a> (and possibly harder to read
in this case):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">kahan+</span> <span class="nf">( </span><span class="nv">c</span> <span class="nv">sum</span> <span class="nv">elt</span> <span class="nf">-- </span><span class="nv">c'</span> <span class="nv">sum'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">rot - 2dup + </span>[ <span class="nb">-rot </span>[ <span class="nb">- </span>] <span class="nb">bi@ </span>] <span class="nb">keep </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">kahan-sum</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">0.0 0.0 </span>] <span class="nb">dip </span>[ kahan+ ] <span class="nb">each nip </span><span class="k">;
</span></span></span></code></pre></div><p>You can see both forward and backward errors no longer exist:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10,000 </span>harmonic-floats kahan-sum <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">9.787606036044382
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10,000 </span>harmonic-floats <span class="nb">reverse </span>kahan-sum <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">9.787606036044382
</span></span></span></code></pre></div><p>Nifty! Thanks, Matt!</p>
Morse Palindromes?
https://re.factorcode.org/2013/10/morse-palindromes.html
Fri, 11 Oct 2013 15:50:00 -0700https://re.factorcode.org/2013/10/morse-palindromes.html<p>There was a fun post today on <a href="https://www.metafilter.com">Metafilter</a>
about the <a href="https://www.metafilter.com/132780/The-longest-palindrome-in-Morse-code-is-intransigence">longest palindrome in Morse
code</a>
being “intransigence” (along with other odd facts).</p>
<p>This jumped out to me for a few reasons:</p>
<ul>
<li>The <a href="https://docs.factorcode.org/content/article-first-program.html">first program</a>
that many newcomers to <a href="https://factorcode.org">Factor</a> create is a
“palindrome” detector.</li>
<li>We have a <a href="https://docs.factorcode.org/content/vocab-morse.html">morse</a>
vocabulary for calculating and playing morse codes.</li>
<li>I blogged about finding <a href="https://re.factorcode.org/2010/10/longest-palindrome.html">longest palindrome substrings</a>
awhile ago.</li>
</ul>
<p>So, maybe we can confirm that “intransigence” really is the longest
palindrome!</p>
<h3 id="palindrome">palindrome?</h3>
<p>The basic definition for a “palindrome” is a word that “reads the same
backward as forward”. Given that, it’s easy to build a first version
that checks this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">palindrome?</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">) </span><span class="nb">dup reverse = </span><span class="k">;
</span></span></span></code></pre></div><p>However, in our tutorial we suggest building a more robust version that
normalizes the input to handle palindromes such as “<em>A man, a plan, a
canal: Panama.</em>”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">normalize</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">) </span>[ Letter? ] <span class="nb">filter </span>>lower <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">palindrome?</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">) </span>normalize <span class="nb">dup reverse = </span><span class="k">;
</span></span></span></code></pre></div><h3 id="morse-palindrome">morse-palindrome?</h3>
<p>For our morse code palindrome detector, we need to <a href="https://docs.factorcode.org/content/word-morse__gt__%2Cmorse.html">convert our string
to morse
code</a>,
removing extra spaces that the morse code vocabulary adds between
letters, before checking for palindrome-ness:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">normalize-morse</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> normalize >morse [ blank? <span class="nb">not </span>] <span class="nb">filter </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">morse-palindrome?</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> normalize-morse <span class="nb">dup reverse = </span><span class="k">;
</span></span></span></code></pre></div><p>The longest word in the dictionary that is a morse palindrome is:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/usr/share/dict/words"</span> ascii file-lines
</span></span><span class="line"><span class="cl"> [ morse-palindrome? ] <span class="nb">filter longest </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"incalescence"</span>
</span></span></code></pre></div><p>Wait, that isn’t “intransigence”!</p>
<p>Well, no, but “incalescence” has the same number of letters (13) and
happens to be slightly longer in morse code. So maybe it’s a tie, or
maybe they should update their trivia!</p>
<p>In fact, there are several “longest” morse palindromes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/usr/share/dict/words"</span> ascii file-lines
</span></span><span class="line"><span class="cl"> [ morse-palindrome? ] <span class="nb">filter </span>all-longest <span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> <span class="s">"incalescence"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"intercentral"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"predestinate"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"predestitute"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"protectorate"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Raphaelesque"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"ultranatural"</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p><em>P.S., it looks like “Raphaelesque” might be the longest morse
palindrome by morse code length.</em></p>
<p><em>P.P.S., for some reason <code>/usr/share/dict/words</code> doesn’t contain the
word “intransigence”.</em></p>
AppleScript
https://re.factorcode.org/2013/10/applescript.html
Thu, 10 Oct 2013 16:24:00 -0700https://re.factorcode.org/2013/10/applescript.html<p>I find myself often developing on Mac OS. Given all the new
technologies and programming languages available, it is sometimes easy
to forget about <a href="https://en.wikipedia.org/wiki/AppleScript">AppleScript</a>,
a scripting language that has been available for over twenty years, and
reads and writes a bit like “plain English”. AppleScript can be run from
the command-line using the
<a href="https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/osascript.1.html">osascript</a>
tool, and edited and run using the “AppleScript Editor” application.</p>
<p>Wouldn’t it be cool if we could run AppleScripts from
<a href="https://factorcode.org">Factor</a>?</p>
<p>Using the <a href="https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/nsapplescript_Class/Reference/Reference.html">NSAppleScript Class
Reference</a>,
we can see that, basically, we need to create an <code>NSAppleScript</code>
initialized from a source string, and then execute the script:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">cocoa</span> <span class="nn">cocoa.application</span> <span class="nn">cocoa.classes</span> <span class="nn">kernel</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">run-apple-script</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ NSAppleScript -> alloc ] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> <NSString> -> initWithSource: -> autorelease
</span></span><span class="line"><span class="cl"> <span class="no">f </span>-> executeAndReturnError: <span class="nb">drop </span><span class="k">;
</span></span></span></code></pre></div><p>Right now we are ignoring the return values, but supposedly we could
figure out how to interpret the return value (which is a
<a href="https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSAppleEventDescriptor_Class/Reference/Reference.html#//apple_ref/doc/c_ref/NSAppleEventDescriptor">NSAppleEventDescriptor</a>)
and pass values back from the script to Factor.</p>
<p>You can see that it works by trying to display a simple dialog:</p>
<p>
<img src="https://re.factorcode.org/images/2013-10-10-applescript-apple-script.png" alt="" width="400" height="171" />
</p>
<p>If you’re curious to see what else you can do with AppleScript, check
out <a href="https://dougscripts.com/itunes/">Doug’s AppleScripts for iTunes</a> or
the <a href="https://macscripter.net/">MacScripter.net</a> forum.</p>
<p>This is available in the <a href="https://github.com/factor/factor">development
version</a> of Factor.</p>
Rock-paper-scissors
https://re.factorcode.org/2013/10/rock-paper-scissors.html
Sun, 06 Oct 2013 21:51:00 -0700https://re.factorcode.org/2013/10/rock-paper-scissors.html<p>The ever-popular game of
<a href="https://en.wikipedia.org/wiki/Rock-paper-scissors">rock-paper-scissors</a>
is a fun game to play with friends and, as it turns out, a fun game to
implement in <a href="https://factorcode.org">Factor</a>.</p>
<p>We have an open issue to finish some <a href="https://github.com/factor/factor/issues/935">planned changes to
multi-methods</a>. While
largely a syntax improvement, one of the challenges will be keeping the
simple case fast while providing for enhanced dispatch ability. As a way
of showing how the current syntax works, I thought I would implement the
“rock-paper-scissors” game.</p>
<p>First, we define each type of object in our game:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SINGLETONS:</span> <span class="nc">rock</span> <span class="nc">paper</span> <span class="nc">scissors</span> <span class="k">;
</span></span></span></code></pre></div><blockquote>
<p><em>Note: we are not playing
<a href="https://en.wikipedia.org/wiki/Rock-paper-scissors-lizard-Spock">rock-paper-scissors-lizard-spock</a>,
which is a bit more complicated and possibly a lot more fun.</em></p>
</blockquote>
<p>Next, we need to determine a winner using
<a href="https://docs.factorcode.org/content/vocab-multi-methods.html">multi-methods</a>.
You’ll notice that our generic method dispatches off the types of two
objects. We can use any type here, including <a href="https://docs.factorcode.org/content/article-predicates.html">predicate
classes</a>,
but can simply dispatch off the singletons we defined above:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">FROM:</span> <span class="nn">multi-methods</span> => <span class="nf">GENERIC:</span> <span class="nf">METHOD:</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">GENERIC:</span> <span class="nf">beats?</span> <span class="nf">( </span><span class="nv">obj1</span> <span class="nv">obj2</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">METHOD: beats? { scissors paper } <span class="nb">2drop </span><span class="no">t </span><span class="k">;
</span></span></span><span class="line"><span class="cl">METHOD: beats? { rock scissors } <span class="nb">2drop </span><span class="no">t </span><span class="k">;
</span></span></span><span class="line"><span class="cl">METHOD: beats? { paper rock } <span class="nb">2drop </span><span class="no">t </span><span class="k">;
</span></span></span><span class="line"><span class="cl">METHOD: beats? { <span class="nb">object object </span>} <span class="nb">2drop </span><span class="no">f </span><span class="k">;
</span></span></span></code></pre></div><p>Each play of the game will determine the outcome (win, lose, or tie) and
print a summary:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">play.</span> <span class="nf">( </span><span class="nv">obj1</span> <span class="nv">obj2</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ <span class="nb">2dup </span>beats? ] [ <span class="s">"WIN"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">2dup = </span>] [ <span class="s">"TIE"</span> ] }
</span></span><span class="line"><span class="cl"> [ <span class="s">"LOSE"</span> ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="s">"%s vs. %s: %s\n"</span> printf <span class="k">;
</span></span></span></code></pre></div><p>The computer will choose randomly amongst the possibilities:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">computer</span> <span class="nf">( -- </span><span class="nv">obj</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { rock paper scissors } random <span class="k">;
</span></span></span></code></pre></div><p>And for fun, we will define words to allow the user to play the computer
at the listener:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">rock</span> <span class="nf">( -- ) </span><span class="no">\ rock</span> computer play. <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">paper</span> <span class="nf">( -- ) </span><span class="no">\ paper</span> computer play. <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">scissors</span> <span class="nf">( -- ) </span><span class="no">\ scissors</span> computer play. <span class="k">;
</span></span></span></code></pre></div><p>A sample output from a few games:</p>
<p>
<img src="https://re.factorcode.org/images/2013-10-06-rock-paper-scissors-rock-paper-scissors.png" alt="" width="213" height="216" />
</p>
<p>The code for this is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/rock-paper-scissors/rock-paper-scissors.factor">GitHub</a>.</p>
Color Support
https://re.factorcode.org/2013/10/color-support.html
Tue, 01 Oct 2013 13:53:00 -0700https://re.factorcode.org/2013/10/color-support.html<p>For a fairly long time, <a href="https://factorcode.org">Factor</a> has had support
for <a href="https://en.wikipedia.org/wiki/RGB">RGB</a>,
<a href="https://en.wikipedia.org/wiki/HSL_and_HSV">HSV</a>, and
<a href="https://en.wikipedia.org/wiki/Grayscale">grayscale</a> color spaces.</p>
<p>More recently, I got interested in colors, even having some fun by
building <a href="https://re.factorcode.org/2012/09/color.html">color tab
completion</a>. We have
now added support for <a href="https://en.wikipedia.org/wiki/CMYK">CMYK</a>,
<a href="https://en.wikipedia.org/wiki/HSL_and_HSV">HSL</a>,
<a href="https://en.wikipedia.org/wiki/RYB">RYB</a>,
<a href="https://en.wikipedia.org/wiki/YIQ">YIQ</a>, and
<a href="https://en.wikipedia.org/wiki/YUV">YUV</a> color spaces. All colors support
an alpha channel and conversion to and from RGB colors!</p>
<p>You could say two colors are equal if their RGB components are <em>very,
very, very</em> close:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">color=</span> <span class="nf">( </span><span class="nv">color1</span> <span class="nv">color2</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ >rgba-components <span class="nb">4array </span>] <span class="nb">bi@
</span></span></span><span class="line"><span class="cl"> [ <span class="m">0.00000000000001 </span>~ ] <span class="nb">2all? </span><span class="k">;
</span></span></span></code></pre></div><p>Using that, you can write a test to confirm that our round trip through
color spaces preserves the original color information:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="no">t </span>} [
</span></span><span class="line"><span class="cl"> COLOR: sky-blue
</span></span><span class="line"><span class="cl"> >hsva
</span></span><span class="line"><span class="cl"> >yiqa
</span></span><span class="line"><span class="cl"> >cmyka
</span></span><span class="line"><span class="cl"> >yuva
</span></span><span class="line"><span class="cl"> >hsla
</span></span><span class="line"><span class="cl"> >ryba
</span></span><span class="line"><span class="cl"> >rgba
</span></span><span class="line"><span class="cl"> COLOR: sky-blue color=
</span></span><span class="line"><span class="cl">] unit-test
</span></span></code></pre></div><p>This is now available in the <a href="https://github.com/factor/factor">development
version</a> of Factor!</p>
<p><em>Note: we also support basic <a href="https://docs.factorcode.org/content/vocab-colors.mix.html">color
mixing</a>, with
the ability to do linear gradients between colors.</em></p>
YouTube Downloader
https://re.factorcode.org/2013/09/youtube-downloader.html
Mon, 23 Sep 2013 12:40:00 -0700https://re.factorcode.org/2013/09/youtube-downloader.html<p><a href="https://youtube.com">YouTube</a> needs no introduction - it hosts many
videos and has a virtual firehose of new content uploaded every second
of every day. The primary interface is through a web browser with no
easy way to download content for offline viewing. Naturally, “YouTube
downloader” plugins have become a popular addition to most web browsers.</p>
<p>I thought it would be fun to implement a basic “YouTube downloader” in
<a href="https://factorcode.org">Factor</a>.</p>
<p>First, we start by obtaining information about a video using the YouTube
API, parsing the response as a query string:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">video-info-url</span> <span class="s">URL" https://www.youtube.com/get_video_info"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">get-video-info</span> <span class="nf">( </span><span class="nv">video-id</span> <span class="nf">-- </span><span class="nv">video-info</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> video-info-url <span class="nb">clone
</span></span></span><span class="line"><span class="cl"> <span class="m">3 </span><span class="s">"asv"</span> set-query-param
</span></span><span class="line"><span class="cl"> <span class="s">"detailpage"</span> <span class="s">"el"</span> set-query-param
</span></span><span class="line"><span class="cl"> <span class="s">"en_US"</span> <span class="s">"hl"</span> set-query-param
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span><span class="s">"video_id"</span> set-query-param
</span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>query>assoc <span class="k">;
</span></span></span></code></pre></div><p>Next, we can get a list of the available video formats:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">video-formats</span> <span class="nf">( </span><span class="nv">video-info</span> <span class="nf">-- </span><span class="nv">video-formats</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"url_encoded_fmt_stream_map"</span> <span class="nb">of
</span></span></span><span class="line"><span class="cl"> <span class="s">","</span> split [ query>assoc ] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>A particular video format includes a download URL and a signature that
needs to be attached to it to successfully download the video:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">video-download-url</span> <span class="nf">( </span><span class="nv">video-format</span> <span class="nf">-- </span><span class="nv">url</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"url"</span> <span class="nb">of </span>] [ <span class="s">"sig"</span> <span class="nb">of </span>] <span class="nb">bi </span><span class="s">"&signature="</span> <span class="nb">glue </span><span class="k">;
</span></span></span></code></pre></div><p>We are going to use the video title to create a filename, but first we
want to sanitize it by removing unprintable characters and a few
characters which might conflict with your filesystem and making it no
more than 200 characters long:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">sanitize</span> <span class="nf">( </span><span class="nv">title</span> <span class="nf">-- </span><span class="nv">title'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">0 31 </span>between? <span class="nb">not </span>] <span class="nb">filter
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"\"#$%'*,./:;<>?^|~\\"</span> <span class="nb">member? not </span>] <span class="nb">filter
</span></span></span><span class="line"><span class="cl"> <span class="m">200 </span><span class="nb">short head </span><span class="k">;
</span></span></span></code></pre></div><p>Finally, to download a video, we lookup its video info, find the first
<code>mp4</code> formatted video, convert it to a download URL, and then
<a href="https://docs.factorcode.org/content/word-download-to,http.client.html">download-to</a>
a file.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">download-video</span> <span class="nf">( </span><span class="nv">video-id</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> get-video-info [
</span></span><span class="line"><span class="cl"> video-formats [ <span class="s">"type"</span> <span class="nb">of </span><span class="s">"video/mp4"</span> <span class="nb">head? </span>] <span class="nb">find nip
</span></span></span><span class="line"><span class="cl"> video-download-url
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="s">"title"</span> <span class="nb">of </span>sanitize <span class="s">".mp4"</span> <span class="nb">append </span>download-to
</span></span><span class="line"><span class="cl"> ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>You can choose a directory to download it to using the
<a href="https://docs.factorcode.org/content/word-with-directory,io.directories.html">with-directory</a>
word. For example, downloading a video to your home directory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"~"</span> [ <span class="s">"G8LC8ES6ogw"</span> download-video ] with-directory
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/youtube/youtube.factor">GitHub</a>.</p>
ZeroMQ
https://re.factorcode.org/2013/09/zeromq.html
Wed, 18 Sep 2013 16:46:00 -0700https://re.factorcode.org/2013/09/zeromq.html<p><a href="https://zeromq.org">ZeroMQ</a> is a “high-level” socket library that
provides some middleware capabilities and markets itself as <em>“The
Simplest Way to Connect Pieces”</em>.</p>
<p>About a year ago, <a href="https://github.com/eungju">Eungju Park</a> created a
binding that was usable from <a href="https://factorcode.org">Factor</a>. He was
gracious to allow us to <a href="https://github.com/factor/factor/commit/c398b24a218dd66f85c2b455af75936a73fe4d96">merge his
vocabulary</a>
into the main repository. In the process, I cleaned up the API a little
bit and made sure it works with the latest version of ZeroMQ (currently
3.2.3).</p>
<p>As a quick tease, I thought I would show off a simple “echo” example.</p>
<h3 id="echo">echo</h3>
<p>Our “echo” server will bind to port 5000. It will loop forever, sending
any messages that it receives back to the client:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">echo-server</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <zmq-context> &dispose
</span></span><span class="line"><span class="cl"> ZMQ_REP <zmq-socket> &dispose
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"tcp://127.0.0.1:5000"</span> zmq-bind
</span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span><span class="m">0 </span>zmq-recv <span class="nb">dupd </span><span class="m">0 </span>zmq-send <span class="no">t </span>] <span class="nb">loop
</span></span></span><span class="line"><span class="cl"> <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> ] with-destructors <span class="k">;
</span></span></span></code></pre></div><p>Our “echo” client will connect to the server on port 5000. Each second,
we grab the current time (by calling
<a href="https://docs.factorcode.org/content/word-now,calendar.html">now</a>),
format it into as string and send it to the server, printing the message
being sent and the message that was received:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">echo-client</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <zmq-context> &dispose
</span></span><span class="line"><span class="cl"> ZMQ_REQ <zmq-socket> &dispose
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"tcp://127.0.0.1:5000"</span> zmq-connect
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> now present
</span></span><span class="line"><span class="cl"> [ <span class="s">"Sending "</span> <span class="nb">write print flush </span>]
</span></span><span class="line"><span class="cl"> [ >byte-array <span class="nb">dupd </span><span class="m">0 </span>zmq-send ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span>zmq-recv <span class="nb">>string
</span></span></span><span class="line"><span class="cl"> <span class="s">"Received "</span> <span class="nb">write print flush
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span>seconds sleep
</span></span><span class="line"><span class="cl"> <span class="no">t
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop
</span></span></span><span class="line"><span class="cl"> <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> ] with-destructors <span class="k">;
</span></span></span></code></pre></div><p>Perhaps not as simple as a <a href="https://re.factorcode.org/2012/08/echo-server.html">pure Factor echo
server</a>, but all
the nuts and bolts are working. Maybe in the future we could write a
<a href="https://thechangelog.com/zero-wrapper-around-omq-python-library/">wrapper for a
wrapper</a>
to simplify using ZeroMQ in common use-cases.</p>
<p>The
<a href="https://github.com/factor/factor/tree/master/extra/zeromq">zeromq</a>
vocabulary and several other
<a href="https://github.com/factor/factor/tree/master/extra/zeromq/examples">examples</a>
of its use are available in the latest development version of
<a href="https://factorcode.org">Factor</a>.</p>
<p><em>Note: the calls into the ZeroMQ are blocking in such a way that we have
to run the <code>echo-server</code> in one Factor process and the <code>echo-client</code> in
another Factor process. It would be nice to integrate it in such a way
this was not necessary.</em></p>
Verbal Expressions
https://re.factorcode.org/2013/09/verbal-expressions.html
Wed, 04 Sep 2013 19:57:00 -0700https://re.factorcode.org/2013/09/verbal-expressions.html<p>Recently, <a href="https://thechangelog.com">thechangelog.com</a> wrote a blog post
asking programmers to <a href="https://thechangelog.com/stop-writing-regular-expressions-express-them-with-verbal-expressions">stop writing regular
expressions</a>
and begin using “verbal expressions”.</p>
<p>Instead of writing this regular expression to parse a URL:</p>
<pre tabindex="0"><code>^(?:http)(?:s)?(?:\:\/\/)(?:www\.)?(?:[^\ ]*)$
</code></pre><p>You could be writing this verbal expression (in Javascript):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">tester</span> <span class="o">=</span> <span class="nx">VerEx</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">startOfLine</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">(</span> <span class="s2">"http"</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">maybe</span><span class="p">(</span> <span class="s2">"s"</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">then</span><span class="p">(</span> <span class="s2">"://"</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">maybe</span><span class="p">(</span> <span class="s2">"www."</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">anythingBut</span><span class="p">(</span> <span class="s2">" "</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="nx">endOfLine</span><span class="p">();</span>
</span></span></code></pre></div><p>I’m not sure the second is more readable than the first once you get
comfortable with <a href="https://en.wikipedia.org/wiki/Regular_expression">regular
expressions</a>, but it
could be useful in some circumstances and particularly to programmers
that aren’t as familiar with the esoteric syntax that is frequently
required when matching text.</p>
<p>These “verbal” expressions seem to have become popular, with a <a href="https://verbalexpressions.github.io/">GitHub
organization</a> listing
implementations in <strong>19 languages</strong> so far.</p>
<p>With that in mind, let’s make a <a href="https://factorcode.org">Factor</a>
implementation!</p>
<h3 id="basics">Basics</h3>
<p>We need to create an object that will keep our state as we build our
regular expression, holding a prefix and suffix that surround a source
string as well as any modifiers that are requested (like
case-insensitivity):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">verbexp</span> <span class="nv">prefix</span> <span class="nv">source</span> <span class="nv">suffix</span> <span class="nv">modifiers</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><verbexp></span> <span class="nf">( -- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">""</span> <span class="s">""</span> <span class="s">""</span> <span class="s">""</span> verbexp <span class="nb">boa </span><span class="k">; inline
</span></span></span></code></pre></div><p>Making a regular expression is as simple as combining the prefix,
source, and suffix, and creating a regular expression with the requested
modifiers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">>regexp</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nf">-- </span><span class="nv">regexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ prefix>> ] [ source>> ] [ suffix>> ] <span class="nb">tri 3append </span>]
</span></span><span class="line"><span class="cl"> [ modifiers>> ] <span class="nb">bi </span><optioned-regexp> <span class="k">; inline
</span></span></span></code></pre></div><p>For convenience, we could have a combinator that creates the verbal
expression, calls a quotation with it on the stack, then converts it to
a regular expression:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">build-regexp</span> <span class="nf">( </span><span class="nv">quot:</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">) -- </span><span class="nv">regexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ <verbexp> @ >regexp ] <span class="nb">call </span><span class="k">; inline
</span></span></span></code></pre></div><p>When we want to add to our expression, we just append it to the source:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">add</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nv">str</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ _ <span class="nb">append </span>] change-source <span class="k">;
</span></span></span></code></pre></div><p>Anything that is not a letter or a digit can be escaped with a
backslash:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">re-escape</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>{ [ Letter? ] [ digit? ] } 1||
</span></span><span class="line"><span class="cl"> [ CHAR: <span class="no">\ ,</span> ] <span class="nb">unless </span>,
</span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="s">""</span> make <span class="k">;
</span></span></span></code></pre></div><h3 id="methods">Methods</h3>
<p>We can specify “anything” or “anything but”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">anything</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"(?:.*)"</span> add <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">anything-but</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nv">value</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> re-escape <span class="s">"(?:[^"</span> <span class="s">"]*)"</span> <span class="nb">surround </span>add <span class="k">;
</span></span></span></code></pre></div><p>We can specify “something” and “something but”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">something</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"(?:.+)"</span> add <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">something-but</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nv">value</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> re-escape <span class="s">"(?:[^"</span> <span class="s">"]+)"</span> <span class="nb">surround </span>add <span class="k">;
</span></span></span></code></pre></div><p>We can specify looking for “start of line” or “end of line”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">start-of-line</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"^"</span> <span class="nb">append </span>] change-prefix <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">end-of-line</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"$"</span> <span class="nb">append </span>] change-suffix <span class="k">;
</span></span></span></code></pre></div><p>We can specify a value (“then”), or an optional value (“maybe”):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">then</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nv">value</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> re-escape <span class="s">"(?:"</span> <span class="s">")"</span> <span class="nb">surround </span>add <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">maybe</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nv">value</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> re-escape <span class="s">"(?:"</span> <span class="s">")?"</span> <span class="nb">surround </span>add <span class="k">;
</span></span></span></code></pre></div><p>We could specify “any of” a set of characters:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">any-of</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nv">value</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> re-escape <span class="s">"(?:["</span> <span class="s">"])"</span> <span class="nb">surround </span>add <span class="k">;
</span></span></span></code></pre></div><p>Or, maybe simply a line break, tab, word, or space:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">line-break</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"(?:(?:\\n)|(?:\\r\\n))"</span> add <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">tab</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">) </span><span class="s">"\\t"</span> add <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">word</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">) </span><span class="s">"\\w+"</span> add <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">space</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">) </span><span class="s">"\\s"</span> add <span class="k">;
</span></span></span></code></pre></div><p>Perhaps many of whatever has been specified so far:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">many</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="nb">dup ?last </span><span class="s">"*+"</span> <span class="nb">member? </span>[ <span class="s">"+"</span> <span class="nb">append </span>] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> ] change-source <span class="k">;
</span></span></span></code></pre></div><h3 id="modifiers">Modifiers</h3>
<p>Some helper words allow us to easily add and remove modifiers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">add-modifier</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nv">ch</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ _ <span class="nb">suffix </span>] change-modifiers <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">remove-modifier</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nv">ch</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ _ <span class="nb">swap remove </span>] change-modifiers <span class="k">;
</span></span></span></code></pre></div><p>Should we be case-insensitive or not:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">case-insensitive</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="sc">CHAR: i </span>add-modifier <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">case-sensitive</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="sc">CHAR: i </span>remove-modifier <span class="k">;
</span></span></span></code></pre></div><p>Should we search across multiple lines or not:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">multiline</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="sc">CHAR: m </span>add-modifier <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">singleline</span> <span class="nf">( </span><span class="nv">verbexp</span> <span class="nf">-- </span><span class="nv">verbexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="sc">CHAR: m </span>remove-modifier <span class="k">;
</span></span></span></code></pre></div><h3 id="testing">Testing</h3>
<p>We can try out our original example using the <a href="https://docs.factorcode.org/content/article-tools.test.html">unit test
framework</a>
to show that it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="no">t </span>} [
</span></span><span class="line"><span class="cl"> <span class="s">"https://www.google.com"</span> [
</span></span><span class="line"><span class="cl"> start-of-line
</span></span><span class="line"><span class="cl"> <span class="s">"http"</span> then
</span></span><span class="line"><span class="cl"> <span class="s">"s"</span> maybe
</span></span><span class="line"><span class="cl"> <span class="s">"://"</span> then
</span></span><span class="line"><span class="cl"> <span class="s">"www."</span> maybe
</span></span><span class="line"><span class="cl"> <span class="s">" "</span> anything-but
</span></span><span class="line"><span class="cl"> end-of-line
</span></span><span class="line"><span class="cl"> ] build-regexp matches?
</span></span><span class="line"><span class="cl">] unit-test
</span></span></code></pre></div><p>I’m not convinced this is an improvement. In the current specification
for “verbal” expressions, the language for expressing characteristics to
match against is relatively limited. Perhaps with some effort, this
could evolve into a more capable (but still readable) syntax.</p>
<p>In any event, the code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/verbal-expressions/verbal-expressions.factor">GitHub</a>.</p>
Domai.nr
https://re.factorcode.org/2013/08/domai-nr.html
Mon, 12 Aug 2013 20:45:00 -0700https://re.factorcode.org/2013/08/domai-nr.html<p>The <a href="https://domai.nr/">domai.nr</a> service is a handy way to find domains
that make nice short URLs like <strong>debu.gs</strong>, <strong>nick.is</strong>, and
<strong>craftstud.io</strong>.</p>
<p>I had a need awhile back to access this web service from a script and
query a bunch of domains all at the same time, so I thought it would be
nice to have a wrapper in <a href="https://factorcode.org">Factor</a>.</p>
<p>The domai.nr service has a JSON API for performing searches, that we can
build URLs for:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">domainr-url</span> <span class="nf">( </span><span class="nv">query</span> <span class="nf">-- </span><span class="nv">url</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">URL" https://domai.nr/api/json/search"</span>
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span><span class="s">"q"</span> set-query-param <span class="k">;
</span></span></span></code></pre></div><p>Each result has these fields that are returned:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">result</span> <span class="nv">domain</span> <span class="nv">host</span> <span class="nv">path</span> <span class="nv">subdomain</span> <span class="nv">availability</span>
</span></span><span class="line"><span class="cl"><span class="nv">register_url</span> <span class="k">;
</span></span></span></code></pre></div><p>It is pretty simple to map a JSON request to these <code>result</code> tuples:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">domainr</span> <span class="nf">( </span><span class="nv">query</span> <span class="nf">-- </span><span class="nv">data</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> domainr-url http-get <span class="nb">nip </span>json> <span class="s">"results"</span> <span class="nb">of
</span></span></span><span class="line"><span class="cl"> [ result from-slots ] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>Finally, we can perform a query and print the output nicely showing
which domains or URLs are available:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">domainr.</span> <span class="nf">( </span><span class="nv">query</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> domainr [
</span></span><span class="line"><span class="cl"> [ subdomain>> ] [ path>> ] [ availability>> ] <span class="nb">tri
</span></span></span><span class="line"><span class="cl"> <span class="s">"%s%s - %s\n"</span> printf
</span></span><span class="line"><span class="cl"> ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>Running this query for “<em>factorcode</em>” looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"factorcode"</span> domainr.
</span></span><span class="line"><span class="cl">factorcode.com <span class="nb">- </span>available
</span></span><span class="line"><span class="cl">factorcode.net <span class="nb">- </span>available
</span></span><span class="line"><span class="cl">factorcode.org <span class="nb">- </span>taken
</span></span><span class="line"><span class="cl">factorcode.co <span class="nb">- </span>available
</span></span><span class="line"><span class="cl">factor.co/de <span class="nb">- </span>taken
</span></span><span class="line"><span class="cl">factorcode.io <span class="nb">- </span>available
</span></span><span class="line"><span class="cl">factorco.de <span class="nb">- </span>available
</span></span><span class="line"><span class="cl">factorc.de <span class="nb">- </span>available
</span></span><span class="line"><span class="cl">fac.to/rcode <span class="nb">- </span>available
</span></span><span class="line"><span class="cl">f.actor/code <span class="nb">- </span>unavailable
</span></span><span class="line"><span class="cl">f.ac/torcode <span class="nb">- </span>unavailable
</span></span><span class="line"><span class="cl">fctrc.de <span class="nb">- </span>available
</span></span><span class="line"><span class="cl">fctr.cd/e <span class="nb">- </span>available
</span></span><span class="line"><span class="cl">fc.tr/cde <span class="nb">- </span>unavailable
</span></span></code></pre></div><p>Well, if we wanted to make our URL something like <strong>factorco.de</strong> we
could, but I’m not sure that’s worth it. Still, a neat way to look for
interesting URLs and available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/domainr/domainr.factor">GitHub</a>.</p>
uniq
https://re.factorcode.org/2013/08/uniq.html
Tue, 06 Aug 2013 16:40:00 -0700https://re.factorcode.org/2013/08/uniq.html<p>I’ve used <a href="https://factorcode.org">Factor</a> to build several common unix
programs including
<a href="https://re.factorcode.org/2012/02/copy.html">copy</a>,
<a href="https://re.factorcode.org/2010/08/building-cat.html">cat</a>,
<a href="https://re.factorcode.org/2010/09/fortune-telling.html">fortune</a>,
<a href="https://re.factorcode.org/2011/11/wc-l.html">wc</a>,
<a href="https://re.factorcode.org/2013/04/move.html">move</a>, and
<a href="https://github.com/mrjbq7/re-factor/tree/master/unix-tools">others</a>.</p>
<p>Today, I wanted to show how to build the <code>uniq</code> program. If we look at
the <a href="https://linux.die.net/man/1/uniq">man page</a>, we can see that it is
used to:</p>
<blockquote>
<p><em>Filter adjacent matching lines from <code>INPUT</code> (or standard input),
writing to <code>OUTPUT</code> (or standard output).</em></p>
</blockquote>
<p>Using the
<a href="https://docs.factorcode.org/content/word-each-line,io.html">each-line</a>
combinator to output each line of text that is not identical to the
previous line (using <code>f</code> to force the first line to always be printed):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">uniq-lines</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">2dup = </span>[ <span class="nb">dup print </span>] <span class="nb">unless nip
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-line drop </span><span class="k">;
</span></span></span></code></pre></div><p>The input is optionally specified, so need a <code>uniq-file</code> word that will
“uniq” the lines of a file, or read directly from the current
input-stream (which will be standard input when run from the command
line):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">uniq-file</span> <span class="nf">( </span><span class="nv">path/f</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> utf8 [ uniq-lines ] with-file-reader
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> uniq-lines
</span></span><span class="line"><span class="cl"> ] <span class="nb">if* </span><span class="k">;
</span></span></span></code></pre></div><p>Finally, our “main method” checks the command-line, writing our output
to a file if a second command-line argument is provided:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">run-uniq</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> command-line <span class="nb">get </span>[ <span class="nb">?first </span>] [ <span class="nb">?second </span>] <span class="nb">bi </span>[
</span></span><span class="line"><span class="cl"> utf8 [ uniq-file ] with-file-writer
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> uniq-file
</span></span><span class="line"><span class="cl"> ] <span class="nb">if* </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">run-uniq</span>
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/unix-tools/uniq/uniq.factor">GitHub</a>.</p>
METAR
https://re.factorcode.org/2013/07/metar.html
Sat, 27 Jul 2013 12:25:00 -0700https://re.factorcode.org/2013/07/metar.html<p><a href="https://en.wikipedia.org/wiki/METAR">METAR</a> is a format for reporting
weather information. Often used by pilots and meteorologists, METAR
might be considered the most popular format in the world for sharing
weather data. There are METAR parsers in Python
(<a href="https://www.schwarzvogel.de/software-pymetar.shtml">PyMETAR</a> and
<a href="https://pypi.python.org/pypi/metar/">python-metar</a>), Ruby
(<a href="https://github.com/joeyates/metar-parser">metar-parser</a>), Perl
(<a href="https://metaf2xml.sourceforge.net/">metaf2xml</a> and
<a href="https://idefix.net/~koos/perl/Geo-METAR/">Geo::METAR</a>), Java
(<a href="https://jweather.sourceforge.net/">jweather</a>), and probably most other
programming languages.</p>
<p>Today, we are going to build a METAR parser in
<a href="https://factorcode.org">Factor</a>!</p>
<blockquote>
<p><em>Note: If you are curious, a detailed <a href="https://www.ofcm.gov/fmh-1/pdf/L-CH12.pdf">METAR Coding
Standards</a> is available as
part of the <a href="https://www.ofcm.gov/fmh-1/fmh1.htm">Federal Meteorological Handbook No. 1 - Surface Weather
Observations and Reports</a>.</em></p>
</blockquote>
<h3 id="example">Example</h3>
<p>An example report for <a href="https://en.wikipedia.org/wiki/John_F._Kennedy_International_Airport">JFK
Airport</a>
in New York looks like this:</p>
<pre tabindex="0"><code>KJFK 262351Z 19011KT 10SM -RA SCT060 BKN250 23/18 A3002 RMK AO2
SLP166 T02280183 10289 20228 51004
</code></pre><p>The “readable” output of our METAR parser translates that to:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"KJFK"</span> metar.
</span></span><span class="line"><span class="cl">Station KJFK
</span></span><span class="line"><span class="cl">Timestamp Fri, <span class="m">26 </span>Jul <span class="m">2013 </span>23:51:00 GMT
</span></span><span class="line"><span class="cl">Wind from S (190°) <span class="nb">at </span><span class="m">11 </span>knots
</span></span><span class="line"><span class="cl">Visibility <span class="m">10 </span>statute miles
</span></span><span class="line"><span class="cl">Weather light rain
</span></span><span class="line"><span class="cl">Sky <span class="nb">condition </span>scattered <span class="nb">at </span><span class="m">6000 </span>ft, broken <span class="nb">at </span><span class="m">25000 </span>ft
</span></span><span class="line"><span class="cl">Temperature <span class="m">23 </span>°C
</span></span><span class="line"><span class="cl">Dew point <span class="m">18 </span>°C
</span></span><span class="line"><span class="cl">Altimeter <span class="m">30.02 </span>Hg
</span></span><span class="line"><span class="cl">Remarks AO2 SLP166 T02280183 <span class="m">10289 20228 51004
</span></span></span></code></pre></div><p>The remarks section includes additional weather observations and other
information. For now, we will skip parsing the remarks and just begin
with the standard fields contained in the body of the METAR report.</p>
<p>Each field will have a regular expression that can be used to recognize
its presence in the report.</p>
<h3 id="station">Station</h3>
<pre tabindex="0"><code>KJFK 262351Z 19011KT 10SM -RA SCT060 BKN250 23/18 A3002 RMK AO2
SLP166 T02280183 10289 20228 51004
</code></pre><p>Each station is identified by a 4-letter station identifier:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">re-station</span> R! \w{4}!
</span></span></code></pre></div><p>Given a station identifier, we can lookup its current METAR report
directly from the NOAA (National Oceanic and Atmospheric
Administration):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">http-weather</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">result</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://weather.noaa.gov/"</span> <span class="nb">prepend </span>http-get <span class="nb">nip </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">metar</span> <span class="nf">( </span><span class="nv">station</span> <span class="nf">-- </span><span class="nv">metar</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"pub/data/observations/metar/stations/%s.TXT"</span>
</span></span><span class="line"><span class="cl"> sprintf http-weather <span class="k">;
</span></span></span></code></pre></div><h3 id="timestamp">Timestamp</h3>
<pre tabindex="0"><code>KJFK 262351Z 19011KT 10SM -RA SCT060 BKN250 23/18 A3002 RMK AO2
SLP166 T02280183 10289 20228 51004
</code></pre><p>The timestamp is a date and time of the report containing two-digit
fields for day, hour, and minute, and the letter “<code>Z</code>” indicating
<a href="https://en.wikipedia.org/wiki/Coordinated_Universal_Time">UTC</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">re-timestamp</span> R! \d{2}\d{2}\d{2}Z!
</span></span></code></pre></div><p>We will assume the current year and month when parsing it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-timestamp</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ now [ year>> ] [ month>> ] <span class="nb">bi </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> <span class="m">2 </span><span class="nb">cut </span><span class="m">2 </span><span class="nb">cut </span><span class="m">2 </span><span class="nb">cut drop </span>[ string>number ] <span class="nb">tri@
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>instant <timestamp> timestamp>rfc822 <span class="k">;
</span></span></span></code></pre></div><blockquote>
<p><em>Note: This is not quite robust on the first day of the month when
reading METAR reports that were generated the day before…</em></p>
</blockquote>
<h3 id="wind">Wind</h3>
<pre tabindex="0"><code>KJFK 262351Z 19011KT 10SM -RA SCT060 BKN250 23/18 A3002 RMK AO2
SLP166 T02280183 10289 20228 51004
</code></pre><p>The first element of wind is the direction (specified as degrees, like
on a compass) and speed (specified in knots). The wind speed can
optionally include a higher speed if the location is experiencing wind
gusts:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">re-wind</span> R! (VRB|\d{3})\d{2,3}(G\d{2,3})?KT!
</span></span></code></pre></div><p>The second element is specified if the wind is variable, coming from a
range of directions, it is specified as one three-digit compass
direction followed by a “<code>V</code>” and another three-digit compass direction:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">re-wind-variable</span> R! \d{3}V\d{3}!
</span></span></code></pre></div><p>To help our users, we will first build a word to convert a compass
direction to the nearest “human” direction (e.g., north, east, south,
west):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">compass-directions</span> H{
</span></span><span class="line"><span class="cl"> { <span class="m">0.0 </span><span class="s">"N"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">45.0 </span><span class="s">"NE"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">90.0 </span><span class="s">"E"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">135.0 </span><span class="s">"SE"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">180.0 </span><span class="s">"S"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">225.0 </span><span class="s">"SW"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">270.0 </span><span class="s">"W"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">315.0 </span><span class="s">"NW"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">360.0 </span><span class="s">"N"</span> }
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">direction>compass</span> <span class="nf">( </span><span class="nv">direction</span> <span class="nf">-- </span><span class="nv">compass</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">45.0 </span>round-to-step compass-directions <span class="nb">at </span><span class="k">;
</span></span></span></code></pre></div><p>Either the direction will be variable (<code>VRB</code>), or from a particular
human and compass direction:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-direction</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"VRB"</span> <span class="nb">= </span>[ <span class="nb">drop </span><span class="s">"variable"</span> ] [
</span></span><span class="line"><span class="cl"> string>number [ direction>compass ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> <span class="s">"from %s (%s°)"</span> sprintf
</span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>That’s everything we need to parse the basic wind direction and speed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-wind</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"00000KT"</span> <span class="nb">= </span>[ <span class="nb">drop </span><span class="s">"calm"</span> ] [
</span></span><span class="line"><span class="cl"> <span class="m">3 </span><span class="nb">cut </span><span class="s">"KT"</span> ?tail <span class="nb">drop </span><span class="s">"G"</span> split1
</span></span><span class="line"><span class="cl"> [ parse-direction ] [ string>number ] [ string>number ] <span class="nb">tri*
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"%s at %s knots with gusts to %s knots"</span> sprintf ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"%s at %s knots"</span> sprintf ] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>And, if it is provided, we can parse the variable wind direction also:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-wind-variable</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"V"</span> split1 [ string>number [ direction>compass ] <span class="nb">keep </span>] <span class="nb">bi@
</span></span></span><span class="line"><span class="cl"> <span class="s">", variable from %s (%s°) to %s (%s°)"</span> sprintf <span class="k">;
</span></span></span></code></pre></div><h3 id="visibility">Visibility</h3>
<pre tabindex="0"><code>KJFK 262351Z 19011KT 10SM -RA SCT060 BKN250 23/18 A3002 RMK AO2
SLP166 T02280183 10289 20228 51004
</code></pre><p>The visibility is specified as a number (usually whole, but sometimes a
fraction) of statute miles:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">re-visibility</span> R! [M]?\d+(/\d+)?SM!
</span></span></code></pre></div><p>In very poor conditions, the report might specify “M1/4SM” which means
“less than 1/4 statute miles”, so we want to handle that. We also want
to parse “11/4” as “1+1/4”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-visibility</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"M"</span> ?head <span class="s">"less than "</span> <span class="s">""</span> <span class="nb">? swap </span><span class="s">"SM"</span> ?tail <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> <span class="sc">CHAR: / </span><span class="nb">over index </span>[ <span class="m">1 </span><span class="nb">> </span>[ <span class="m">1 </span><span class="nb">cut </span><span class="s">"+"</span> <span class="nb">glue </span>] <span class="nb">when </span>] <span class="nb">when*
</span></span></span><span class="line"><span class="cl"> string>number <span class="s">"%s%s statute miles"</span> sprintf <span class="k">;
</span></span></span></code></pre></div><h3 id="weather">Weather</h3>
<pre tabindex="0"><code>KJFK 262351Z 19011KT 10SM -RA SCT060 BKN250 23/18 A3002 RMK AO2
SLP166 T02280183 10289 20228 51004
</code></pre><p>The present weather is specified by a severity modifier, an indicator if
it is over the airport or in the general vicinity, and one or two
weather descriptors:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">re-weather</span> R! [+-]?(VC)?(\w{2}|\w{4})!
</span></span></code></pre></div><p>For use in our parser, we define all the two-letter codes used to
represent weather:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">weather</span> H{
</span></span><span class="line"><span class="cl"> { <span class="s">"BC"</span> <span class="s">"patches"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"BL"</span> <span class="s">"blowing"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"BR"</span> <span class="s">"mist"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"DR"</span> <span class="s">"low drifting"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"DS"</span> <span class="s">"duststorm"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"DU"</span> <span class="s">"widespread dust"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"DZ"</span> <span class="s">"drizzle"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"FC"</span> <span class="s">"funnel clouds"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"FG"</span> <span class="s">"fog"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"FU"</span> <span class="s">"smoke"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"FZ"</span> <span class="s">"freezing"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"GR"</span> <span class="s">"hail"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"GS"</span> <span class="s">"small hail and/or snow pellets"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"HZ"</span> <span class="s">"haze"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"IC"</span> <span class="s">"ice crystals"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"MI"</span> <span class="s">"shallow"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"PL"</span> <span class="s">"ice pellets"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"PO"</span> <span class="s">"well-developed dust/sand whirls"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"PR"</span> <span class="s">"partial"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"PY"</span> <span class="s">"spray"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"RA"</span> <span class="s">"rain"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"RE"</span> <span class="s">"recent"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"SA"</span> <span class="s">"sand"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"SG"</span> <span class="s">"snow grains"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"SH"</span> <span class="s">"showers"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"SN"</span> <span class="s">"snow"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"SQ"</span> <span class="s">"squalls"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"SS"</span> <span class="s">"sandstorm"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"TS"</span> <span class="s">"thuderstorm"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"UP"</span> <span class="s">"unknown"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"VA"</span> <span class="s">"volcanic ash"</span> }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Weather severity defaults to moderate, but a leading “<code>+</code>” is used to
indicate heavy weather and a “<code>-</code>” for light weather. Also, “<code>+FC</code>” is a
special indicator of tornadoes and waterspouts:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(parse-weather)</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"+FC"</span> <span class="nb">= </span>[ <span class="nb">drop </span><span class="s">"tornadoes or waterspouts"</span> ] [
</span></span><span class="line"><span class="cl"> <span class="nb">dup first </span>{
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: + </span>[ <span class="nb">rest </span><span class="s">"heavy "</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: - </span>[ <span class="nb">rest </span><span class="s">"light "</span> ] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="no">f </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span>[
</span></span><span class="line"><span class="cl"> <span class="m">2 </span>group [ weather <span class="nb">at </span>] <span class="nb">map </span><span class="s">" "</span> <span class="nb">join
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">dip prepend
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>If “<code>VC</code>” is specified, it means the weather is in the vicinity (instead
of overhead). We check to see if it was specified, and add the phrase if
it was:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-weather</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"VC"</span> <span class="nb">over subseq? </span>[ <span class="s">"VC"</span> <span class="s">""</span> replace <span class="no">t </span>] [ <span class="no">f </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> [ (parse-weather) ]
</span></span><span class="line"><span class="cl"> [ [ <span class="s">" in the vicinity"</span> <span class="nb">append </span>] <span class="nb">when </span>] <span class="nb">bi* </span><span class="k">;
</span></span></span></code></pre></div><h3 id="sky-conditions">Sky Conditions</h3>
<pre tabindex="0"><code>KJFK 262351Z 19011KT 10SM -RA SCT060 BKN250 23/18 A3002 RMK AO2
SLP166 T02280183 10289 20228 51004
</code></pre><p>Several sky conditions might be present in the message, each having a
three-character indicator of cloud cover and an altitude, optionally
including the type of clouds observed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">re-sky-condition</span> R! (\w{3}\d{3}(\w+)?|\w{3}|CAVOK)!
</span></span></code></pre></div><p>We should have a hashtable with mappings from the abbreviations to plain
text:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">sky</span> H{
</span></span><span class="line"><span class="cl"> { <span class="s">"BKN"</span> <span class="s">"broken"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"FEW"</span> <span class="s">"few"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"OVC"</span> <span class="s">"overcast"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"SCT"</span> <span class="s">"scattered"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"SKC"</span> <span class="s">"clear sky"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"CLR"</span> <span class="s">"clear sky"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"NSC"</span> <span class="s">"clear sky"</span> }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> { <span class="s">"ACC"</span> <span class="s">"altocumulus castellanus"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"ACSL"</span> <span class="s">"standing lenticular altocumulus"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"CCSL"</span> <span class="s">"cirrocumulus standing lenticular cloud"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"CU"</span> <span class="s">"cumulus"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"SC"</span> <span class="s">"stratocumulus"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"SCSL"</span> <span class="s">"stratocumulus standing lenticular cloud"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"TCU"</span> <span class="s">"towering cumulus"</span> }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>The altitudes present are in hundreds of feet above the ground. Also, we
check for “<code>CAVOK</code>”, which is sometimes used to mean “<strong>C</strong>eiling
<strong>a</strong>nd <strong>V</strong>isibility are <strong>OK</strong>”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-sky-condition</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"CAVOK"</span> <span class="nb">= </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">drop </span><span class="s">"clear skies and unlimited visibility"</span>
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="m">3 </span><span class="nb">cut </span><span class="m">3 </span><span class="nb">cut
</span></span></span><span class="line"><span class="cl"> [ sky <span class="nb">at </span>]
</span></span><span class="line"><span class="cl"> [ string>number <span class="s">" at %s00 ft"</span> sprintf ]
</span></span><span class="line"><span class="cl"> [ sky <span class="nb">at </span>[ <span class="s">" (%s)"</span> sprintf ] [ <span class="no">f </span>] <span class="nb">if* </span>]
</span></span><span class="line"><span class="cl"> <span class="nb">tri* 3append
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><h3 id="temperature-and-dew-point">Temperature and Dew Point</h3>
<pre tabindex="0"><code>KJFK 262351Z 19011KT 10SM -RA SCT060 BKN250 23/18 A3002 RMK AO2
SLP166 T02280183 10289 20228 51004
</code></pre><p>The temperature is specified as a number of degrees Celsius (preceded by
an “<code>M</code>” to indicate a negative number), and optionally a dew point if
available:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">re-temperature</span> R! [M]?\d{2}/([M]?\d{2})?!
</span></span></code></pre></div><p>The parsing code is fairly easy in this case:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-temperature</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">temperature</span> <span class="nv">dew-point</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"/"</span> split1 [
</span></span><span class="line"><span class="cl"> [ <span class="no">f </span>] [
</span></span><span class="line"><span class="cl"> <span class="s">"M"</span> ?head [ string>number ] [ [ <span class="nb">neg </span>] <span class="nb">when </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> <span class="s">"%s °C"</span> sprintf
</span></span><span class="line"><span class="cl"> ] <span class="nb">if-empty
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">bi@ </span><span class="k">;
</span></span></span></code></pre></div><h3 id="altimeter">Altimeter</h3>
<pre tabindex="0"><code>KJFK 262351Z 19011KT 10SM -RA SCT060 BKN250 23/18 A3002 RMK AO2
SLP166 T02280183 10289 20228 51004
</code></pre><p>The altimeter information is a four-digit numerical observation of the
current air pressure at the surface, measured in <a href="https://en.wikipedia.org/wiki/Inch_of_mercury">inches of
mercury</a> or
<a href="https://en.wikipedia.org/wiki/Pascal_(unit)">hectopascals</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">re-altimeter</span> R! [AQ]\d{4}!
</span></span></code></pre></div><p>We can parse it, checking for <code>A</code> (USA) or <code>Q</code> (International) units:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-altimeter</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">unclip </span>[ string>number ] [ <span class="sc">CHAR: A </span><span class="nb">= </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> [ <span class="m">100 </span><span class="nb">/f </span><span class="s">"%.2f Hg"</span> sprintf ] [ <span class="s">"%s hPa"</span> sprintf ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><h3 id="remarks">Remarks</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">KJFK 262351Z 19011KT 10SM -RA SCT060 BKN250 23/18 A3002 RMK AO2
</span></span><span class="line"><span class="cl">SLP166 T02280183 <span class="m">10289 20228 51004
</span></span></span></code></pre></div><p>For the remarks, since this post is getting to be rather long, let’s
just save them raw and deal with parsing them later… however, as a
preview of that, here is a short interpretation of this message:</p>
<ul>
<li><code>AO2</code> - station with precipitation discriminator</li>
<li><code>SLP166</code> - sea-level pressure is 1016.6 hPa</li>
<li><code>T02280183</code> - 1-hr temperature 22.8 °C and dew point 18.3 °C</li>
<li><code>10289</code> - 6-hr maximum temperature 28.9 °C</li>
<li><code>20228</code> - 6-hr minimum temperature 22.8 °C</li>
<li><code>51004</code> - atmospheric pressure increasing by 0.4 hPa</li>
</ul>
<h3 id="finally">Finally!</h3>
<p>We will define a report having these fields:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">report</span> <span class="nv">station</span> <span class="nv">timestamp</span> <span class="nv">wind</span> <span class="nv">visibility</span> <span class="nv">weather</span>
</span></span><span class="line"><span class="cl"><span class="nv">sky-condition</span> <span class="nv">temperature</span> <span class="nv">dew-point</span> <span class="nv">altimeter</span> <span class="nv">remarks</span> <span class="k">;
</span></span></span></code></pre></div><p>Okay, just a little bit more and we can finally get to parsing and
displaying the message. We need a way to find a single token if present,
removing it from the list, and returning the remainder of the message:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-one</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">elt</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">) -- </span><span class="nv">seq</span> <span class="nv">elt/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dupd find drop </span>[ <span class="nb">tail unclip </span>] [ <span class="no">f </span>] <span class="nb">if* </span><span class="k">; inline
</span></span></span></code></pre></div><p>Some tokens are present multiple times (such as weather and sky
condition), so we want to return a sequence of all tokens found
sequentially matching the requested type:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-all</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">elt</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">) -- </span><span class="nv">seq</span> <span class="nv">elts</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ find-one <span class="nb">swap </span>] <span class="nb">keep </span>'[
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ <span class="no">f </span>] [ <span class="nb">first </span>@ ] <span class="nb">if-empty
</span></span></span><span class="line"><span class="cl"> ] [ <span class="nb">unclip </span>] <span class="nb">produce rot </span>[ <span class="nb">prefix </span>] <span class="nb">when* </span><span class="k">; inline
</span></span></span></code></pre></div><p>Parsing a report is a rather lengthy, but it basically splits the report
into the body and remarks, then looks for each token in the order that
it should be found in the message, setting it on the report object if
present:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><report></span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">report</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ report <span class="nb">new </span>] <span class="nb">dip </span>[ blank? ] split-when
</span></span><span class="line"><span class="cl"> { <span class="s">"RMK"</span> } split1 [ body ] [ remarks ] <span class="nb">bi* </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">body</span> <span class="nf">( </span><span class="nv">report</span> <span class="nv">seq</span> <span class="nf">-- </span><span class="nv">report</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ re-station matches? ] find-one
</span></span><span class="line"><span class="cl"> [ <span class="nb">pick </span>station<< ] <span class="nb">when*
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ re-timestamp matches? ] find-one
</span></span><span class="line"><span class="cl"> [ parse-timestamp <span class="nb">pick </span>timestamp<< ] <span class="nb">when*
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ re-wind matches? ] find-one
</span></span><span class="line"><span class="cl"> [ parse-wind <span class="nb">pick </span>wind<< ] <span class="nb">when*
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ re-wind-variable matches? ] find-one
</span></span><span class="line"><span class="cl"> [ parse-wind-variable <span class="nb">pick </span>wind>> <span class="nb">prepend pick </span>wind<< ] <span class="nb">when*
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ re-visibility matches? ] find-one
</span></span><span class="line"><span class="cl"> [ parse-visibility <span class="nb">pick </span>visibility<< ] <span class="nb">when*
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ re-weather matches? ] find-all
</span></span><span class="line"><span class="cl"> [ parse-weather ] <span class="nb">map </span><span class="s">", "</span> <span class="nb">join pick </span>weather<<
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ re-sky-condition matches? ] find-all
</span></span><span class="line"><span class="cl"> [ parse-sky-condition ] <span class="nb">map </span><span class="s">", "</span> <span class="nb">join pick </span>sky-condition<<
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ re-temperature matches? ] find-one
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> parse-temperature
</span></span><span class="line"><span class="cl"> [ <span class="nb">pick </span>temperature<< ]
</span></span><span class="line"><span class="cl"> [ <span class="nb">pick </span>dew-point<< ] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">when*
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ re-altimeter matches? ] find-one
</span></span><span class="line"><span class="cl"> [ parse-altimeter <span class="nb">pick </span>altimeter<< ] <span class="nb">when*
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nb">drop </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">remarks</span> <span class="nf">( </span><span class="nv">report</span> <span class="nv">seq</span> <span class="nf">-- </span><span class="nv">report</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">" "</span> <span class="nb">join </span>>>remarks <span class="k">;
</span></span></span></code></pre></div><p>And now, display our output in a table, wrapping the right column at 65
characters:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">row.</span> <span class="nf">( </span><span class="nv">name</span> <span class="nv">quot</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> '[
</span></span><span class="line"><span class="cl"> [ _ <span class="nb">write </span>] with-cell
</span></span><span class="line"><span class="cl"> [ @ [ <span class="m">65 </span>wrap-string <span class="nb">write </span>] <span class="nb">when* </span>] with-cell
</span></span><span class="line"><span class="cl"> ] with-row <span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">report.</span> <span class="nf">( </span><span class="nv">report</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> standard-table-style [
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ <span class="s">"Station"</span> [ station>> ] row. ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"Timestamp"</span> [ timestamp>> ] row. ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"Wind"</span> [ wind>> ] row. ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"Visibility"</span> [ visibility>> ] row. ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"Weather"</span> [ weather>> ] row. ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"Sky condition"</span> [ sky-condition>> ] row. ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"Temperature"</span> [ temperature>> ] row. ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"Dew point"</span> [ dew-point>> ] row. ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"Altimeter"</span> [ altimeter>> ] row. ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"Remarks"</span> [ remarks>> ] row. ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave
</span></span></span><span class="line"><span class="cl"> ] tabular-output <span class="nb">nl </span><span class="k">;
</span></span></span></code></pre></div><p>Combining this with our original <code>metar</code> word to download a current
report and print it out, printing a nice message if the station
requested does not exist or have a weather report:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">metar.</span> <span class="nf">( </span><span class="nv">station</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ metar <report> report. ]
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">"%s METAR not found\n"</span> printf ] <span class="nb">recover </span><span class="k">;
</span></span></span></code></pre></div><p>Phew, that plus a lot more parsing of the remarks section is available
on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/metar/metar.factor">GitHub</a>.</p>
Logistic Map
https://re.factorcode.org/2013/07/logistic-map.html
Mon, 22 Jul 2013 08:35:00 -0700https://re.factorcode.org/2013/07/logistic-map.html<p>A <a href="https://kenta.blogspot.com/2013/07/abfsttwf-optimizing-iterate.html">recent blog
post</a>
discusses the relative performance between Haskell and C when
calculating the billionth iteration of a particular <a href="https://en.wikipedia.org/wiki/Logistic_map">logistic
map</a> function:</p>
<pre tabindex="0"><code>f(x) = 3.57 * x * (1-x)
</code></pre><p>A bit curious about how <a href="https://factorcode.org">Factor</a> would perform, I
decided to do a comparison.</p>
<h3 id="c">C</h3>
<p>A simple implementation in C…</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf"><stdio.h></span><span class="cp">
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kt">double</span> <span class="n">x</span> <span class="o">=</span> <span class="mf">0.5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">1000000000</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">){</span>
</span></span><span class="line"><span class="cl"> <span class="n">x</span> <span class="o">=</span> <span class="mf">3.57</span> <span class="o">*</span> <span class="n">x</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span><span class="o">-</span><span class="n">x</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">"x=%f</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">x</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>…yields an answer in about 4.5 seconds on my machine:</p>
<pre tabindex="0"><code>$ time ./answer
x=0.495618
real 0m4.501s
user 0m4.495s
sys 0m0.005s
</code></pre><h3 id="factor">Factor</h3>
<p>A simple implementation in <a href="https://factorcode.org">Factor</a>…</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">logistic-chaos</span> <span class="nf">( </span><span class="nv">x</span> <span class="nf">-- </span><span class="nv">y</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">3.57 </span><span class="nb">* </span>] [ <span class="m">1 </span><span class="nb">swap - * </span>] <span class="nb">bi </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">answer</span> <span class="nf">( -- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0.5 1,000,000,000 </span>[ logistic-chaos ] <span class="nb">times </span><span class="k">;
</span></span></span></code></pre></div><p>…yields an answer in about 4.5 seconds on my machine!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ answer ] time <span class="m">.
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">4.478111574 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="m">0.4956180832934247
</span></span></span></code></pre></div><p><em>Note: Part of the speed comes from the Factor compiler using the
<code>inline</code> request to determine that this is always called with floating
point numbers. If the word was not inlined, the overhead of generic
dispatch on every call would make this calculation take 19 seconds. If
the calculation was not compiled into a word (as it is above), and just
typed into the listener and executed with the non-optimizing compiler,
the calculation would take 57 seconds.</em></p>
Reading List
https://re.factorcode.org/2013/07/reading-list.html
Mon, 15 Jul 2013 08:38:00 -0700https://re.factorcode.org/2013/07/reading-list.html<p>The Safari browser has a feature called “Reading List” that makes it
easy to synchronize and bookmark pages for later reading. Recently, a
<a href="https://hints.macworld.com/article.php?story=20130715063948309">Mac OS
Hint</a>
linked to a question on stackexchange.com asking <a href="https://apple.stackexchange.com/questions/96353/how-to-get-reading-list-items-as-links">how to get Reading
List items as
links</a>.</p>
<p>The current answer is a Python script that:</p>
<ol>
<li>Finds the safari bookmarks plist file</li>
<li>Copies the plist file to a temporary file</li>
<li>Converts the plist file from binary to text format</li>
<li>Parses it into a python object</li>
<li>Finds the “Reading List” entry</li>
<li>Prints out the saved URLs</li>
</ol>
<p>I thought it would be fun to contrast that with a simple solution in
<a href="https://factorcode.org">Factor</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">reading-list</span> <span class="nf">( -- </span><span class="nv">urls</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"~/Library/Safari/Bookmarks.plist"</span> read-plist
</span></span><span class="line"><span class="cl"> <span class="s">"Children"</span> <span class="nb">of </span>[ <span class="s">"Title"</span> <span class="nb">of </span><span class="s">"com.apple.ReadingList"</span> <span class="nb">= </span>] <span class="nb">find nip
</span></span></span><span class="line"><span class="cl"> <span class="s">"Children"</span> <span class="nb">of </span>[ <span class="s">"URLString"</span> <span class="nb">of </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>You can try it out in the listener to get something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> reading-list <span class="m">.
</span></span></span><span class="line"><span class="cl">V{
</span></span><span class="line"><span class="cl"> <span class="s">"https://factorcode.org"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"https://news.ycombinator.com"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"https://reddit.com"</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div>Non-repeating
https://re.factorcode.org/2013/05/non-repeating.html
Thu, 02 May 2013 08:18:00 -0700https://re.factorcode.org/2013/05/non-repeating.html<p>The <a href="https://programmingpraxis.com">Programming Praxis</a> blog, which
recently achieved the milestone of <a href="https://programmingpraxis.com/2013/03/29/one-million-hits/">One Million
Hits</a>, posted
a challenge a couple days to find the <a href="https://programmingpraxis.com/2013/04/30/first-unrepeated-character-in-a-string/">first unrepeated character in a
string</a>:</p>
<blockquote>
<p><em>Given a string, find the first character that appears only once in
the string. For instance, given the string “aabbcddd”, the first
character that appears only once is the “c” found at the 4th character
in the string, counting from 0. Be sure that your program properly
handles the case that all characters appear more than once.</em></p>
</blockquote>
<p>Our quick-and-dirty solution, using <a href="https://docs.factorcode.org/content/article-locals.html">lexical
variables</a>, will
add each element in the sequence to a
<a href="https://docs.factorcode.org/content/article-hash-sets.html">hash-set</a>,
adding the element to an accumulator if not seen before, removing the
element otherwise:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">non-repeating</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> HS{ } <span class="nb">clone </span>:> visited
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>seq <span class="nb">new-resizable </span>:> accum
</span></span><span class="line"><span class="cl"> seq [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>visited ?adjoin
</span></span><span class="line"><span class="cl"> [ accum <span class="nb">push </span>] [ accum <span class="nb">remove! drop </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span>accum seq <span class="nb">like </span><span class="k">;
</span></span></span></code></pre></div><p>We can then use it like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="c">! find all non-repeating characters (in-order)</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"abcddd"</span> non-repeating <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"abc"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">! find the first non-repeating character</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"abcddd"</span> non-repeating <span class="nb">first </span><span class="s">"%c"</span> printf
</span></span><span class="line"><span class="cl">a
</span></span></code></pre></div><p>An improvement might be, instead of using
<a href="https://docs.factorcode.org/content/word-remove!,sequences.html">remove!</a>
which will look for and remove all occurrences of an object in a
sequence, we could instead only remove the first occurrence (knowing an
object will only ever be in a sequence once):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">remove-first!</span> <span class="nf">( </span><span class="nv">obj</span> <span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">index </span>] <span class="nb">keep over </span>[ <span class="nb">remove-nth! </span>] [ <span class="nb">nip </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>How else might we improve it?</p>
Terminfo
https://re.factorcode.org/2013/04/terminfo.html
Fri, 26 Apr 2013 09:00:00 -0700https://re.factorcode.org/2013/04/terminfo.html<p>While investigating <a href="https://stackoverflow.com/questions/2465425/how-do-i-determine-if-a-terminal-is-color-capable">how to determine if a terminal is color
capable</a>,
I re-discovered <a href="https://en.wikipedia.org/wiki/Terminfo">terminfo
databases</a>. These database files
store the capabilities of terminals in a device-independent manner.</p>
<h3 id="tput">tput</h3>
<p>The simple answer is to use the <a href="https://linux.die.net/man/1/tput">tput</a>
program to lookup the terminal functionality (using the <code>TERM</code>
environment variable):</p>
<pre tabindex="0"><code>$ TERM=xterm-256color tput colors
256
$ TERM=xterm tput colors
8
$ TERM=vt100 tput colors
-1
</code></pre><p>If you trace the system calls that <code>tput</code> makes, you will see that it is
loading a terminfo file to provide the answer:</p>
<pre tabindex="0"><code>...
stat64("/usr/share/terminfo\0", 0x7FFF5F21A120, 0x7FB279403AD0)
access("/usr/share/terminfo/78/xterm-256color\0", 0x4, 0xE)
open("/usr/share/terminfo/78/xterm-256color\0", 0x0, 0x0)
read(0x3, "\032\001%\0", 0x1001)
...
</code></pre><p>I wanted to have access to these capabilities from
<a href="https://factorcode.org">Factor</a>, without running <code>tput</code>, and chose
instead to directly parse the terminfo files.</p>
<h3 id="terminfo">terminfo</h3>
<p>The compiled terminfo file is created by the
<a href="https://linux.die.net/man/1/tic">tic</a> program, and begins with a header
containing six two-byte short integers:</p>
<ol>
<li>the magic number (octal 0432)</li>
<li>the size, in bytes, of the names section</li>
<li>the number of bytes in the boolean section</li>
<li>the number of short integers in the numbers section</li>
<li>the number of offsets (short integers) in the strings section</li>
<li>the size, in bytes, of the string table</li>
</ol>
<p>We can parse this header pretty easily using the
<a href="https://docs.factorcode.org/content/vocab-pack.html">pack</a> vocabulary:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">terminfo-header</span> <span class="nv">names-bytes</span> <span class="nv">boolean-bytes</span> <span class="nv">#numbers</span>
</span></span><span class="line"><span class="cl"><span class="nv">#strings</span> <span class="nv">string-bytes</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">C:</span> <span class="nf"><terminfo-header></span> <span class="nc">terminfo-header</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-header</span> <span class="nf">( -- </span><span class="nv">header</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">12 </span><span class="nb">read </span><span class="s">"ssssss"</span> unpack-le <span class="nb">unclip
</span></span></span><span class="line"><span class="cl"> <span class="mo">0o432 </span><span class="nb">= </span>[ <span class="s">"bad magic"</span> <span class="nb">throw </span>] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> <span class="m">5 </span>firstn <terminfo-header> <span class="k">;
</span></span></span></code></pre></div><p>The names section comes next, containing the various names for the
terminal separated by a “|” character and terminated by a NUL byte
(“0”):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-names</span> <span class="nf">( </span><span class="nv">header</span> <span class="nf">-- </span><span class="nv">names</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> names-bytes>> <span class="nb">read but-last </span><span class="s">"|"</span> split [ <span class="nb">>string </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>The boolean section is stored as one byte per boolean flag, either a 0
or 1:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-booleans</span> <span class="nf">( </span><span class="nv">header</span> <span class="nf">-- </span><span class="nv">booleans</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> boolean-bytes>> <span class="nb">read </span>[ <span class="m">1 </span><span class="nb">= </span>] { } <span class="nb">map-as </span><span class="k">;
</span></span></span></code></pre></div><p>The number section is stored as a sequence of two-byte short integers,
aligned to an even byte (meaning if the name and boolean sections
consume an “odd” number of bytes, an extra byte is inserted that should
be skipped over to ensure the numbers start on an even byte):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-shorts</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">2 </span><span class="nb">* read </span><span class="m">2 </span><groups> [ signed-le> <span class="nb">dup </span><span class="m">0 </span><span class="nb">< </span>[ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">when </span>] <span class="nb">map </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">align-even-bytes</span> <span class="nf">( </span><span class="nv">header</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ names-bytes>> ] [ boolean-bytes>> ] <span class="nb">bi + odd?
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">read1 drop </span>] <span class="nb">when </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-numbers</span> <span class="nf">( </span><span class="nv">header</span> <span class="nf">-- </span><span class="nv">numbers</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ align-even-bytes ] [ #numbers>> read-shorts ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>The strings are more complex, stored in two sections. The first section
is a sequence of two-byte short integers and the second section is a
sequence of bytes. To rebuild the string capabilities, interpret the
integers as an offset into the string table:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-strings</span> <span class="nf">( </span><span class="nv">header</span> <span class="nf">-- </span><span class="nv">strings</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ #strings>> read-shorts ] [ string-bytes>> <span class="nb">read </span>] <span class="nb">bi </span>'[
</span></span><span class="line"><span class="cl"> [ _ <span class="m">0 </span><span class="nb">2over index-from swap subseq >string </span>] [ <span class="no">f </span>] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>Putting this all together, we can “parse” our terminfo file into an
object:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">terminfo</span> <span class="nv">names</span> <span class="nv">booleans</span> <span class="nv">numbers</span> <span class="nv">strings</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">C:</span> <span class="nf"><terminfo></span> <span class="nc">terminfo</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-terminfo</span> <span class="nf">( -- </span><span class="nv">terminfo</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> read-header {
</span></span><span class="line"><span class="cl"> [ read-names ]
</span></span><span class="line"><span class="cl"> [ read-booleans ]
</span></span><span class="line"><span class="cl"> [ read-numbers ]
</span></span><span class="line"><span class="cl"> [ read-strings ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span><terminfo> <span class="k">;
</span></span></span></code></pre></div><p>Finally, we can write a parsing word to convert a terminfo file into a
terminfo object:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">file>terminfo</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">terminfo</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> binary [ read-terminfo ] with-file-reader <span class="k">;
</span></span></span></code></pre></div><h3 id="usrshareterminfo">/usr/share/terminfo</h3>
<p>The terminfo files are stored in <code>/usr/share/terminfo</code>. If we wanted to
get a list of all available terminfo files, we can just list this
directory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MEMO:</span> <span class="nf">terminfo-names</span> <span class="nf">( -- </span><span class="nv">names</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"/usr/share/terminfo"</span> [
</span></span><span class="line"><span class="cl"> [ directory-files ] <span class="nb">map concat
</span></span></span><span class="line"><span class="cl"> ] with-directory-files <span class="k">;
</span></span></span></code></pre></div><p>If instead, we wanted to lookup a specific terminal, we can map the name
of the terminal to a directory. On Mac OS, these are stored in a
sub-directory with the hexadecimal representation of the first byte in
the string. On Linux, the first character is the name of the
sub-directory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">HOOK:</span> <span class="nf">terminfo-path</span> <span class="nf">os</span> <span class="nf">( </span><span class="nv">name</span> <span class="nf">-- </span><span class="nv">path</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">macosx</span> <span class="nf">terminfo-path</span> <span class="nf">( </span><span class="nv">name</span> <span class="nf">-- </span><span class="nv">path</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">first </span>>hex ] <span class="nb">keep </span><span class="s">"/usr/share/terminfo/%s/%s"</span> sprintf <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">linux</span> <span class="nf">terminfo-path</span> <span class="nf">( </span><span class="nv">name</span> <span class="nf">-- </span><span class="nv">path</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">first </span>] <span class="nb">keep </span><span class="s">"/usr/share/terminfo/%c/%s"</span> sprintf <span class="k">;
</span></span></span></code></pre></div><h3 id="success">Success!</h3>
<p>With just this much implemented, we can lookup our “max_colors”
attribute, knowing it is the 14th number in the numbers table:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">max-colors</span> <span class="nf">( </span><span class="nv">name</span> <span class="nf">-- </span><span class="nv">n/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> terminfo-path file>terminfo numbers>> <span class="m">13 </span><span class="nb">swap ?nth </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"xterm-256color"</span> max-colors <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">256
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"xterm"</span> max-colors <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">8
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"vt100"</span> max-colors <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">f
</span></span></span></code></pre></div><p>I added support for parsing all the capabilities into a hashtable, and
allowing named lookup (rather than needing to know the offset like we
used above).</p>
<p>
<img src="https://re.factorcode.org/images/2013-04-26-terminfo-terminfo.png" alt="" width="400" height="273" />
</p>
<p>This is available now in the <a href="https://github.com/factor/factor/blob/master/extra/terminfo/terminfo.factor">terminfo
vocabulary</a>.</p>
Factor 0.96 now available
https://re.factorcode.org/2013/04/factor-0-96-now-available.html
Sat, 20 Apr 2013 21:38:00 -0700https://re.factorcode.org/2013/04/factor-0-96-now-available.html<p><em>“You’re smart too late and old too soon.” - Mike Tyson</em></p>
<p>I’m very pleased to announce the release of
<a href="https://factorcode.org">Factor</a> 0.96!</p>
<table class="downloads" cellspacing="0">
<colgroup>
<col style="width: 25%" />
<col style="width: 25%" />
<col style="width: 25%" />
<col style="width: 25%" />
</colgroup>
<thead>
<tr class="header">
<th class="nobg" style="text-align: center;">OS/CPU</th>
<th class="bg" style="text-align: center;" scope="col">Windows</th>
<th class="bg" style="text-align: center;" scope="col">Mac OS</th>
<th class="bg" style="text-align: center;" scope="col">Linux</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<th class="bg" style="text-align: center;" scope="row">x86</th>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.96/factor-windows-x86-32-0.96.zip">0.96</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.96/factor-macosx-x86-32-0.96.dmg">0.96</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.96/factor-linux-x86-32-0.96.tar.gz">0.96</a>
</td>
</tr>
<tr class="even">
<th class="bg" style="text-align: center;" scope="row">x86-64</th>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.96/factor-windows-x86-64-0.96.zip">0.96</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.96/factor-macosx-x86-64-0.96.dmg">0.96</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.96/factor-linux-x86-64-0.96.tar.gz">0.96</a>
</td>
</tr>
</tbody>
</table>
<p><strong>Source code</strong>: <a
href="https://downloads.factorcode.org/releases/0.96/factor-src-0.96.zip"
class="release">0.96</a></p>
<p>This release is brought to you with over 1100 commits by the following
individuals:</p>
<blockquote>
<p>Alex Vondrak, Benjamin Pollack, Daniel Nagel, Doug Coleman, John
Benediktsson, Jon Harper, Michael T. Richter, and
<a href="https://github.com/PGGB">@PGGB</a>.</p>
</blockquote>
<p>Aside from bug fixes and various library improvements, I want to
highlight the following changes:</p>
<ul>
<li>Major compiler improvements (thanks Alex Vondrak!):
<ul>
<li>Global Value Numbering (disabled currently by default)</li>
<li>Parallel-Copy Semantics</li>
</ul>
</li>
<li>Performance improvements to hashtables, hash-sets, heaps, and
bloom-filters.</li>
<li>Support for Retina Displays on OS X</li>
<li>Greatly <a href="https://re.factorcode.org/2012/09/faster-tables.html">improved table gadget
performance</a></li>
<li>PDF streams (and related PDF documentation)!</li>
<li>Speed up math comparisons</li>
<li>Support resize-window (thanks Jon Harper!)</li>
<li>New <a href="https://re.factorcode.org/2012/11/new-logo.html">logo and
icons</a> for
Factor</li>
<li>Added editor support for <a href="https://foicica.com/textadept/">Textadept</a>,
<a href="https://www.sublimetext.com">Sublime Text</a>,
<a href="https://www.geany.org">Geany</a>,
<a href="https://www.barebones.com/products/bbedit/">BBEdit</a>, and
<a href="https://developer.apple.com/xcode/">XCode</a>.</li>
</ul>
<p>Some possible backwards compatibility issues:</p>
<ul>
<li>Changed <code><groups></code>, <code><clumps></code>, and <code><circular-clumps></code> to use
slices.</li>
<li>Removed <code><slicing-groups></code>, <code><slicing-clumps></code>,
<code><slicing-circular-clumps></code>.</li>
<li>Renamed <code>editors.notepadpp</code> to <code>editors.notepad++</code>.</li>
</ul>
<p>Since quite a few performance improvements have been made, I thought I
would highlight some improvements using our benchmarks against version
0.95. The chart below shows benchmark time in 0.96 as a percent of the
time it took in 0.95, lower is better:</p>
<p>
<img src="https://re.factorcode.org/images/2013-04-20-factor-0-96-now-available-performance.png" alt="" width="432" height="800" />
</p>
<h3 id="what-is-factor">What is Factor</h3>
<p>Factor is a <a href="https://www.concatenative.org/">concatenative</a>, stack-based
programming language with <a href="https://concatenative.org/wiki/view/Factor/Features/The%20language">high-level
features</a>
including dynamic types, extensible syntax, macros, and garbage
collection. On a practical side, Factor has a <a href="https://docs.factorcode.org/content/article-vocab-index.html">full-featured
library</a>,
supports many different platforms, and has been extensively documented.</p>
<p>The implementation is <a href="https://concatenative.org/wiki/view/Factor/Optimizing%20compiler">fully
compiled</a>
for performance, while still supporting <a href="https://concatenative.org/wiki/view/Factor/Interactive%20development">interactive
development</a>.
Factor applications are portable between all common platforms. Factor
can <a href="https://concatenative.org/wiki/view/Factor/Deployment">deploy stand-alone
applications</a> on
all platforms. Full source code for the Factor project is available
under a BSD license.</p>
<h3 id="new-libraries">New Libraries</h3>
<ul>
<li><a href="https://docs.factorcode.org/content/vocab-arrays.shaped.html">arrays.shaped</a>:
prototype of n-dimensional array class (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-base85.html">base85</a>:
support for Base85 encoding (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-benchmark.html">benchmark</a>:
flip, lcs, linked-assocs, unicode, randomize, timers (John
Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-cgi.html">cgi</a>: support
using Factor in CGI scripts (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-checksums.bsd.html">checksums.bsd</a>:
support for BSD-style checksums (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.cmyk.html">colors.cmyk</a>:
adding support for CMYK colors (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.hsl.html">colors.hsl</a>:
adding HSL color support (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.mix.html">colors.mix</a>:
mixing colors together (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.yiq.html">colors.yiq</a>:
adding YIQ color support (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-combinators.extras.html">combinators.extras</a>:
new words (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-compiler.cfg.graphviz.html">compiler.cfg.graphviz</a>:
graphviz support (Alex Vondrak)</li>
<li><a href="https://docs.factorcode.org/content/vocab-compiler.cfg.gvn.html">compiler.cfg.gvn</a>:
global value numbering (Alex Vondrak)</li>
<li><a href="https://docs.factorcode.org/content/vocab-hacker-news.html">hacker-news</a>:
add a <a href="https://news.ycombinator.com">news.ycombinator.com</a> wrapper
(Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-hash-sets.wrapped.html">hash-sets.wrapped</a>:
identity hash-sets and other wrapped versions (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-help.pdf.html">help.pdf</a>:
render PDF files from help articles (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.files.acls.html">io.files.acls</a>:
support for ACL’s on OS X (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.random.html">io.random</a>:
adding <a href="https://re.factorcode.org/2012/10/select-random-lines.html">random-line and
random-lines</a>
(John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.random.html">io.streams.random</a>:
adding random-stream (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.zero.html">io.streams.zero</a>:
adding
<a href="https://code-factor.blogspot.com/2010/09/filling-file-with-zeros.html">zero-stream</a>
(Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-ip-parser.html">ip-parser</a>:
adding <a href="https://re.factorcode.org/2012/10/parsing-ipv4-addresses.html">“ping-compatible” IP
parser</a>
(John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-literate.html">literate</a>:
support for <a href="https://re.factorcode.org/2012/08/literate-programming.html">literate programming
ideas</a>
(John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-machine-learning.data-sets.html">machine-learning.data-sets</a>:
load common data-sets (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-machine-learning.rebalancing.html">machine-learning.rebalancing</a>:
support for rebalancing an {X,y} dataset (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-machine-learning.one-hot.html">machine-learning.one-hot</a>:
adding <a href="https://code-factor.blogspot.com/2012/10/one-hotone-of-k-data-encoder-for.html">one-hot
encoder</a>
(Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.cardinality.html">math.cardinality</a>:
adding a cardinality estimator (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.combinatorics.bits.html">math.combinatorics.bits</a>:
adding <a href="https://re.factorcode.org/2013/04/bitwise-permutations.html">bitwise
permutations</a>
(John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.factorials.html">math.factorials</a>:
adding <a href="https://re.factorcode.org/2013/04/factorial.html">lots of factorial
words</a> (John
Benediktsson</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.hashcodes.html">math.hashcodes</a>:
consistent number hashing (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.transforms.bwt.html">math.transforms.bwt</a>:
adding Burrows-Wheeler transform (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-pdf.html">pdf</a>: adding a
pdf render vocab (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-random.c.html">random.c</a>:
prototype of “libc” random number generator (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-reddit.html">reddit</a>: add
a <a href="https://reddit.com">Reddit</a> API wrapper (John Benediktsson and
Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.rotated.html">sequences.rotated</a>:
adding rotated virtual sequence (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.shifted.html">sequences.shifted</a>:
adding shifted virtual sequence (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.snipped.html">sequences.snipped</a>:
adding snipped virtual sequence (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.zipped.html">sequences.zipped</a>:
adding zipped virtual sequence (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-sets.extras.html">sets.extras</a>:
new words (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-terminal.html">terminal</a>:
adding terminal-size support (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.which.html">tools.which</a>:
adding a <a href="https://re.factorcode.org/2013/01/which.html">“which”
command</a> (John
Benediktsson)</li>
</ul>
Factorial
https://re.factorcode.org/2013/04/factorial.html
Mon, 15 Apr 2013 10:43:00 -0700https://re.factorcode.org/2013/04/factorial.html<p>Calculating <a href="https://en.wikipedia.org/wiki/Factorial">factorial numbers</a>
is frequently used to compare programming languages. Usually the
implementation is simple, as is the one in
<a href="https://factorcode.org">Factor</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MEMO:</span> <span class="nf">factorial</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">n!</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">1 </span><span class="nb">> </span>[ [1..b] <span class="nb">product </span>] [ <span class="nb">drop </span><span class="m">1 </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>I hadn’t realized until I skimmed the <a href="https://en.wikipedia.org/wiki/Factorial">Wikipedia article on
Factorials</a> that there are
actually many more kinds of factorials and what
<a href="https://factorcode.org">Factor</a> really needed was implementations of all
of them!</p>
<h3 id="lots-of-factorials">Lots of Factorials</h3>
<p><strong>primorial</strong></p>
<blockquote>
<p>Similar to the factorial, the
<a href="https://en.wikipedia.org/wiki/Primorial">primorial</a> is the product of
the first <em>n</em> prime numbers.</p>
</blockquote>
<p><strong>double-factorial</strong></p>
<blockquote>
<p>The product of all the odd integers up to some odd positive integer
<em>n</em> is called the <a href="https://en.wikipedia.org/wiki/Double_factorial">double
factorial</a> of <em>n</em>, and
denoted by <em>n!!</em>.</p>
</blockquote>
<p><strong>multifactorial</strong></p>
<blockquote>
<p>The
<a href="https://en.wikipedia.org/wiki/Factorial#Multifactorials">multifactorial</a>
is a product of integers in steps of two (<em>n!!</em>, the “double
factorial”), three (<em>n!!!</em>), or more (in general for a given <em>k</em> step:
<em>n!<sup>(k)</sup></em>).</p>
</blockquote>
<p><strong>quadruple-factorial</strong></p>
<blockquote>
<p>The so-called <a href="https://en.wikipedia.org/wiki/Factorial#Quadruple_factorial">quadruple
factorial</a>,
however, is not the multifactorial <em>n!<sup>(4)</sup></em>; it is a much
larger number given by <em>(2n)!/n!</em>.</p>
</blockquote>
<p><strong>super-factorial</strong></p>
<blockquote>
<p>The <a href="https://en.wikipedia.org/wiki/Factorial#Superfactorial">super
factorial</a> is
the product of the first <em>n</em> factorials.</p>
</blockquote>
<p><strong>hyper-factorial</strong></p>
<blockquote>
<p>The <a href="https://en.wikipedia.org/wiki/Factorial#Hyperfactorial">hyper
factorial</a> is
defined as:</p>
<p>
<img src="https://re.factorcode.org/images/2013-04-15-factorial-749f9973c81bee8a558471a38a6e54f7.png" alt="" width="385" height="49" />
</p>
</blockquote>
<p><strong>alternating-factorial</strong></p>
<blockquote>
<p>The <a href="https://en.wikipedia.org/wiki/Alternating_factorial">alternating
factorial</a> is the
absolute value of the alternating sum of the first n factorials.</p>
<p>
<img src="https://re.factorcode.org/images/2013-04-15-factorial-ed82e9ea4d3132f16be1bc4d114d1cc0.png" alt="" width="176" height="55" />
</p>
</blockquote>
<p><strong>exponential-factorial</strong></p>
<blockquote>
<p>The <a href="https://en.wikipedia.org/wiki/Exponential_factorial">exponential
factorial</a> is a
positive integer <em>n</em> raised to the power of <em>n−1</em>, which in turn is
raised to the power of <em>n − 2</em>, and so on and so forth.</p>
<p><em>Be careful with n > 4</em>: the exponential factorial of 5 is
5<sup>262144</sup> which is approximately 6.206069878660874 ×
10<sup>183230</sup>.</p>
</blockquote>
<p><strong>factorial-prime?</strong></p>
<blockquote>
<p>A <a href="https://en.wikipedia.org/wiki/Factorial_prime">factorial prime</a> is a
prime number that is one less or one more than a factorial.</p>
</blockquote>
<p><strong>primorial-prime?</strong></p>
<blockquote>
<p>A <a href="https://en.wikipedia.org/wiki/Primorial_prime">primorial prime</a> is a
prime number that is one less or one more than a primorial.</p>
</blockquote>
<p><strong>falling-factorial</strong></p>
<blockquote>
<p>The <em>“descending factorial”, “falling sequential product”, or “lower
factorial”</em>.</p>
<p>
<img src="https://re.factorcode.org/images/2013-04-15-factorial-993d1de3aa68728983bb51a738e78b5a.png" alt="" width="317" height="21" />
</p>
</blockquote>
<p><strong>factorial-power</strong></p>
<blockquote>
<p>A generalized version of <a href="https://mathworld.wolfram.com/FallingFactorial.html">falling
factorial</a>.</p>
</blockquote>
<p><strong>rising-factorial</strong></p>
<blockquote>
<p>The <em>“ascending factorial”, “rising sequential product”, or “upper
factorial”</em>.</p>
<p>
<img src="https://re.factorcode.org/images/2013-04-15-factorial-bcac5397cbea0183addda73c61662385.png" alt="" width="320" height="25" />
</p>
</blockquote>
<h3 id="now-available">Now available!</h3>
<p>That’s probably more factorials than anyone really cares to know about,
but now <a href="https://factorcode.org">Factor</a> has more than ten times as many
factorials as before! The code could probably use some cleanup, but it
is available now in the
<a href="https://github.com/factor/factor/blob/master/extra/math/factorials/factorials.factor">math.factorials</a>
vocabulary.</p>
Bitwise permutations
https://re.factorcode.org/2013/04/bitwise-permutations.html
Thu, 11 Apr 2013 11:31:00 -0700https://re.factorcode.org/2013/04/bitwise-permutations.html<p>Using <a href="https://graphics.stanford.edu/~seander/bithacks.html">bit twiddling
hacks</a> can be fun.
When I noticed <a href="https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation">compute the lexicographically next bit
permutation</a>,
I knew it could be a neat feature for <a href="https://factorcode.org">Factor</a>.</p>
<p>The idea is that given a number like 7 (represented as <code>00111</code> in bits),
the next few
<a href="https://en.wikipedia.org/wiki/Lexicographical_order">lexicographic</a>
permutations of numbers with 3 bits set would be <code>01011</code>, <code>01101</code>,
<code>01110</code>, <code>10011</code>, <code>10101</code>, <code>10110</code>.</p>
<h3 id="next-permutation">Next Permutation</h3>
<p>The “bit hacks” website uses this C code to calculate the next
permutation of bits:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">v</span><span class="p">;</span> <span class="c1">// current permutation of bits
</span></span></span><span class="line"><span class="cl"><span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">w</span><span class="p">;</span> <span class="c1">// next permutation of bits
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">t</span> <span class="o">=</span> <span class="p">(</span><span class="n">v</span> <span class="o">|</span> <span class="p">(</span><span class="n">v</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">w</span> <span class="o">=</span> <span class="n">t</span> <span class="o">|</span> <span class="p">((((</span><span class="n">t</span> <span class="o">&</span> <span class="o">-</span><span class="n">t</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">v</span> <span class="o">&</span> <span class="o">-</span><span class="n">v</span><span class="p">))</span> <span class="o">>></span> <span class="mi">1</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span></span></code></pre></div><p>A direct translation into <a href="https://factorcode.org">Factor</a> looks like
this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-permutation-bits</span> <span class="nf">( </span><span class="nv">v</span> <span class="nf">-- </span><span class="nv">w</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span><span class="m">1 </span><span class="nb">- bitor </span><span class="m">1 </span><span class="nb">+ dup </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup neg bitand </span>] <span class="nb">bi@ /i 2/ </span><span class="m">1 </span><span class="nb">- bitor </span><span class="k">;
</span></span></span></code></pre></div><h3 id="permutation-words">Permutation Words</h3>
<p>Factor has a
<a href="https://docs.factorcode.org/content/vocab-math.combinatorics.html">math.combinatorics</a>
vocabulary containing useful operations on permutations and combinations
for sequences. Now that we have a way of obtaining the next bitwise
permutation of a number, I thought we should have similar words for
operating on permutations of bits.</p>
<p>A helper word takes <code>bit-count</code> (the number of set bits) and <code>bits</code> (the
overall number of bits), and <code>quot</code> (a
<a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a>
to apply to each permutation), returning an initial starting
permutation, a predicate to continue as long as we are generating valid
numbers, and a body that applies the quotation then calculates the next
permutation in the sequence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">permutation-bits-quot</span> <span class="nf">( </span><span class="nv">bit-count</span> <span class="nv">bits</span> <span class="nv">quot</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nv">pred</span> <span class="nv">body</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ on-bits <span class="nb">dup </span>'[ <span class="nb">dup </span>_ <span class="nb">>= </span>] ] [ on-bits ] <span class="nb">bi* </span>] <span class="nb">dip swap
</span></span></span><span class="line"><span class="cl"> '[ _ [ next-permutation-bits _ <span class="nb">bitand </span>] <span class="nb">bi </span>] <span class="k">; inline
</span></span></span></code></pre></div><p>That might look a little complicated, but it makes these operations
fairly simple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">each-permutation-bits</span> <span class="nf">( </span><span class="nv">bit-count</span> <span class="nv">bits</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- ) -- )
</span></span></span><span class="line"><span class="cl"> permutation-bits-quot <span class="nb">while drop </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">map-permutation-bits</span> <span class="nf">( </span><span class="nv">bit-count</span> <span class="nv">bits</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">m</span> <span class="nf">) -- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> permutation-bits-quot [ <span class="nb">swap </span>] <span class="nb">compose produce nip </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">filter-permutation-bits</span> <span class="nf">( </span><span class="nv">bit-count</span> <span class="nv">bits</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">) -- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">selector </span>[ each-permutation-bits ] <span class="nb">dip </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-permutation-bits</span> <span class="nf">( </span><span class="nv">bit-count</span> <span class="nv">bits</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ ] map-permutation-bits <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-permutation-bits</span> <span class="nf">( </span><span class="nv">bit-count</span> <span class="nv">bits</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">) -- </span><span class="nv">elt/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="no">f f </span>] <span class="nb">3dip </span>[ <span class="nb">2nip </span>] <span class="nb">prepose </span>[ <span class="nb">keep swap </span>] <span class="nb">curry
</span></span></span><span class="line"><span class="cl"> permutation-bits-quot [ [ <span class="nb">pick not and </span>] <span class="nb">compose </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> <span class="nb">while drop swap and </span><span class="k">; inline
</span></span></span></code></pre></div><h3 id="try-it">Try It</h3>
<p>You can then do things like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="c">! find all 5-bit numbers with 3 bits set</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">3 5 </span>all-permutation-bits <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">7 11 13 14 19 21 22 25 26 28 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">! find the first 5-bit number with 3 bits set, multiples of 5</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">3 5 </span>[ <span class="m">5 </span>divisor? ] find-permutation-bits <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">25
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">! print all the even 5-bit numbers with 3 bits set</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">3 5 </span>[ <span class="nb">even? </span>] filter-permutation-bits [ .b ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"><span class="m">01110
</span></span></span><span class="line"><span class="cl"><span class="m">10110
</span></span></span><span class="line"><span class="cl"><span class="m">11010
</span></span></span><span class="line"><span class="cl"><span class="m">11100
</span></span></span></code></pre></div><p>This is available in the main repository in the
<a href="https://github.com/factor/factor/blob/master/extra/math/combinatorics/bits/bits.factor">math.combinatorics.bits</a>
vocabularly.</p>
move
https://re.factorcode.org/2013/04/move.html
Tue, 02 Apr 2013 11:08:00 -0700https://re.factorcode.org/2013/04/move.html<p>I’ve used <a href="https://factorcode.org">Factor</a> to build several common unix
programs including
<a href="https://re.factorcode.org/2012/02/copy.html">copy</a>,
<a href="https://re.factorcode.org/2010/08/building-cat.html">cat</a>,
<a href="https://re.factorcode.org/2010/09/fortune-telling.html">fortune</a>,
<a href="https://re.factorcode.org/2011/11/wc-l.html">wc</a>, and
<a href="https://github.com/mrjbq7/re-factor/tree/master/unix-tools">others</a>.</p>
<p>Today, I wanted to show how to build the <code>mv</code> (“move”) program using the
simple <a href="https://re.factorcode.org/2011/09/manipulating-files.html">file
manipulation</a>
words available in <a href="https://factorcode.org">Factor</a>. If we look at the
<a href="https://linux.die.net/man/1/mv">man page</a>, we can see that its usage is
two-fold:</p>
<ol>
<li>Rename a source file to a destination file</li>
<li>Move source file(s) to a destination directory</li>
</ol>
<p>We can make a nice usage string to display if the arguments are not
correct:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">usage</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"Usage: move source ... target"</span> <span class="nb">print </span><span class="k">;
</span></span></span></code></pre></div><p>Moving files into a directory (and displaying the usage if the
destination is not a directory):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">move-to-dir</span> <span class="nf">( </span><span class="nv">args</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup last </span>file-info directory?
</span></span><span class="line"><span class="cl"> [ <span class="nb">unclip-last </span>move-files-into ] [ <span class="nb">drop </span>usage ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>If we specify two arguments, we are either moving a single file into a
directory, or renaming a file using the
<a href="https://docs.factorcode.org/content/word-move-file,io.directories.html">move-file</a>
word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">move-to-file</span> <span class="nf">( </span><span class="nv">args</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup last </span>file-info directory?
</span></span><span class="line"><span class="cl"> [ move-to-dir ] [ <span class="nb">first2 </span>move-file ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>A “main” word checks the number of arguments and performs the
appropriate action:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">run-move</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> command-line <span class="nb">get dup length </span>{
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">2 </span><span class="nb">> </span>] [ <span class="nb">drop </span>move-to-dir ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">2 </span><span class="nb">= </span>] [ <span class="nb">drop </span>move-to-file ] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">2drop </span>usage ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">run-move</span>
</span></span></code></pre></div><p>An improvement that could be made would be producing better error
messages when files don’t exist. Something like the errors <code>mv</code>
produces:</p>
<pre tabindex="0"><code>$ mv src dst
mv: rename src to dst: No such file or directory
</code></pre><p>The code for this is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/unix-tools/move/move.factor">GitHub</a>.</p>
Improved hash-sets
https://re.factorcode.org/2013/03/improved-hash-sets.html
Fri, 22 Mar 2013 10:41:00 -0700https://re.factorcode.org/2013/03/improved-hash-sets.html<p>For quite a long time, the
<a href="https://docs.factorcode.org/content/article-hash-sets.html">hash-sets</a>
vocabulary in <a href="https://factorcode.org">Factor</a> has had this note at the
top:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="c">! In a better implementation, less memory would be used</span>
</span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">hash-set</span> { <span class="nv">table</span> hashtable read-only } <span class="k">;
</span></span></span></code></pre></div><p>By wrapping a
<a href="https://docs.factorcode.org/content/article-hashtables.html">hashtable</a>,
we had a pleasantly simple set implementation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">hash-set</span> <span class="nf">in?</span> table>> <span class="nb">key? </span><span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">hash-set</span> <span class="nf">adjoin</span> table>> <span class="nb">dupd set-at </span><span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">hash-set</span> <span class="nf">delete</span> table>> <span class="nb">delete-at </span><span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">hash-set</span> <span class="nf">members</span> table>> <span class="nb">keys </span><span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">hash-set</span> <span class="nf">null?</span> table>> <span class="nb">assoc-empty? </span><span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">hash-set</span> <span class="nf">cardinality</span> table>> <span class="nb">assoc-size </span><span class="k">;
</span></span></span></code></pre></div><p>Unfortunately, this implementation had the consequence of increased
memory usage (members of the set would be stored twice - as both key and
value in the hashtable) and some performance implications due to
collecting the underlying hashtable into an array and then throwing out
the values to return only the keys.</p>
<p>After a <a href="https://github.com/factor/factor/commit/ede0232ddc29702cd7d561548541219c3b9bce10">new hash-set
implementation</a>
and a
<a href="https://github.com/factor/factor/commit/db4e6592a2075b1ec0291e96f1a26b1068ef65ac">series</a>
of
<a href="https://github.com/factor/factor/commit/64489d93c78334ba82de5ae0ca65f3005d4f5101">minor</a>
<a href="https://github.com/factor/factor/commit/16f7850590ece09d41a9f6e31f6af7c87f6ee79f">improvements</a>,
we had a greatly improved hash-set. While working on this, I realized we
could speed up <a href="https://github.com/factor/factor/commit/a75d852a89b9f6ccd93a7f4d329f607e172a8de2">growing the backing
array</a>
and applied a <a href="https://github.com/factor/factor/commit/684b897dead302373e595034394c36460780c049">similar improvement to
hashtables</a>.</p>
<p>Some performance results using our
<a href="https://github.com/factor/factor/blob/master/extra/benchmark/hash-sets/hash-sets.factor">benchmark.hash-sets</a>
test:</p>
<p><strong>Before:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ hash-sets-benchmark ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">3.066324117 </span>seconds
</span></span></code></pre></div><p><strong>After:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ hash-sets-benchmark ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.746381819 </span>seconds
</span></span></code></pre></div><p>I have since converted various parts of the compiler infrastructure to
use hash-sets which had previously used hashtables for set operations
and have gotten some overall performance improvements. This is available
now in the <a href="https://github.com/factor/factor">development version of
Factor</a>.</p>
Faster "shuffle"
https://re.factorcode.org/2013/02/faster-shuffle.html
Wed, 27 Feb 2013 17:24:00 -0800https://re.factorcode.org/2013/02/faster-shuffle.html<p>A funny comment on a <a href="https://www.reddit.com/r/programming/comments/19azqd/comparing_performance_of_shuffle_in_ruby_python/">Reddit
discussion</a>
of my <a href="https://re.factorcode.org/2013/02/fast-shuffle.html">last post about
“shuffle”</a>
said:</p>
<blockquote>
<p><em>“Turtle races are fun indeed.”</em></p>
</blockquote>
<h3 id="improvements">Improvements</h3>
<p>Well, that is perhaps true given the performance of Factor, Python, and
Ruby compared with implementations in C (or SBCL and LuaJIT). However,
I’m happy to say that as of today, the performance of
<a href="https://docs.factorcode.org/content/word-randomize,random.html">randomize</a>
(the standard library “shuffle” word) in <a href="https://factorcode.org">Factor</a>
is greatly improved.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10,000,000 </span><iota> <span class="nb">>array
</span></span></span></code></pre></div><p><strong>Before:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ randomize ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">2.78315852 </span>seconds
</span></span></code></pre></div><p><strong>After:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> gc [ randomize ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">1.373153681 </span>seconds
</span></span></code></pre></div><h3 id="commits">Commits</h3>
<p>So, what changed?</p>
<p>Well, while investigating the overhead of Factor versus C, I fixed a few
things:</p>
<ul>
<li><a href="https://github.com/factor/factor/commit/fd1d451162668e7c1be598032ab95c04a13279f9">speed up
random-integer</a></li>
<li><a href="https://github.com/factor/factor/commit/ceb1b0534fea6fb77f1fdd42af67b4c7833d4211">speed up
uniform-random-float</a>
(not relevant to randomize, but a nice speedup!)</li>
<li><a href="https://github.com/factor/factor/commit/de69c55e0b986b0b7ce47007487de561e7f289a4">speed up
randomize</a>
(similar to my <a href="https://re.factorcode.org/2013/02/fast-shuffle.html">version
4</a> of
“shuffle”)</li>
<li><a href="https://github.com/factor/factor/commit/4da316cf1b88c70b74fcbcc41de2534e47ae1cfd">speed up
fixnum-log2</a></li>
<li><a href="https://github.com/factor/factor/commit/d92e9ca8534ca2c592c602701876acf521de1f76">re-enable fixnum-log2
intrinsic</a></li>
</ul>
<p>Looking at other programming languages is useful to know where your
performance can improve and by approximately how much. Since
<a href="https://factorcode.org">Factor</a> is an attempt at making a high level
language that produces relatively fast code, I hope someday to turn this
“turtle” into a “hare”. Every little bit helps!</p>
Fast "shuffle"
https://re.factorcode.org/2013/02/fast-shuffle.html
Tue, 26 Feb 2013 15:28:00 -0800https://re.factorcode.org/2013/02/fast-shuffle.html<p>Randomly shuffling the elements in an array is a pretty common task. The
standard algorithm for this is called the <a href="https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle">Fisher-Yates
shuffle</a>. The modern
version of it looks like this in pseudo-code:</p>
<pre tabindex="0"><code>To shuffle an array a of n elements (indices 0..n-1):
for i from n − 1 downto 1 do
j ← random integer with 0 ≤ j ≤ i
exchange a[j] and a[i]
</code></pre><p>We’re going to implement this in <a href="https://factorcode.org">Factor</a>, and
then look at improving the performance when shuffling an array of
10,000,000 numbers (generated with “<code>10,000,000 <iota> >array</code>”).
Afterwards, we are going to look at Ruby and Python versions.</p>
<h3 id="factor">Factor</h3>
<p><strong>Version 1:</strong></p>
<p>Our first implementation is a straight-forward translation of the
algorithm (a simplification of the
<a href="https://docs.factorcode.org/content/word-randomize,random.html">randomize</a>
word included in the Factor standard library), and looks something like
this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">shuffle1</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>[ <span class="nb">dup </span><span class="m">1 </span><span class="nb">> </span>] [
</span></span><span class="line"><span class="cl"> [ random ] [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">pick exchange </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while drop </span><span class="k">;
</span></span></span></code></pre></div><p>On our test case, this takes <strong>2.972</strong> seconds.</p>
<p><strong>Version 2:</strong></p>
<p>Our second implementation uses
<a href="https://docs.factorcode.org/content/word-exchange-unsafe,sequences.private.html">exchange-unsafe</a>,
which does not perform bounds checks (unnecessary given our loop
constraints).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">shuffle2</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>[ <span class="nb">dup </span><span class="m">1 </span><span class="nb">> </span>] [
</span></span><span class="line"><span class="cl"> [ random ] [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">pick </span>exchange-unsafe ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while drop </span><span class="k">;
</span></span></span></code></pre></div><p>On our test case, this takes <strong>2.830</strong> seconds (a 5% improvement on
version 1!).</p>
<p><strong>Version 3:</strong></p>
<p>Our third implementation uses the
<a href="https://docs.factorcode.org/content/article-typed.html">typed</a>
vocabulary to provide type information to the compiler to produce more
optimal code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TYPED:</span> <span class="nf">shuffle3</span> <span class="nf">( </span><span class="nv">seq:</span> <span class="nv">array</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>[ <span class="nb">dup </span><span class="m">1 </span><span class="nb">> </span>] [
</span></span><span class="line"><span class="cl"> [ random ] [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">pick </span>exchange-unsafe ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while drop </span><span class="k">;
</span></span></span></code></pre></div><p>On our test case, this takes <strong>2.554</strong> seconds (a 15% improvement on
version 1!).</p>
<p><strong>Version 4:</strong></p>
<p>Our fourth implementation instead removes the generic dispatch of
calling
<a href="https://docs.factorcode.org/content/word-random,random.html">random</a> as
well as the dynamic variable lookup for the current random number
generator:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">shuffle4</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>[ <span class="nb">dup </span><span class="m">1 </span><span class="nb">> </span>]
</span></span><span class="line"><span class="cl"> random-generator <span class="nb">get </span>'[
</span></span><span class="line"><span class="cl"> [ _ (random-integer) ] [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">pick </span>exchange-unsafe ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while drop </span><span class="k">;
</span></span></span></code></pre></div><p>On our test case, this takes <strong>2.408</strong> seconds (a 19% improvement on
version 1!).</p>
<p><strong>Version 5:</strong></p>
<p>Our fifth implementation combines the optimizations in version 3 and 4:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TYPED:</span> <span class="nf">shuffle5</span> <span class="nf">( </span><span class="nv">seq:</span> <span class="nv">array</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>[ <span class="nb">dup </span><span class="m">1 </span><span class="nb">> </span>]
</span></span><span class="line"><span class="cl"> random-generator <span class="nb">get </span>'[
</span></span><span class="line"><span class="cl"> [ _ (random-integer) ] [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">pick </span>exchange-unsafe ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while drop </span><span class="k">;
</span></span></span></code></pre></div><p>On our test case, this takes <strong>2.254</strong> seconds (a 24% improvement on
version 1!).</p>
<p><strong>Version 6:</strong></p>
<p>Our sixth implementation declares that the indices to
<a href="https://docs.factorcode.org/content/word-exchange-unsafe,sequences.private.html">exchange-unsafe</a>
are <a href="https://docs.factorcode.org/content/word-fixnum,math.html">fixnums</a>
(removing a minor check that isn’t optimized out by the compiler for
some reason):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TYPED:</span> <span class="nf">shuffle6</span> <span class="nf">( </span><span class="nv">seq:</span> <span class="nv">array</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>[ <span class="nb">dup </span><span class="m">1 </span><span class="nb">> </span>]
</span></span><span class="line"><span class="cl"> random-generator <span class="nb">get </span>'[
</span></span><span class="line"><span class="cl"> [ _ (random-integer) ] [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> { <span class="nb">fixnum fixnum </span>} declare
</span></span><span class="line"><span class="cl"> [ <span class="nb">pick </span>exchange-unsafe ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while drop </span><span class="k">;
</span></span></span></code></pre></div><p>On our test case, this takes <strong>2.187</strong> seconds (a 27% improvement on
version 1!).</p>
<p><strong>Version 7:</strong></p>
<p>Our seventh implementation is just version 6 with a quick-and-dirty
random number generator using <a href="https://linux.die.net/man/3/rand">rand()</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">FUNCTION: int rand <span class="nf">( )
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">SINGLETON:</span> <span class="nc">c-random</span>
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">c-random</span> <span class="nf">random-32*</span> <span class="nb">drop </span>rand <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">with-c-random</span> <span class="nf">( </span><span class="nv">quot</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ c-random ] <span class="nb">dip </span>with-random <span class="k">; inline
</span></span></span></code></pre></div><p>On our test case, this takes <strong>2.015</strong> seconds (a 32% improvement on
version 1!).</p>
<p><strong>Version 8:</strong></p>
<p>Our eighth (and final!) version drops down to C, to implement a
<a href="https://docs.factorcode.org/content/article-primitives.html">primitive</a>
version of shuffle:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">factor_vm</span><span class="o">::</span><span class="n">primitive_shuffle_array</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">array</span><span class="o">*</span> <span class="n">a</span> <span class="o">=</span> <span class="n">untag_check</span><span class="o"><</span><span class="n">array</span><span class="o">></span><span class="p">(</span><span class="n">ctx</span><span class="o">-></span><span class="n">peek</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"> <span class="n">cell</span> <span class="n">capacity</span> <span class="o">=</span> <span class="n">array_capacity</span><span class="p">(</span><span class="n">a</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="n">cell</span> <span class="n">i</span> <span class="o">=</span> <span class="n">capacity</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">></span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">cell</span> <span class="n">j</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="n">rand</span><span class="p">()</span> <span class="o">/</span> <span class="p">(</span><span class="n">RAND_MAX</span> <span class="o">/</span> <span class="p">(</span><span class="n">capacity</span> <span class="o">-</span> <span class="n">i</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="n">cell</span> <span class="n">tmp</span> <span class="o">=</span> <span class="n">array_nth</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="n">set_array_nth</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">array_nth</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">j</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"> <span class="n">set_array_nth</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">j</span><span class="p">,</span> <span class="n">tmp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>On our test case, this takes <strong>0.494</strong> seconds (a 83% improvement on
version 1!).</p>
<h3 id="python">Python</h3>
<p>I wanted to see how various <a href="https://python.org">Python</a> versions
performed, so I wrote a simple version that takes <strong>19.025</strong> seconds on
Python 2.7.3, <strong>12.436</strong> seconds on Jython 2.5.3, and <strong>1.392</strong> seconds
with PyPy 1.9.0:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randrange</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">n</span> <span class="o">=</span> <span class="mi">10000000</span>
</span></span><span class="line"><span class="cl"><span class="n">l</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">xrange</span><span class="p">(</span><span class="n">n</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">t0</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">i</span> <span class="o">=</span> <span class="n">n</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="n">i</span> <span class="o">></span> <span class="mi">1</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">j</span> <span class="o">=</span> <span class="n">randrange</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">i</span> <span class="o">-=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="n">l</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">l</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">l</span><span class="p">[</span><span class="n">j</span><span class="p">],</span> <span class="n">l</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="s1">'took </span><span class="si">%.3f</span><span class="s1"> seconds'</span> <span class="o">%</span> <span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">t0</span><span class="p">)</span>
</span></span></code></pre></div><p>A version using <a href="https://numpy.org">Numpy</a> takes <strong>3.273</strong> seconds:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">a</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">10000000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">t0</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">shuffle</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="s1">'took </span><span class="si">%.3f</span><span class="s1"> seconds'</span> <span class="o">%</span> <span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">t0</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="ruby">Ruby</h3>
<p>Curious what Ruby would look like, I tested two versions. The first
implements shuffle in “pure” Ruby, taking <strong>149.975</strong> seconds on Ruby
1.8.7, <strong>25.086</strong> seconds in Ruby 1.9.3, <strong>8.054</strong> seconds on MacRuby
0.12, <strong>7.258</strong> seconds on JRuby 1.7.3, and <strong>4.682</strong> seconds on
Rubinius 1.2.4:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">shuffle!</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">size</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">length</span>
</span></span><span class="line"><span class="cl"> <span class="n">size</span><span class="o">.</span><span class="n">times</span> <span class="k">do</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="n">r</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="no">Kernel</span><span class="o">.</span><span class="n">rand</span><span class="p">(</span><span class="n">size</span> <span class="o">-</span> <span class="n">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">a</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="p">,</span> <span class="n">a</span><span class="o">[</span><span class="n">r</span><span class="o">]</span> <span class="o">=</span> <span class="n">a</span><span class="o">[</span><span class="n">r</span><span class="o">]</span><span class="p">,</span> <span class="n">a</span><span class="o">[</span><span class="n">i</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">a</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">10000000</span><span class="p">)</span><span class="o">.</span><span class="n">to_a</span>
</span></span><span class="line"><span class="cl"><span class="n">t0</span> <span class="o">=</span> <span class="no">Time</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="n">shuffle!</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">t1</span> <span class="o">=</span> <span class="no">Time</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="nb">printf</span> <span class="s2">"took %.3f seconds"</span><span class="p">,</span> <span class="n">t1</span><span class="o">-</span><span class="n">t0</span>
</span></span></code></pre></div><p>But since Ruby 1.8.7,
<a href="https://www.ruby-doc.org/core-2.0/Array.html#method-i-shuffle-21">Array.shuffle!</a>
is a builtin (<a href="https://github.com/ruby/ruby/blob/trunk/array.c#L4296">implemented in
C</a>), so lets look
at how performance differs. It takes <strong>0.443</strong> seconds in Ruby 1.8.7,
<strong>0.554</strong> seconds in Ruby 1.9.3, <strong>0.884</strong> seconds in MacRuby 0.12,
<strong>2.170</strong> seconds in JRuby 1.7.3, and <strong>3.479</strong> seconds in Rubinius
1.2.4:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">a</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">10000000</span><span class="p">)</span><span class="o">.</span><span class="n">to_a</span>
</span></span><span class="line"><span class="cl"><span class="n">t0</span> <span class="o">=</span> <span class="no">Time</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="n">a</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">shuffle!</span>
</span></span><span class="line"><span class="cl"><span class="n">t1</span> <span class="o">=</span> <span class="no">Time</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="nb">printf</span> <span class="s2">"took %.3f seconds"</span><span class="p">,</span> <span class="n">t1</span><span class="o">-</span><span class="n">t0</span>
</span></span></code></pre></div><h3 id="conclusion">Conclusion</h3>
<p><a href="https://en.wikipedia.org/wiki/C_(programming_language)">C</a> is awesome.
<a href="https://pypy.org">PyPy</a> is really fast. <a href="https://factorcode.org">Factor</a>
can be pretty fast. <a href="https://numpy.org">Numpy</a> should be faster.
<a href="https://jython.org">Jython</a> could be faster. <a href="https://python.org">Python</a>
isn’t very fast at all. <a href="https://ruby-lang.org">Ruby</a> is both blazing
fast and slower than snails!</p>
Typoglycemia
https://re.factorcode.org/2013/02/typoglycemia.html
Thu, 07 Feb 2013 16:59:00 -0800https://re.factorcode.org/2013/02/typoglycemia.html<p><a href="https://en.wikipedia.org/wiki/Typoglycemia">Typoglycemia</a> is the name
given to an internet meme that went around a few years ago, purporting
to demonstrate that “readers can understand the meaning of words in a
sentence even when the interior letters of each word are scrambled”.</p>
<p>Can you read this?</p>
<blockquote>
<p><em>…it deosn’t mttaer in waht oredr the ltteers in a wrod are, the
olny iprmoatnt tihng is taht the frist and lsat ltteer be in the rghit
pclae.</em></p>
</blockquote>
<p>Well, perhaps it isn’t completely true, but it at least is mostly true.
How about an implementation of this in <a href="https://factorcode.org">Factor</a>?</p>
<p>We will start by building a word to “misspell” a string that is at least
four letters long (ignoring any punctuation at the end) and randomizing
all the characters except the first and last:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">misspell-word</span> <span class="nf">( </span><span class="nv">word</span> <span class="nf">-- </span><span class="nv">word'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ <span class="s">",'.:;!?"</span> <span class="nb">member? not </span>] <span class="nb">find-last drop </span><span class="m">0 </span><span class="nb">or
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">2 </span><span class="nb">> </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">dupd head-slice dup </span>[ Letter? ] <span class="nb">all?
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">rest-slice </span>randomize ] <span class="nb">when drop
</span></span></span><span class="line"><span class="cl"> ] [ <span class="nb">drop </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Next, we will “misspell” a line of text:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">misspell-line</span> <span class="nf">( </span><span class="nv">line</span> <span class="nf">-- </span><span class="nv">line'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ blank? ] split-when [ misspell-word ] <span class="nb">map </span><span class="s">" "</span> <span class="nb">join </span><span class="k">;
</span></span></span></code></pre></div><p>Finally, misspelling a block of text:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">misspell</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">string'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> string-lines [ misspell-line ] <span class="nb">map </span><span class="s">"\n"</span> <span class="nb">join </span><span class="k">;
</span></span></span></code></pre></div><p>You can try it to show it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"this really works!"</span> misspell <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"this relaly wroks!"</span>
</span></span></code></pre></div><p>Maybe it would be easier to read if we just randomly swap two letters in
the middle of each word, rather than fully randomizing it?</p>
Crafting Code in Factor
https://re.factorcode.org/2013/02/crafting-code-in-factor.html
Fri, 01 Feb 2013 16:32:00 -0800https://re.factorcode.org/2013/02/crafting-code-in-factor.html<p>An interesting post about <a href="https://tapestryjava.blogspot.se/2013/02/crafting-code-in-clojure.html">crafting code in
Clojure</a>
discusses how <a href="https://clojure.org">Clojure</a> the language encourages code
that is “<em>concise, easier to read, and easier to maintain</em>”. I’ve
mentioned before how <a href="https://factorcode.org">Factor</a> encourages
<a href="https://re.factorcode.org/2012/02/readability.html">readability</a>,
but I thought I would show how an implementation of the author’s task in
Factor is quite simple.</p>
<p>The problem statement is to “<em>convert the keys of two maps into a
comma-separated string</em>” for nicer error messages. The specific tasks
outlined by the author are:</p>
<ul>
<li>Extract all the keys from both maps</li>
<li>Remove any duplicates</li>
<li>Convert the keys to strings</li>
<li>Sort the strings into ascending order</li>
<li>Build and return one big string, by concatenating all the key
strings, using “, " as a separator</li>
<li>Return “<none>” if both maps are empty</li>
</ul>
<h3 id="implementation">Implementation</h3>
<p>This specification can be translated more or less directly into Factor:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">sorted-key-list</span> <span class="nf">( </span><span class="nv">assoc1</span> <span class="nv">assoc2</span> <span class="nf">-- </span><span class="nv">string</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">keys </span>] <span class="nb">bi@ append </span>members [ <span class="s">"<none>"</span> ] [
</span></span><span class="line"><span class="cl"> [ present ] <span class="nb">map </span>natural-sort <span class="s">", "</span> <span class="nb">join
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-empty </span><span class="k">;
</span></span></span></code></pre></div><h3 id="testing">Testing</h3>
<p>And some unit tests to show it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="s">"<none>"</span> } [ H{ } H{ } sorted-key-list ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="s">"barney, fred, wilma"</span> } [
</span></span><span class="line"><span class="cl"> H{ { <span class="s">"fred"</span> <span class="no">t </span>} }
</span></span><span class="line"><span class="cl"> H{ { <span class="s">"barney"</span> <span class="no">t </span>} { <span class="s">"wilma"</span> <span class="no">t </span>} }
</span></span><span class="line"><span class="cl"> sorted-key-list
</span></span><span class="line"><span class="cl">] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="s">"fred"</span> } [ H{ { <span class="s">"fred"</span> <span class="no">t </span>} } H{ } sorted-key-list ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="s">"barney, fred"</span> } [
</span></span><span class="line"><span class="cl"> H{ { <span class="s">"fred"</span> <span class="no">t </span>} }
</span></span><span class="line"><span class="cl"> H{ { <span class="s">"fred"</span> <span class="no">t </span>} { <span class="s">"barney"</span> <span class="no">t </span>} }
</span></span><span class="line"><span class="cl"> sorted-key-list
</span></span><span class="line"><span class="cl">] unit-test
</span></span></code></pre></div><h3 id="errata">Errata</h3>
<p>An improved version of the Clojure version allows a variable number of
maps to be combined in this manner. To do so in Factor might use as a
macro, or instead just change the stack effect to take a sequence of
maps that are combined, something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">sorted-key-list2</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">string</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">keys </span>] <span class="nb">map concat </span>members [ <span class="s">"<none>"</span> ] [
</span></span><span class="line"><span class="cl"> [ present ] <span class="nb">map </span>natural-sort <span class="s">", "</span> <span class="nb">join
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-empty </span><span class="k">;
</span></span></span></code></pre></div>which
https://re.factorcode.org/2013/01/which.html
Fri, 11 Jan 2013 10:10:00 -0800https://re.factorcode.org/2013/01/which.html<p>Most of you are likely aware of the
<a href="https://linux.die.net/man/1/which">which</a> utility, used to find and
print “<em>the full path of the executables that would have been executed
when this argument had been entered at the shell prompt</em>”. As you might
imagine, I was curious what a cross-platform
<a href="https://factorcode.org">Factor</a> version would look like.</p>
<p>To determine if a given path points to an executable file, we want to
make sure it exists, is executable, and is not a directory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">executable?</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ exists? ]
</span></span><span class="line"><span class="cl"> [ file-executable? ]
</span></span><span class="line"><span class="cl"> [ file-info directory? <span class="nb">not </span>]
</span></span><span class="line"><span class="cl"> } 1&& <span class="k">;
</span></span></span></code></pre></div><p>The Windows convention is to separate a list of paths with a “<code>;</code>” and
on Mac and Linux to use a “<code>:</code>”. We’ll make a word to split the paths
appropriately:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">split-path</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> os windows? <span class="s">";"</span> <span class="s">":"</span> <span class="nb">? </span>split <span class="nb">harvest </span><span class="k">;
</span></span></span></code></pre></div><p>There is a concept of “path extensions” on Windows that we should
support. Basically, the <a href="https://environmentvariables.org/PathExt">PathExt environment
variable</a> contains a list of
file extensions that the operating system considers to be executable.
The command interpreter (cmd.exe) checks these extensions in order
looking for an executable. For example, if you type <code>PYTHON</code> at the
shell, it might look for the first to exist of <code>PYTHON.COM</code>,
<code>PYTHON.EXE</code>, <code>PYTHON.BAT</code>, and <code>PYTHON.CMD</code>.</p>
<p>We will optionally extend the list of commands to search for with those
extensions specified in the <code>PATHEXT</code> environment variable, but only if
the command does not have one of those extensions already:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">path-extensions</span> <span class="nf">( </span><span class="nv">command</span> <span class="nf">-- </span><span class="nv">commands</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"PATHEXT"</span> os-env [
</span></span><span class="line"><span class="cl"> split-path <span class="nb">2dup </span>[ [ >lower ] <span class="nb">bi@ tail? </span>] <span class="nb">with any?
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">drop 1array </span>] [ [ <span class="nb">append </span>] <span class="nb">with map </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] [ <span class="nb">1array </span>] <span class="nb">if* </span><span class="k">;
</span></span></span></code></pre></div><p>Building up our <code>which</code> word backwards, we will have an inner word that
takes a list of commands and a list of paths to check in the correct
order, returning the first path that is <code>executable?</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">((which))</span> <span class="nf">( </span><span class="nv">commands</span> <span class="nv">paths</span> <span class="nf">-- </span><span class="nv">file/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ normalize-path ] <span class="nb">map </span>members
</span></span><span class="line"><span class="cl"> <span class="nb">cartesian-product flip concat
</span></span></span><span class="line"><span class="cl"> [ prepend-path ] { } <span class="nb">assoc>map
</span></span></span><span class="line"><span class="cl"> [ executable? ] <span class="nb">find nip </span><span class="k">;
</span></span></span></code></pre></div><p>An outer word takes a single command and a string representing paths to
search in, adding the path extensions on Windows as well as making sure
we check the current directory first:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(which)</span> <span class="nf">( </span><span class="nv">command</span> <span class="nv">path</span> <span class="nf">-- </span><span class="nv">file/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> split-path os windows? [
</span></span><span class="line"><span class="cl"> [ path-extensions ] [ <span class="s">"."</span> <span class="nb">prefix </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> ] [ [ <span class="nb">1array </span>] <span class="nb">dip </span>] <span class="nb">if </span>((which)) <span class="k">;
</span></span></span></code></pre></div><p>And a simple public interface that checks for a single command against
the current search path:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">which</span> <span class="nf">( </span><span class="nv">command</span> <span class="nf">-- </span><span class="nv">file/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"PATH"</span> os-env (which) <span class="k">;
</span></span></span></code></pre></div><p>Here’s a few examples running on my laptop:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"python"</span> which <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"/usr/bin/python"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"ping"</span> which <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"/sbin/ping"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"does-not-exist"</span> which <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">f
</span></span></span></code></pre></div><p>This is implemented in the
<a href="https://github.com/factor/factor/blob/master/extra/tools/which/which.factor">tools.which</a>
vocabulary.</p>
Today In History
https://re.factorcode.org/2012/12/today-in-history.html
Wed, 19 Dec 2012 17:03:00 -0800https://re.factorcode.org/2012/12/today-in-history.html<p>For every day of the year, <a href="https://wikipedia.org">Wikipedia</a> maintains a
list of famous events, births, and deaths in history. For example, if
you look at <a href="https://en.wikipedia.org/wiki/December_19">today in
history</a>, you will see that in
1843, Charles Dickens novel, <em>A Christmas Carol</em> first went on sale.</p>
<p>Wouldn’t it be great if you could use <a href="https://factorcode.org">Factor</a> to
parse and display these famous historical events?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">calendar</span> <span class="nn">formatting</span> <span class="nn">http.client</span> <span class="nn">io</span> <span class="nn">kernel</span>
</span></span><span class="line"><span class="cl"><span class="nn">sequences</span> <span class="nn">xml</span> <span class="nn">xml.traversal</span> <span class="k">;
</span></span></span></code></pre></div><p>Using our
<a href="https://docs.factorcode.org/content/article-calendar.html">calendar</a>
vocabulary, we will build a word that converts a timestamp into a
Wikipedia URL:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">historical-url</span> <span class="nf">( </span><span class="nv">timestamp</span> <span class="nf">-- </span><span class="nv">url</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ month-name ] [ day>> ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://en.wikipedia.org/wiki/%s_%s"</span> sprintf <span class="k">;
</span></span></span></code></pre></div><p>If you look at the source for these Wikipedia pages, you will find that
it contains a series of <a href="https://www.w3schools.com/tags/tag_ul.asp">unordered
lists</a>. In particular, we are
interested in the second list containing “Events”. The third list
contains “Births” and the fourth contains “Deaths”.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">historical-events</span> <span class="nf">( </span><span class="nv">timestamp</span> <span class="nf">-- </span><span class="nv">events</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> historical-url http-get <span class="nb">nip </span>string>xml
</span></span><span class="line"><span class="cl"> <span class="s">"ul"</span> deep-tags-named <span class="nb">second </span>children-tags
</span></span><span class="line"><span class="cl"> [ deep-children>string ] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>Sometimes it is a nice convention to build a word that produces output:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">historical-events.</span> <span class="nf">( </span><span class="nv">timestamp</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> historical-events [ <span class="nb">print </span>] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>You can try this out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> today historical-events.
</span></span><span class="line"><span class="cl"><span class="m">211 </span>– Publius Septimius Geta, co-emperor <span class="nb">of </span>Rome, is lured
</span></span><span class="line"><span class="cl">to come without his bodyguards to meet his brother Marcus
</span></span><span class="line"><span class="cl">Aurelius Antoninus (Caracalla), to discuss a possible
</span></span><span class="line"><span class="cl">reconciliation. When he arrives the Praetorian Guard murders
</span></span><span class="line"><span class="cl">him <span class="nb">and </span>he dies in the arms <span class="nb">of </span>his mother Julia Domna.
</span></span><span class="line"><span class="cl"><span class="m">324 </span>– Licinius abdicates his position as Roman Emperor.
</span></span><span class="line"><span class="cl"><span class="m">1154 </span>– Henry II <span class="nb">of </span>England is crowned <span class="nb">at </span>Westminster Abbey.
</span></span><span class="line"><span class="cl"><span class="m">1490 </span>– Anne, Duchess <span class="nb">of </span>Brittany, is married to Maximilian I,
</span></span><span class="line"><span class="cl">Holy Roman Emperor by proxy.
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/wikipedia/wikipedia.factor">GitHub</a>,
along with words to output historical births and deaths and formatted
output that includes links to Wikipedia.</p>
New Logo
https://re.factorcode.org/2012/11/new-logo.html
Fri, 30 Nov 2012 16:02:00 -0800https://re.factorcode.org/2012/11/new-logo.html<p>For a long time now, the <a href="https://factorcode.org">Factor</a> logo has been a
cool looking <a href="https://en.wikipedia.org/wiki/Velociraptor">velociraptor</a>
designed in 2007 by <a href="https://fun-factor.blogspot.com/2007/09/logo-rama.html">Elie
Chaftari</a>. As
part of <a href="https://re.factorcode.org/2012/11/retina-displays.html">supporting retina
displays</a>,
we need new higher resolution icons. Unfortunately, Elie doesn’t have
the original artwork anymore to produce them.</p>
<p>With the help of <a href="https://99designs.com">99designs</a>, we ran a contest to
create a new logo and icon for the Factor programming language (to
capture the increase in speed and functionality gained in the last few
years).</p>
<p>This is our new logo:</p>
<p>
<img src="https://re.factorcode.org/images/2012-11-30-new-logo-logo.png" alt="" width="583" height="150" />
</p>
<p>And incorporating the logo into a new application icon:</p>
<p>
<img src="https://re.factorcode.org/images/2012-11-30-new-logo-icon_256x256.png" alt="" width="256" height="256" />
</p>
<p>The <a href="https://factorcode.org">website has been updated</a> and the latest
development version <a href="https://github.com/factor/factor/commit/708313fb00ab0f684b96e7ea5c5d3d9269c03e79">includes new icon
files</a>.</p>
Retina Displays
https://re.factorcode.org/2012/11/retina-displays.html
Thu, 15 Nov 2012 12:08:00 -0800https://re.factorcode.org/2012/11/retina-displays.html<p>I recently purchased a MacBook Pro with Retina Display. Shortly after
receiving it, I realized that <a href="https://factorcode.org">Factor</a> did not
support rendering on its Retina Display. Magnified (and blurry) text
looks pretty terrible next to the crisp native fonts on these HiDPI
displays.</p>
<p>Well, after a <a href="https://github.com/factor/factor/compare/9e0439e24141a87cc52e743941ef58136f3a185c...879177f1894f623dc57c456537e31c51f451e401">series of
commits</a>,
the latest development version of Factor now supports it! There are
still a few minor rendering issues with our newly supported <a href="https://en.wikipedia.org/wiki/Resolution_independence">“resolution
independence”</a>,
but we plan on fixing those in the near future.</p>
<p>To use the development version, follow the <a href="https://github.com/factor/factor#building-factor-from-source">directions from the
README</a>.</p>
The 3n+1 Problem
https://re.factorcode.org/2012/10/the-3n-1-problem.html
Thu, 25 Oct 2012 12:20:00 -0700https://re.factorcode.org/2012/10/the-3n-1-problem.html<p>Yesterday, a lengthy <a href="https://blog.racket-lang.org/2012/10/the-3n1-problem_4990.html">blog
post</a>
from the <a href="https://racket-lang.org">Racket</a> community discussed the “3n+1”
problem (also known as the <a href="https://en.wikipedia.org/wiki/Collatz_conjecture">Collatz
conjecture</a>):</p>
<blockquote>
<p>Consider a positive number n. The cycle length of n is the number of
times we repeat the following, until we reach n=1:</p>
<ul>
<li>If n is odd, then n = 3n+1</li>
<li>If n is even, then n = n/2</li>
</ul>
<p>For example, given n=22, we see the following sequence: 22 11 34 17 52
26 13 40 20 10 5 16 8 4 2 1. The cycle length of 22 is, therefore, 16,
since it took 16 repetitions to get to 1.</p>
<p>Given a definition of cycle length of a number, here’s the rest of the
problem: given any two numbers <code>i</code> and <code>j</code>, compute the maximum cycle
length over all numbers between <code>i</code> and <code>j</code>, inclusive.</p>
</blockquote>
<h3 id="solution">Solution</h3>
<p>Solving this in <a href="https://factorcode.org">Factor</a> could start by defining
a word to take a number and compute its next value in the cycle. Since
this is an internal word, we won’t add the check for the end condition
of n=1:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-cycle</span> <span class="nf">( </span><span class="nv">x</span> <span class="nf">-- </span><span class="nv">y</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup odd? </span>[ <span class="m">3 </span><span class="nb">* </span><span class="m">1 </span><span class="nb">+ </span>] [ <span class="m">2 </span><span class="nb">/ </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Creating the “cycle sequence” for a given number can be done by:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">cycles</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span><span class="m">1 </span><span class="nb">> </span>] [ [ next-cycle ] <span class="nb">keep </span>] <span class="nb">produce swap suffix </span><span class="k">;
</span></span></span></code></pre></div><p>We could always find the “cycle length” by calling <code>cycles length</code>, but
a slightly faster way would not create an intermediate sequence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">cycle-length</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">m</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span>[ <span class="nb">over </span><span class="m">1 </span><span class="nb">> </span>] [ [ next-cycle ] [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">bi* </span>] <span class="nb">while nip </span><span class="k">;
</span></span></span></code></pre></div><p>We can find the maximum cycle (number and it’s cycle length) for any
given sequence of numbers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">max-cycle</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">elt</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>cycle-length ] { } <span class="nb">map>assoc </span>[ <span class="nb">second </span>] <span class="nb">supremum-by </span><span class="k">;
</span></span></span></code></pre></div><p>For convenience, we can make some helper words to find either the number
and/or the maximum cycle length:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">max-cycle-value</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">) </span>max-cycle <span class="nb">first </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">max-cycle-length</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">m</span> <span class="nf">) </span>max-cycle <span class="nb">second </span><span class="k">;
</span></span></span></code></pre></div><h3 id="testing">Testing</h3>
<p>Factor strives to be a <a href="https://re.factorcode.org/2011/04/verbosity.html">concise
language</a>, some of
which is natural for a <a href="https://re.factorcode.org/2011/07/concatenative-thinking.html">concatenative
style</a>.
You can see this in the simple approach to <a href="https://docs.factorcode.org/content/article-tools.test.html">unit
testing:</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> { <span class="m">22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1 </span>}
</span></span><span class="line"><span class="cl">} [ <span class="m">22 </span>cycles ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="m">1 </span> } [ <span class="m">1 </span>cycle-length ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">2 </span> } [ <span class="m">2 </span>cycle-length ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">6 </span> } [ <span class="m">5 </span>cycle-length ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">16 </span>} [ <span class="m">22 </span>cycle-length ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="m">20 </span> } [ <span class="m">1 10 </span>[a..b] max-cycle-length ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">125 </span>} [ <span class="m">100 200 </span>[a..b] max-cycle-length ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">89 </span> } [ <span class="m">201 210 </span>[a..b] max-cycle-length ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">174 </span>} [ <span class="m">900 1000 </span>[a..b] max-cycle-length ] unit-test
</span></span></code></pre></div><h3 id="performance">Performance</h3>
<p>The original post discussed performance. Since many cycles have numbers
in common, we could change our approach to a recursive one, with
<a href="https://docs.factorcode.org/content/article-memoize.html">memoization</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MEMO:</span> <span class="nf">fast-cycle-length</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">m</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">1 </span><span class="nb">> </span>[ next-cycle fast-cycle-length <span class="m">1 </span><span class="nb">+ </span>] [ <span class="nb">drop </span><span class="m">1 </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Indeed, this takes roughly 15-20% of the time that our previous approach
took to compute the number of cycles for the numbers 1 to 100,000 with a
cold cache.</p>
<h3 id="main">Main</h3>
<p>Like the original solution, we can add a “main” to allow running from
the command line:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">run-cycles</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ blank? ] split-when <span class="nb">harvest first2
</span></span></span><span class="line"><span class="cl"> [ string>number ] <span class="nb">bi@ 2dup </span>[a..b] max-cycle-length
</span></span><span class="line"><span class="cl"> <span class="s">"%s %s %s\n"</span> printf
</span></span><span class="line"><span class="cl"> ] <span class="nb">each-line </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">run-cycles</span>
</span></span></code></pre></div><p>And using the original sample file, produce the same output:</p>
<pre tabindex="0"><code>$ cat sample-data.txt
1 10
100 200
201 210
900 1000
$ factor cycles.factor < sample-data.txt
1 10 20
100 200 125
201 210 89
900 1000 174
</code></pre><p>The code for this is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/cycles/cycles.factor">GitHub</a>.</p>
Select Random Lines
https://re.factorcode.org/2012/10/select-random-lines.html
Tue, 23 Oct 2012 18:09:00 -0700https://re.factorcode.org/2012/10/select-random-lines.html<p>Ned Batchelder made an interesting blog post on <a href="https://nedbatchelder.com/blog/201208/selecting_randomly_from_an_unknown_sequence.html">selecting randomly from
an unknown
sequence</a>.
His <a href="https://python.org">Python</a> version uses a “random replacement”
strategy to choose each line with a <code>1/n</code> probability, where <code>n</code> is the
number of elements seen so far:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">random</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">random_element</span><span class="p">(</span><span class="n">seq</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"""Return an element chosen at random from `seq`."""</span>
</span></span><span class="line"><span class="cl"> <span class="n">it</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">n</span><span class="p">,</span> <span class="n">elem</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">seq</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">it</span> <span class="o">=</span> <span class="n">elem</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">it</span>
</span></span></code></pre></div><h3 id="random-line">random-line</h3>
<p>I wanted to show how a similar approach in
<a href="https://factorcode.org">Factor</a> could be used to select a random line
from a file, or any stream that supports a character-based <a href="https://docs.factorcode.org/content/article-stream-protocol.html">stream
protocol</a>.</p>
<p>We can make a variant of
<a href="https://docs.factorcode.org/content/word-each-line,io.html">each-line</a>
that calls a quotation with each line and its “line number” (indexed
from 1 to <code>n</code>, the number of lines in the stream):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">each-numbered-line</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">line</span> <span class="nv">number</span> <span class="nf">-- </span><span class="nv">...</span> <span class="nf">) -- </span><span class="nv">...</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">1 </span>] <span class="nb">dip </span>'[ <span class="nb">swap </span>[ @ ] [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">bi </span>] <span class="nb">each-line drop </span><span class="k">; inline
</span></span></span></code></pre></div><p>Then, it is pretty easy to select a random line (replacing each line
with chance of <code>1/K</code> probability where <code>K</code> is the current line number.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-line</span> <span class="nf">( -- </span><span class="nv">line/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span>[ random <span class="nb">zero? </span>[ <span class="nb">nip </span>] [ <span class="nb">drop </span>] <span class="nb">if </span>] each-numbered-line <span class="k">;
</span></span></span></code></pre></div><h3 id="random-lines">random-lines</h3>
<p>To efficiently select more than one random line from a stream, we will
be using a technique called <a href="https://en.wikipedia.org/wiki/Reservoir_sampling">reservoir
sampling</a>. The idea is
to select the first <code>n</code> elements, then randomly replace them with
weighted probability <code>n/K</code>, where <code>K</code> is the current line number:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">random-lines</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">lines</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> V{ } <span class="nb">clone </span>:> accum
</span></span><span class="line"><span class="cl"> [| line line# |
</span></span><span class="line"><span class="cl"> line# n <span class="nb"><= </span>[
</span></span><span class="line"><span class="cl"> line accum <span class="nb">push
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> line# random :> r
</span></span><span class="line"><span class="cl"> r n <span class="nb">< </span>[ line r accum <span class="nb">set-nth </span>] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] each-numbered-line accum <span class="k">;
</span></span></span></code></pre></div><blockquote>
<p><em>Note: we used <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a> to
implement this.</em></p>
</blockquote>
<p>This is available in the
<a href="https://github.com/factor/factor/blob/master/extra/io/random/random.factor">io.random</a>
vocabulary in the Factor development branch.</p>
Parsing IPv4 Addresses
https://re.factorcode.org/2012/10/parsing-ipv4-addresses.html
Thu, 18 Oct 2012 18:33:00 -0700https://re.factorcode.org/2012/10/parsing-ipv4-addresses.html<p>I came across an interesting question on StackOverflow asking about
<a href="https://superuser.com/questions/486788/why-does-pinging-192-168-072-only-2-dots-return-a-response-from-192-168-0-58">parsing IPv4
addresses</a>.
Typically, IPv4 addresses are specified with four components (e.g.,
something like <code>192.164.1.55</code>). As the top answer points out, you might
be suprised to see that <code>ping</code> interprets addresses a bit oddly:</p>
<pre tabindex="0"><code>C:\>ping 1
Pinging 0.0.0.1 with 32 bytes of data:
C:\>ping 1.2
Pinging 1.0.0.2 with 32 bytes of data:
C:\>ping 1.2.3
Pinging 1.2.0.3 with 32 bytes of data:
C:\>ping 1.2.3.4
Pinging 1.2.3.4 with 32 bytes of data:
C:\>ping 1.2.3.4.5
Ping request could not find host 1.2.3.4.5. Please check the name and try again.
C:\>ping 255
Pinging 0.0.0.255 with 32 bytes of data:
C:\>ping 256
Pinging 0.0.1.0 with 32 bytes of data:
</code></pre><p>In fact, you can reach <code>google.com</code>, using the IP address specified as
dotted decimal (<code>74.125.226.4</code>), flat decimal (<code>1249763844</code>), dotted
octal (<code>0112.0175.0342.0004</code>), flat octal (<code>011237361004</code>), dotted hex
(<code>0x4A.0x7D.0xE2.0x04</code>), flat hex (<code>0x4A7DE204</code>), or even something of
each (<code>74.0175.0xe2.4</code>).</p>
<h3 id="implementation">Implementation</h3>
<p>Of course, my first thought was that <a href="https://factorcode.org">Factor</a>
should have a parser that works similarly (especially since I
implemented <a href="https://re.factorcode.org/2011/02/ping-pong.html">support for the ping
protocol</a> awhile
ago). We want a <code>parse-ipv4</code> word taking a string representing the
address and returning an IPv4 address string that has the typical four
components.</p>
<p>First, we need to have words to split a string into numbered parts and a
word to join them back together:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">split-components</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">array</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"."</span> split [ string>number ] <span class="nb">map </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">join-components</span> <span class="nf">( </span><span class="nv">array</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ number>string ] <span class="nb">map </span><span class="s">"."</span> <span class="nb">join </span><span class="k">;
</span></span></span></code></pre></div><p>Then, we can parse the address simply:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-ipv4</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">ip</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> split-components <span class="nb">dup length </span>{
</span></span><span class="line"><span class="cl"> { <span class="m">1 </span>[ { <span class="m">0 0 0 </span>} <span class="nb">prepend </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">2 </span>[ <span class="nb">first2 </span>[| A D | { A <span class="m">0 0 </span>D } ] <span class="nb">call </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">3 </span>[ <span class="nb">first3 </span>[| A B D | { A B <span class="m">0 </span>D } ] <span class="nb">call </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">4 </span>[ ] }
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span>join-components <span class="k">;
</span></span></span></code></pre></div><h3 id="extras">Extras</h3>
<p>If we want to support octal addresses, we can convert an octal number
like <code>0112</code> to something Factor can easily parse (<code>0o112</code>) in our
splitting code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">cleanup-octal</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>{ [ <span class="s">"0"</span> <span class="nb">head? </span>] [ <span class="s">"0x"</span> <span class="nb">head? not </span>] } 1&&
</span></span><span class="line"><span class="cl"> [ <span class="m">1 </span><span class="nb">tail </span><span class="s">"0o"</span> <span class="nb">prepend </span>] <span class="nb">when </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">split-components</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">array</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"."</span> split [ cleanup-octal string>number ] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>And if we want to support the “carry propagation” which allows <code>256</code> to
mean <code>0.0.1.0</code>, we need to “bubble” the array before joining:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bubble</span> <span class="nf">( </span><span class="nv">array</span> <span class="nf">-- </span><span class="nv">array'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">reverse </span><span class="m">0 </span><span class="nb">swap </span>[ <span class="nb">+ </span><span class="m">256 </span><span class="nb">/mod </span>] <span class="nb">map reverse nip </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">join-components</span> <span class="nf">( </span><span class="nv">array</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> bubble [ number>string ] <span class="nb">map </span><span class="s">"."</span> <span class="nb">join </span><span class="k">;
</span></span></span></code></pre></div><p>This (along with some error handling) has been
<a href="https://github.com/factor/factor/blob/master/extra/ip-parser/ip-parser.factor">committed</a>
to the Factor repository in the <code>ip-parser</code> vocabulary. If it proves
useful, it might be nice to change the <code>io.sockets</code> to use this when
resolving IPv4 addresses…</p>
COLOR: <TAB>
https://re.factorcode.org/2012/09/color-tab.html
Mon, 24 Sep 2012 18:58:00 -0700https://re.factorcode.org/2012/09/color-tab.html<p>As a diversion today, I added “color tab completion” to the
<a href="https://factorcode.org">Factor</a> environment. I even made the color names
render with their assigned color value:</p>
<p>
<img src="https://re.factorcode.org/images/2012-09-24-color-tab-colors-matching.png" alt="" width="432" height="218" />
</p>
<p>For the curious, I extended the <code>tools.completion</code> vocabulary to
<a href="https://github.com/factor/factor/commit/f2a1ec7d848cfaaf04638bbcd80ad0a4a8e991c7">implement a “colors-matching”
word</a>
and then <a href="https://github.com/factor/factor/commit/a8e0ccfd5f4ca32b27fe42a5a7693a28f0bb49ab">added a “colors-completion”
type</a>
to the UI.</p>
Faster Tables!
https://re.factorcode.org/2012/09/faster-tables.html
Tue, 18 Sep 2012 17:15:00 -0700https://re.factorcode.org/2012/09/faster-tables.html<p>We’ve known for awhile that the <a href="https://factorcode.org">Factor</a> user
interface is a little bit slow when displaying tables with many rows. I
wasn’t sure this was a big problem until a <a href="https://github.com/factor/factor/issues/679">detailed bug
report</a> was filed
(thanks <a href="https://github.com/k7f">@k7f</a>!) that pointed out the <a href="https://docs.factorcode.org/content/vocab-opengl.gl.html">opengl.gl
vocabulary</a>
(with over 2,000 symbols in it) was “virtually unscrollable”.</p>
<p>After poking around at the profiler output, I noticed that some <a href="https://docs.factorcode.org/content/article-building-ui.html">UI
gadgets</a>
had “baseline alignment” support which was used in the layout process.
Some of these calculations were quite expensive and could easily be
cached.</p>
<p>After a few changes, and introducing the concept of an “aligned gadget”
which provides a mechanism to cache these alignment calculations, the
original test case is almost 10 times as responsive as before!</p>
<p>This is available in the master branch on
<a href="https://github.com/factor/factor">GitHub</a>.</p>
Watching Words
https://re.factorcode.org/2012/09/watching-words.html
Mon, 10 Sep 2012 15:31:00 -0700https://re.factorcode.org/2012/09/watching-words.html<p>Function “annotations” are a feature that many languages support. They
may take various forms such as <a href="https://www.python.org/dev/peps/pep-0318/">decorators in
Python</a> or <a href="https://docs.oracle.com/javase/tutorial/java/javaOO/annotations.html">class and method
annotations in
Java</a>
– having direct (like Python) or no direct (like Java) effect on the
code it annotates. <a href="https://factorcode.org">Factor</a> has some similar
features that I’ll demonstrate below.</p>
<p>We can define a simple word to use in this example (that adds <code>2</code> to its
input):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">foo</span> <span class="nf">( </span><span class="nv">x</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">) </span><span class="m">2 </span><span class="nb">+ </span><span class="k">;
</span></span></span></code></pre></div><h3 id="watch">Watch</h3>
<p>Using the
<a href="https://docs.factorcode.org/content/article-tools.annotations.html">tools.annotations</a>
vocabulary, we can attach a “watch” annotation that prints the inputs
and outputs of a word when it is called:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="no">\ foo</span> watch
</span></span></code></pre></div><p>If you <a href="https://docs.factorcode.org/content/article-see.html">print the word
definition</a>, you
can see how it was modified:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="no">\ foo</span> see
</span></span><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">math</span> <span class="nn">tools.annotations.private</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span>
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">foo</span> <span class="nf">( </span><span class="nv">x</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">) </span><span class="no">\ foo</span> entering <span class="m">2 </span><span class="nb">+ </span><span class="no">\ foo</span> leaving <span class="k">;
</span></span></span></code></pre></div><p>You can call this word (either directly, or indirectly by calling
another word which calls it) and see its inputs and outputs. A nice
feature of this is that the UI lets you click on these values and see
more detail (particularly useful if they are tuples or more complex
objects):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">3 </span>foo
</span></span><span class="line"><span class="cl">--- Entering foo
</span></span><span class="line"><span class="cl">x <span class="m">3
</span></span></span><span class="line"><span class="cl">--- Leaving foo
</span></span><span class="line"><span class="cl">n <span class="m">5
</span></span></span></code></pre></div><h3 id="reset">Reset</h3>
<p>You can stop watching a word by calling “reset” on it (or right-clicking
on its definition in the UI and choosing “reset”) to remove all of its
annotations. Currently, a word can only be annotated once.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="no">\ foo</span> reset
</span></span></code></pre></div><h3 id="timing">Timing</h3>
<p>In addition to “watching”, you can also use annotations to track the
total running time of a word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="no">\ foo</span> add-timing
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">0 10,000 </span>[ foo ] <span class="nb">times </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">20000
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> word-timing.
</span></span><span class="line"><span class="cl">foo <span class="m">0.000594456
</span></span></span></code></pre></div><h3 id="other">Other</h3>
<p>It also supports arbitrary annotations, such as adding “before” and
“after” logic:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="no">\ foo</span> [ <span class="s">"hi"</span> <span class="nb">print </span>] [ <span class="s">"bye"</span> <span class="nb">print </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">surround </span>] <span class="nb">2curry </span>annotate
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">3 </span>foo <span class="m">.
</span></span></span><span class="line"><span class="cl">hi
</span></span><span class="line"><span class="cl">bye
</span></span><span class="line"><span class="cl"><span class="m">5
</span></span></span></code></pre></div><p>These annotations can be pretty powerful and were even used to build our
<a href="https://code-factor.blogspot.com/2011/11/code-coverage-tool.html">code coverage
tool</a>.</p>
Literate Programming
https://re.factorcode.org/2012/08/literate-programming.html
Wed, 29 Aug 2012 17:37:00 -0700https://re.factorcode.org/2012/08/literate-programming.html<p>Donald Knuth pioneered <a href="https://en.wikipedia.org/wiki/Literate_programming">Literate
programming</a> as a
technique for writing structured programs. The literate programming
world typically has more descriptive text than code, so rather than
“comment out” the descriptive text, they “comment in” the code.</p>
<p>It is very popular in some communities, for example Haskell, where even
many blog posts are written in a <a href="https://www.haskell.org/haskellwiki/Literate_programming">Literate
Haskell</a> style.
This is done by creating a <code>.lhs</code> file instead of a <code>.hs</code> file. In it,
all lines starting with <code>></code> are interpreted as code, everything else is
considered a comment.</p>
<p>Graham Telfer <a href="https://sourceforge.net/mailarchive/message.php?msg_id=29735956">asked a
question</a>
on the mailing list if <a href="https://factorcode.org">Factor</a> supported
literate programming. I thought it might be fun to implement some of the
ideas quickly, below I’ll share how it works.</p>
<h3 id="the-lexer">The Lexer</h3>
<p>Factor uses a
<a href="https://docs.factorcode.org/content/article-parser-lexer.html">lexer</a>
object to turn a stream of text into tokens that are used by the
<a href="https://docs.factorcode.org/content/article-parser.html">parser</a> to turn
tokens into Factor objects and definitions. We can extend the lexer
system to create a version that skips over any lines that are not
prefixed by a <code>></code> character.</p>
<p>First, we define our <code>literate-lexer</code> sub-class:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">literate-lexer</span> < <span class="nc">lexer</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><literate-lexer></span> <span class="nf">( </span><span class="nv">text</span> <span class="nf">-- </span><span class="nv">lexer</span> <span class="nf">) </span>literate-lexer new-lexer <span class="k">;
</span></span></span></code></pre></div><p>Second, we implement the
<a href="https://docs.factorcode.org/content/word-skip-blank,lexer.html">skip-blank</a>
word to skip over all lines that are just comments:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">literate-lexer</span> <span class="nf">skip-blank</span>
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>column>> <span class="nb">zero? </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>line-text>> [
</span></span><span class="line"><span class="cl"> <span class="s">"> "</span> <span class="nb">head?
</span></span></span><span class="line"><span class="cl"> [ [ <span class="m">2 </span><span class="nb">+ </span>] change-column call-next-method ]
</span></span><span class="line"><span class="cl"> [ [ <span class="nb">nip length </span>] change-lexer-column ]
</span></span><span class="line"><span class="cl"> <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] [ <span class="nb">drop </span>] <span class="nb">if*
</span></span></span><span class="line"><span class="cl"> ] [ call-next-method ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>We can then create a quick syntax word that looks for an end token and
parses all the lines between:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYNTAX: </span><LITERATE
</span></span><span class="line"><span class="cl"> <span class="s">"LITERATE>"</span> parse-multiline-string string-lines [
</span></span><span class="line"><span class="cl"> <literate-lexer> (parse-lines) <span class="nb">append!
</span></span></span><span class="line"><span class="cl"> ] with-nested-compilation-unit <span class="k">;
</span></span></span></code></pre></div><h3 id="try-it">Try It</h3>
<p>Now, you can do something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <LITERATE
</span></span><span class="line"><span class="cl"> This is a big wall <span class="nb">of </span>text <span class="nb">with </span>no Factor code...
</span></span><span class="line"><span class="cl"> Does this work?
</span></span><span class="line"><span class="cl"> <span class="m">1 1 </span><span class="nb">+ </span><span class="m">.
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> I bet it didn't... maybe the following works:
</span></span><span class="line"><span class="cl"> <span class="nb">> </span><span class="m">8675309 .
</span></span></span><span class="line"><span class="cl"> Yay!
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> You can create functions that span multiple <span class="nb">lines
</span></span></span><span class="line"><span class="cl"> <span class="nb">> </span><span class="k">:</span> <span class="nf">foo</span> <span class="nf">( -- </span><span class="nv">x</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> We interrupt this program to bring you this:
</span></span><span class="line"><span class="cl"> <span class="nb">> </span><span class="m">12 </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> Now, we can run foo:
</span></span><span class="line"><span class="cl"> <span class="nb">> </span>foo <span class="m">.
</span></span></span><span class="line"><span class="cl"> LITERATE
</span></span><span class="line"><span class="cl"><span class="m">8675309
</span></span></span><span class="line"><span class="cl"><span class="m">12
</span></span></span></code></pre></div><p>It might be nice to automatically support <code>.lfactor</code> files, but this is
a quick prototype to see if it makes sense. Not bad for ten or so lines
of code?</p>
<p>It is <a href="https://github.com/factor/factor/commit/43586752938e4d8cfde036614de8275f1c34f0c3">available
now</a>
in the development branch of Factor, in the <code>literate</code> vocabulary.</p>
Factor 0.95 now available
https://re.factorcode.org/2012/08/factor-0-95-now-available.html
Thu, 16 Aug 2012 19:38:00 -0700https://re.factorcode.org/2012/08/factor-0-95-now-available.html<p><em>“The reports of my death are greatly exaggerated” - Mark Twain</em></p>
<p>I’m very pleased to announce the release of
<a href="https://factorcode.org">Factor</a> 0.95!</p>
<table class="downloads" cellspacing="0">
<colgroup>
<col style="width: 25%" />
<col style="width: 25%" />
<col style="width: 25%" />
<col style="width: 25%" />
</colgroup>
<thead>
<tr class="header">
<th class="nobg" style="text-align: center;">OS/CPU</th>
<th class="bg" style="text-align: center;" scope="col">Windows</th>
<th class="bg" style="text-align: center;" scope="col">Mac OS</th>
<th class="bg" style="text-align: center;" scope="col">Linux</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<th class="bg" style="text-align: center;" scope="row">x86</th>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.95/factor-windows-x86-32-0.95.zip">0.95</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.95/factor-macosx-x86-32-0.95.dmg">0.95</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.95/factor-linux-x86-32-0.95.tar.gz">0.95</a>
</td>
</tr>
<tr class="even">
<th class="bg" style="text-align: center;" scope="row">x86-64</th>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.95/factor-windows-x86-64-0.95.zip">0.95</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.95/factor-macosx-x86-64-0.95.dmg">0.95</a>
</td>
<td class="supported">
<a href="https://downloads.factorcode.org/releases/0.95/factor-linux-x86-64-0.95.tar.gz">0.95</a>
</td>
</tr>
</tbody>
</table>
<p><strong>Source code</strong>: <a
href="https://downloads.factorcode.org/releases/0.95/factor-src-0.95.zip"
class="release">0.95</a></p>
<blockquote>
<p><em>Note: it looks like the Mac OS binaries unintentionally require
10.8 or greater, due to an upgrade of our build farm. If you want to
use it on 10.6 or 10.7 before we make a fix, you can build from
source:</em></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ git clone https://github.com/factor/factor.git
</span></span><span class="line"><span class="cl">$ <span class="nb">cd</span> factor
</span></span><span class="line"><span class="cl">$ ./build-support/factor.sh update
</span></span></code></pre></div></blockquote>
<p>This release is brought to you with <strong>over 2,500 commits</strong> by the
following individuals:</p>
<blockquote>
<p>Alex Vondrak, Alfredo Beaumont, Andrew Pennebaker, Anton Gorenko, Brennan
Cheung, Chris Double, Daniel Ehrenberg, Doug Coleman, Eric Charlebois, Eungju
Park, Hugo Schmitt, Joe Groff, John Benediktsson, Jon Harper, Keita Haga,
Maximilian Lupke, Philip Searle, Philipp Brüschweiler, Rupert Swarbrick,
Sascha Matzke, Slava Pestov, <a href="https://github.com/8byte-jose">@8byte-jose</a>,
<a href="https://github.com/yac">@yac</a>, <a href="https://github.com/otoburb">@otoburb</a>,
<a href="https://github.com/rien">@rien</a></p>
</blockquote>
<p>In addition to lots (and lots!) of bug fixes and improvements, I want to
highlight the following features:</p>
<ul>
<li>GTK-based UI backend</li>
<li>Native image loaders using Cocoa, GTK, and GDI+</li>
<li>Sampling profiler replacing counting profiler</li>
<li><a href="https://code-factor.blogspot.com/2011/11/code-coverage-tool.html">Code coverage
tool</a>
to improve unit tests</li>
<li>VM and application-level signal handlers</li>
<li>ICMP support and an IPv4 “ping” implementation</li>
<li>DNS client and “host” implementation</li>
<li>Support frexp and log of <a href="https://re.factorcode.org/2011/09/really-big-numbers.html">really big
numbers</a></li>
<li>Cross-platform <a href="https://re.factorcode.org/2011/05/open-url-in-listener.html">open URL in
webbrowser</a></li>
<li>Cross-platform send file to trash (<a href="https://re.factorcode.org/2011/01/trashing-files-part-1-mac-os.html">Mac OS
X</a>,
<a href="https://re.factorcode.org/2011/01/trashing-files-part-2-unix.html">Linux</a>,
<a href="https://re.factorcode.org/2011/01/trashing-files-part-3-windows.html">Windows</a>)</li>
<li>Speedup <a href="https://re.factorcode.org/2012/04/faster-big-ratios.html">bignum GCD and
ratio</a>
operations</li>
<li>Speedup in thread yield performance on Mac OS</li>
<li>CSV library is 3x faster</li>
<li>XML library is 2x faster</li>
<li>JSON library is 2-3x faster</li>
<li>Many stability and performance enhancements</li>
</ul>
<p>Some possible backwards compatibility issues:</p>
<ul>
<li>Change alien references from “<code><int></code>” to “<code>int <ref></code>” and “<code>*int</code>”
to “<code>int deref</code>”</li>
<li>Removed Windows CE, BSD, and Solaris platform support</li>
<li>Natively support binary (0b), octal (0o), and hexadecimal (0x)
number syntax</li>
<li>Unify “<code>( -- )</code>” and “<code>(( -- ))</code>” stack effect syntax</li>
<li>Change prepend to return type of first sequence to match append
behavior</li>
<li>Change “.factor-rc” to be “.factor-rc” on all platforms</li>
<li>Cleanup specialized array syntax to be more generic and consistent</li>
<li>Change to quadratic probing instead of linear probing in hashtables</li>
<li>Allow dispatching on anonymous intersections/unions</li>
</ul>
<h3 id="what-is-factor">What is Factor</h3>
<p>Factor is a <a href="https://www.concatenative.org/">concatenative</a>, stack-based
programming language with <a href="https://concatenative.org/wiki/view/Factor/Features/The%20language">high-level
features</a>
including dynamic types, extensible syntax, macros, and garbage
collection. On a practical side, Factor has a <a href="https://docs.factorcode.org/content/article-vocab-index.html">full-featured
library</a>,
supports many different platforms, and has been extensively documented.</p>
<p>The implementation is <a href="https://concatenative.org/wiki/view/Factor/Optimizing%20compiler">fully
compiled</a>
for performance, while still supporting <a href="https://concatenative.org/wiki/view/Factor/Interactive%20development">interactive
development</a>.
Factor applications are portable between all common platforms. Factor
can <a href="https://concatenative.org/wiki/view/Factor/Deployment">deploy stand-alone
applications</a> on
all platforms. Full source code for the Factor project is available
under a BSD license.</p>
<h3 id="new-libraries">New Libraries</h3>
<ul>
<li><a href="https://docs.factorcode.org/content/vocab-99-bottles.html">99-bottles</a>:
texting “99 bottles of beer” (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-anagrams.html">anagrams</a>:
words for working with anagrams (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-atk.html">atk</a>: bindings
to GNOME Accessibility Toolkit (Anton Gorenko)</li>
<li><a href="https://docs.factorcode.org/content/vocab-audio-gadget.html">audio-gadget</a>:
play button (Joe Groff)</li>
<li><a href="https://docs.factorcode.org/content/vocab-bitcoin.client.html">bitcoin.client</a>
(Chris Double)</li>
<li><a href="https://docs.factorcode.org/content/vocab-classes.maybe.html">classes.maybe</a>:
union of classes and <code>f</code> (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-clutter.html">clutter</a>:
bindings to Clutter (Anton Gorenko)</li>
<li><a href="https://docs.factorcode.org/content/vocab-code-arrays.html">code-arrays</a>
(Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-colors.hex.html">colors.hex</a>
(John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-combinators.random.html">combinators.random</a>:
random versions of combinators (Jon Harper)</li>
<li><a href="https://docs.factorcode.org/content/vocab-core-foundation.launch-services.html">core-foundation.launch-services</a>
bindings to OS X Launch Services API (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-dice.html">dice</a>: <a href="https://re.factorcode.org/2010/07/rolling-dice.html">rolling
random
dice</a> (John
Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-dns.html">dns</a>: implements
DNS protocol (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-echo-server.html">echo-server</a>:
simple TCP <a href="https://re.factorcode.org/2012/08/echo-server.html">echo
server</a>
(John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-fastcgi.html">fastcgi</a>:
support for FastCGI standard (Brennan Cheung)</li>
<li><a href="https://docs.factorcode.org/content/vocab-furnace.auth.providers.couchdb.html">furnace.auth.providers.couchdb</a>:
CouchDB authentication (Alex Drummond)</li>
<li><a href="https://docs.factorcode.org/content/vocab-gdk.html">gdk</a>: bindings
to GDK (Anton Gorenko)</li>
<li><a href="https://docs.factorcode.org/content/vocab-gio.html">gio</a>: bindings
to GIO (Anton Gorenko)</li>
<li><a href="https://docs.factorcode.org/content/vocab-gmodule.html">gmodule</a>:
bindings to GModule (Anton Gorenko)</li>
<li><a href="https://docs.factorcode.org/content/vocab-gobject.html">gobject</a>:
bindings to GObject (Anton Gorenko)</li>
<li><a href="https://docs.factorcode.org/content/vocab-gobject-introspection.html">gobject-introspection</a>:
bindings to GObject Introspection (Anton Gorenko)</li>
<li><a href="https://docs.factorcode.org/content/vocab-google.charts.html">google.charts</a>:
<a href="https://re.factorcode.org/2011/03/google-charts.html">render Google charts in the
listener</a>
(John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-google.search.html">google.search</a>:
<a href="https://re.factorcode.org/2011/05/google-search.html">search Google from the
listener</a>
(John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-google.translate.html">google.translate</a>:
<a href="https://re.factorcode.org/2011/03/google-translate.html">translate languages from the
listener</a>
(Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-graphviz.html">graphviz</a>:
bindings to Graphviz library (Alex Vondrak)</li>
<li><a href="https://docs.factorcode.org/content/vocab-gtk.html">gtk</a>: bindings
to GTK (Anton Gorenko)</li>
<li><a href="https://docs.factorcode.org/content/vocab-hamurabi.html">hamurabi</a>:
<a href="https://re.factorcode.org/2010/08/hamurabi.html">port of HAMURABI.BAS
game</a> (John
Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-hashtables.sequences.html">hashtables.sequences</a>
(John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-help.search.html">help.search</a>:
allow searching within help article text (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-ini-file.html">ini-file</a>:
parsing <a href="https://re.factorcode.org/2010/07/parsing-configuration-files.html">INI-style configuration
files</a>
(John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-interval-sets.html">interval-sets</a>:
efficient sets of integer intervals (Daniel Ehrenberg)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.binary.fast.html">io.binary.fast</a>:
fast endian conversion (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.files.temp.html">io.files.temp</a>:
better cross-platform temp files (Joe Groff)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.files.trash.html">io.files.trash</a>:
cross platform send-to-trash (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.sockets.icmp.html">io.sockets.icmp</a>:
ICMP support (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.256color.html">io.streams.256color</a>:
<a href="https://re.factorcode.org/2012/07/xterm-256color.html">xterm-256color formatted
stream</a>
(John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.peek.html">io.streams.peek</a>:
peekable I/O streams (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.streams.throwing.html">io.streams.throwing</a>:
I/O streams that throw on EOF (Doug Coleman, Joe Groff)</li>
<li><a href="https://docs.factorcode.org/content/vocab-lint.html">lint</a>: Factor
“lint” tool (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-llvm.clang.html">llvm.clang</a>:
Clang FFI bindings (Erik Charlebois)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.approx.html">math.approx</a>:
approximating rationals (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.distances.html">math.distances</a>:
distance functions (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.extras.html">math.extras</a>:
assorted math functions (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.similarity.html">math.similarity</a>:
similary functions (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.transforms.html">math.transforms.fft</a>:
fast Fourier transform (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-memcached.html">memcached</a>:
<a href="https://re.factorcode.org/2010/04/connecting-to-memcached.html">query memcached
databases</a>
(John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-ntp.html">ntp</a>: client for
NTP protocol (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-ping.html">ping</a>:
implements IPv4 ping client (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-progress-bars.html">progress-bars</a>:
displaying textual progress bars (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-random.data.html">random.data</a>:
generate randomized strings (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-readline-listener.html">readline-listener</a>:
with tab completion! (Eric Charlebois)</li>
<li><a href="https://docs.factorcode.org/content/vocab-readline.html">readline</a>:
libreadline bindings (Eric Charlebois)</li>
<li><a href="https://docs.factorcode.org/content/vocab-resolv-conf.html">resolv-conf</a>:
parser for <code>resolv.conf</code> files (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-rosetta-code.html">rosetta-code</a>:
examples from <a href="https://rosettacode.org/wiki/Category:Factor">Rosetta
Code</a> website (many
anonymous authors)</li>
<li><a href="https://docs.factorcode.org/content/vocab-semantic-versioning.html">semantic-versioning</a>:
implements <a href="https://semver.org">Semantic Versioning</a> specification
(Maximilian Lupke)</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.extras.html">sequences.extras</a>:
assorted math functions (Doug Coleman, John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.windowed.html">sequences.windowed</a>:
virtual rolling sequence (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-slots.macros.html">slots.macros</a>:
macro-based accessors (Joe Groff)</li>
<li><a href="https://docs.factorcode.org/content/vocab-sorting.extras.html">sorting.extras</a>:
assorted words (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-tnetstrings.html">tnetstrings</a>:
reader/writer for <a href="https://re.factorcode.org/2011/03/typed-netstrings.html">“tagged
netstrings”</a>
(John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.coverage.html">tools.coverage</a>:
measure code coverage (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.deploy.embed.html">tools.deploy.embed</a>:
self-executing images (Joe Groff)</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.dns.html">tools.dns</a>
(Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.profiler.sampling.html">tools.profiler.sampling</a>:
sampling profiler (Joe Groff)</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.ps.html">tools.ps</a>:
process listing tool (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.test.fuzz.html">tools.test.fuzz</a>:
quickcheck-style probabilistic testing (Joe Groff, Andrew
Pennebaker)</li>
<li><a href="https://docs.factorcode.org/content/vocab-txon.html">txon</a>:
reader/writer for <a href="https://re.factorcode.org/2012/02/txon.html">TXON
format</a> (John
Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-units.reduction.html">units.reduction</a>:
reduce units to convenient forms (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-unix.signals.html">unix.signals</a>:
signal handlers (Joe Groff)</li>
<li><a href="https://docs.factorcode.org/content/vocab-wake-on-lan.html">wake-on-lan</a>:
support for <a href="https://re.factorcode.org/2012/05/wake-on-lan.html">Wake-on-LAN
protocol</a>
(John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-webbrowser.html">webbrowser</a>:
open URL’s in your webbrowser (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.gdiplus.html">windows.gdiplus</a>:
bindings to GDI+ (Joe Groff)</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.iphlpapi.html">windows.iphlpapi</a>:
bindings to IP Helper (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.privileges.html">windows.privileges</a>:
bindings to Privileges API (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.registry.html">windows.registry</a>:
bindings to Registry API (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-windows.streams.html">windows.streams</a>:
IStream interface (Joe Groff)</li>
<li><a href="https://docs.factorcode.org/content/vocab-wolfram-alpha.html">wolfram-alpha</a>:
<a href="https://re.factorcode.org/2011/01/wolframalpha-using-factor.html">query Wolfram|Alpha from the
listener</a>
(John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-xkcd.html">xkcd</a>: <a href="https://re.factorcode.org/2011/04/xkcd.html">viewer
for XKCD comics</a>
(John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-xmode.highlight.html">xmode.highlight</a>:
<a href="https://re.factorcode.org/2010/10/syntax-highlighting.html">syntax highlighting using formatted
streams</a>
(John Benediktsson)</li>
</ul>
<h3 id="improved-libraries">Improved Libraries:</h3>
<ul>
<li><a href="https://docs.factorcode.org/content/vocab-alien.endian.html">alien.endian</a>:
speedup byte-reverse (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-alien.fortran.html">alien.fortran</a>:
various improvements (Joe Groff)</li>
<li><a href="https://docs.factorcode.org/content/vocab-assocs.html">assocs</a>:
performance improvements (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-classes.struct.html">classes.struct</a>:
support for packed structures (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-combinators.smart.html">combinators.smart</a>:
various improvements (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-compiler.html">compiler</a>:
various improvements (Slava Pestov)</li>
<li><a href="https://docs.factorcode.org/content/vocab-command-line.html">command-line</a>:
pass arguments to “-run” vocabs (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-core-foundation.run-loop.html">core-foundation.run-loop</a>:
greatly speed up yield (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-csv.html">csv</a>:
performance improvements (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-curses.html">curses</a>:
various improvements (Philipp Brüschweiler)</li>
<li><a href="https://docs.factorcode.org/content/vocab-fuel.html">fuel</a>: various
improvements (Doug Coleman, Eungju Park, John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-generalizations.html">generalizations</a>:
adding <code>smart-with</code> (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-graphviz.html">graphviz</a>:
unit tests and improvements (Alex Vondrak)</li>
<li><a href="https://docs.factorcode.org/content/vocab-help.apropos.html">help.apropos</a>:
fixes and improvements (otoburb, John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-http.client.html">http.client</a>:
added OPTIONS, TRACE and HEAD HTTP methods (otoburb)</li>
<li><a href="https://docs.factorcode.org/content/vocab-images.png.html">images.png</a>:
various fixes (Philip Searle)</li>
<li><a href="https://docs.factorcode.org/content/vocab-infix.html">infix</a>:
support slice notation (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.html">io</a>: non-copying
read operations (Joe Groff)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.backend.html">io.backend</a>:
various improvements (Slava Pestov, Joe Groff, Erik Charlebois)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.binary.html">io.binary</a>:
performance improvements (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.encodings.string.html">io.encodings.string</a>:
performance improvements (Joe Groff)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.encodings.utf8.html">io.encodings.utf8</a>:
various improvements (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.files.windows.html">io.files.windows</a>:
fix handling of UNC paths (8byte-jose)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.pathnames.html">io.pathnames</a>:
support “~” pathname expansion (otoburb)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.serial.html">io.serial</a>:
fixes (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-io.sockets.html">io.sockets</a>:
implement a <raw> port (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-json.html">json</a>:
performance improvements (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-lexer.html">lexer</a>/<a href="https://docs.factorcode.org/content/vocab-parser.html">parser</a>:
various improvements (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-make.html">make</a>: support
building assocs (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.combinatorics.html">math.combinatorics</a>:
various improvements (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.complex.html">math.complex</a>:
syntax checking (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.floating-point.html">math.floating-point</a>:
added <code>double>ratio</code> (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.functions.html">math.functions</a>:
various improvements (Doug Coleman, John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.matrices.html">math.matrices</a>:
various improvements (Doug Coleman, John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.polynomials.html">math.polynomials</a>:
speedup polyval (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.primes.html">math.primes</a>:
various improvements (Samuel Tardieu)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.primes.erato.html">math.primes.erato</a>:
bug fixes (Rupert Swarbrick)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.statistics.html">math.statistics</a>:
many new words (Doug Coleman, John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-math.vectors.html">math.vectors</a>:
adding <code>v^</code>, <code>v^n</code>, <code>n^v</code> (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-memoize.html">memoize</a>:
speedup no-argument memoized words (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-mongodb.html">mongodb</a>:
various improvements (Sascha Matzke)</li>
<li><a href="https://docs.factorcode.org/content/vocab-namespaces.html">namespaces</a>:
speedup global access (Joe Groff)</li>
<li><a href="https://docs.factorcode.org/content/vocab-path-finding.html">path-finding</a>:
Dijkstra’s algorithm and fixes (Samuel Tardieu, Alfredo Beaumont)</li>
<li><a href="https://docs.factorcode.org/content/vocab-random.html">random</a>: many
improvements (Doug Coleman, John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.html">sequences</a>:
performance improvements (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-sequences.repeating.html">sequences.repeating</a>:
various improvements (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-sets.html">sets</a>:
performance improvements (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-slots.html">slots</a>:
improved slot type checking and coercion (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-sorting.human.html">sorting.human</a>:
case-insensitive comparisons (Doug Coleman)</li>
<li><a href="https://docs.factorcode.org/content/vocab-specialized-arrays.html">specialized-arrays</a>:
various cleanups (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-splitting.html">splitting</a>:
speedup <code>string-lines</code>, add <code>split*</code> (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-system-info.macosx.html">system-info.macosx</a>:
various improvements (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.completion.html">tools.completion</a>:
performance improvements (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-tools.crossref.html">tools.crossref</a>:
performance improvements (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.gadgets.table.html">ui.gadgets.table</a>:
improve hook behavior (Hugo Schmitt)</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.html">ui.tools</a>:
Ctrl-Shift-F for “toggle fullscreen” (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.browser.html">ui.tools.browser</a>:
support font resizing (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.listener.html">ui.tools.listener</a>:
better exception handling (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.listener.completion.html">ui.tools.listener.completion</a>:
improved word completion (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-ui.tools.operations.html">ui.tools.operations</a>:
better watch/reset behavior (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-unicode.breaks.html">unicode.breaks</a>:
speedup <code>>words</code> (John Benediktsson)</li>
<li><a href="https://docs.factorcode.org/content/vocab-xml.html">xml</a>:
performance improvements (John Benediktsson)</li>
</ul>
Echo Server
https://re.factorcode.org/2012/08/echo-server.html
Mon, 06 Aug 2012 15:39:00 -0700https://re.factorcode.org/2012/08/echo-server.html<p><a href="https://factorcode.org">Factor</a> has nice cross-platform support for
network programming. As is fairly typical, I am going to use an “echo
server” to demonstrate how the libraries work.</p>
<p>The basic logic for an “echo server” is to read input from a client,
write it back to them, and repeat until the client disconnects. Using
the <a href="https://docs.factorcode.org/content/article-stdio.html">input and output stream
abstraction</a>, we
can build a word which does this in a general manner, recursing until
<code>f</code> is read indicating end-of-stream:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">echo-loop</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="m">1024 </span><span class="nb">read-partial </span>[ <span class="nb">write flush </span>echo-loop ] <span class="nb">when* </span><span class="k">;
</span></span></span></code></pre></div><p>Our “echo server” will use TCP (i.e., <a href="https://docs.factorcode.org/content/article-network-connection.html">connection-oriented
networking</a>)
and the <a href="https://docs.factorcode.org/content/article-io.servers.html">general server
abstraction</a>
that comes with Factor with a binary encoding. The server framework uses
the name <code>"echo.server"</code> to automatically log client-related messages
(such as connect and disconnect events as well as errors) to
<code>$FACTOR/logs/echo.server</code>. We specify the <code>echo-loop</code> quotation as the
handler for clients:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><echo-server></span> <span class="nf">( </span><span class="nv">port</span> <span class="nf">-- </span><span class="nv">server</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> binary <threaded-server>
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span>>>insecure
</span></span><span class="line"><span class="cl"> <span class="s">"echo.server"</span> >>name
</span></span><span class="line"><span class="cl"> [ echo-loop ] >>handler <span class="k">;
</span></span></span></code></pre></div><p>We can start an echo server on, for example, port 12345:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">12345 </span><echo-server> start-server
</span></span></code></pre></div><p>Testing this is pretty easy using
<a href="https://en.wikipedia.org/wiki/Netcat">Netcat</a> or a similar client:</p>
<pre tabindex="0"><code>$ nc localhost 12345
Hello, Factor
Hello, Factor
</code></pre><p>This is available as the
<a href="https://github.com/factor/factor/blob/master/extra/echo-server/echo-server.factor">echo-server</a>
vocabulary.</p>
xterm-256color
https://re.factorcode.org/2012/07/xterm-256color.html
Mon, 23 Jul 2012 18:01:00 -0700https://re.factorcode.org/2012/07/xterm-256color.html<p>For those of you using terminals, you are probably aware of <a href="https://en.wikipedia.org/wiki/ANSI_escape_code">ANSI escape
codes</a>. As a refresher,
it allows formatting text with foreground and background colors,
underlines, italics, blinking, among other characteristics.</p>
<p><a href="https://factorcode.org">Factor</a> has support for <a href="https://docs.factorcode.org/content/article-io.styles.html">formatted output
streams</a> that
can apply styles to textual content. It is used extensively by the help
system to produce documentation in both the UI and command-line, export
to HTML or PDF, and even syntax highlighting.</p>
<p>This morning, I wanted to implement a formatted output stream that can
be used in the terminal to produce colored text output easily.</p>
<p>You can look at documentation for the
<a href="https://www.mudpedia.org/wiki/Xterm_256_colors">xterm-256color</a> ANSI
extensions, but basically you wrap your text with format codes, like so:</p>
<blockquote>
<p>Setting foreground to blue:</p>
<p>
<img src="https://re.factorcode.org/images/2012-07-23-xterm-256color-256color-1.png" alt="" width="386" height="27" />
</p>
<p>Setting background to red:</p>
<p>
<img src="https://re.factorcode.org/images/2012-07-23-xterm-256color-256color-2.png" alt="" width="387" height="28" />
</p>
</blockquote>
<p>There are varying methods of checking if a terminal supports the 256
color extension, but a simple way is to look at the <code>TERM</code> environment
variable:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">256color-terminal?</span> <span class="nf">( -- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"TERM"</span> os-env <span class="s">"-256color"</span> <span class="nb">tail? </span><span class="k">;
</span></span></span></code></pre></div><p>The specification should tell us how to map RGB color codes to their
“256color” code:</p>
<blockquote>
<p><strong>RGB Colors</strong></p>
<p>A value between 16 and 231 is used for RGB colors with each color
having 6 intensities. Red has a value between 0 and 5 multiplied by
36, Green has a value between 0 and 5 multiplied by 6, and Blue has a
value between 0 and 5.</p>
<p><strong>8 to 24 bit color conversion</strong></p>
<p>To display xterm RGB colors as 24 bit RGB colors the following values
are suggested for the 6 intensities: 0x00, 0x5F, 0x87, 0xAF, 0xD7, and
0xFF.</p>
</blockquote>
<p>Following that guideline, we can build our map of “256color” codes. I
also added system colors and grayscale colors, which is left as an
exercise for the reader (or you can <a href="https://github.com/factor/factor/blob/master/extra/io/streams/256color/256color.factor#L13">read the source
code</a>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">intensities</span> { <span class="m">0x00 0x5F 0x87 0xAF 0xD7 0xFF </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">256colors</span> H{ }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">! Add the RGB colors</span>
</span></span><span class="line"><span class="cl">intensities [| r i |
</span></span><span class="line"><span class="cl"> intensities [| g j |
</span></span><span class="line"><span class="cl"> intensities [| b k |
</span></span><span class="line"><span class="cl"> i <span class="m">36 </span><span class="nb">* </span>j <span class="m">6 </span><span class="nb">* + </span>k <span class="nb">+ </span><span class="m">16 </span><span class="nb">+
</span></span></span><span class="line"><span class="cl"> r g b <span class="nb">3array
</span></span></span><span class="line"><span class="cl"> 256colors <span class="nb">set-at
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-index
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-index
</span></span></span><span class="line"><span class="cl">] <span class="nb">each-index
</span></span></span></code></pre></div><p>Given a <a href="https://docs.factorcode.org/content/article-colors.html">RGBA
Color</a>, we want
to convert it to an array of its red, green, and blue components, and
then find the “256color” with the smallest “distance” (in this case
Euclidean distance) from the desired value.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">color>rgb</span> <span class="nf">( </span><span class="nv">color</span> <span class="nf">-- </span><span class="nv">rgb</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ red>> ] [ green>> ] [ blue>> ] <span class="nb">tri
</span></span></span><span class="line"><span class="cl"> [ <span class="m">255 </span><span class="nb">* </span>round <span class="nb">>integer </span>] <span class="nb">tri@ 3array </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">color>256color</span> <span class="nf">( </span><span class="nv">color</span> <span class="nf">-- </span><span class="nv">256color</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> color>rgb '[ _ distance ]
</span></span><span class="line"><span class="cl"> 256colors [ <span class="nb">keys swap infimum-by </span>] [ <span class="nb">at </span>] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>Converting a color to either a foreground or a background ANSI escape
code is now pretty easy:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">color>foreground</span> <span class="nf">( </span><span class="nv">color</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> color>256color <span class="s">"\u00001b[38;5;%sm"</span> sprintf <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">color>background</span> <span class="nf">( </span><span class="nv">color</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> color>256color <span class="s">"\u00001b[48;5;%sm"</span> sprintf <span class="k">;
</span></span></span></code></pre></div><p>With a basic implementation of the <a href="https://docs.factorcode.org/content/article-formatted-stream-protocol.html">formatted stream
protocol</a>,
we can now take something like this:</p>
<p>
<img src="https://re.factorcode.org/images/2012-07-23-xterm-256color-apropos.png" alt="" width="410" height="574" />
</p>
<p>And easily (and automatically!) display it as something like this:</p>
<p>
<img src="https://re.factorcode.org/images/2012-07-23-xterm-256color-256color-3.png" alt="" width="362" height="395" />
</p>
<p>To see more colorful implementation of words:</p>
<p>
<img src="https://re.factorcode.org/images/2012-07-23-xterm-256color-256color-4.png" alt="" width="325" height="71" />
</p>
<p>Or even experiment with various background colors:</p>
<p>
<img src="https://re.factorcode.org/images/2012-07-23-xterm-256color-256color-5.png" alt="" width="336" height="365" />
</p>
<p>This has been
<a href="https://github.com/factor/factor/commit/2a087c55965f4739e8f703c539510ba83021f155">committed</a>
to Factor’s development branch and is available now.</p>
Wake-on-LAN
https://re.factorcode.org/2012/05/wake-on-lan.html
Tue, 29 May 2012 11:39:00 -0700https://re.factorcode.org/2012/05/wake-on-lan.html<p><a href="https://en.wikipedia.org/wiki/Wake-on-LAN">Wake-on-LAN</a> is an ethernet
standard that allows a computer to be turned on or woken up by a network
message. This is a kinda-fun-sometimes-useful feature that I thought we
should have in <a href="https://factorcode.org">Factor</a>.</p>
<p>The way the protocol works is to broadcast a “magic packet” that
contains information describing the computer you want to wakeup. From
the Wikipedia article:</p>
<blockquote>
<p><em>The magic packet is a broadcast frame containing anywhere within its
payload 6 bytes of all 255 (FF FF FF FF FF FF in hexadecimal),
followed by sixteen repetitions of the target computer’s 48-bit MAC
address, for a total of 102 bytes. Since the magic packet is only
scanned for the string above, and not actually parsed by a full
protocol stack, it may be sent as any network- and transport-layer
protocol, although it is typically sent as a UDP datagram to port 7 or
9, or directly over Ethernet as EtherType 0x0842.</em></p>
</blockquote>
<p>First, we need a way to turn a <a href="https://en.wikipedia.org/wiki/MAC_address">MAC
address</a> into an array of
bytes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">mac-address-bytes</span> <span class="nf">( </span><span class="nv">mac-address</span> <span class="nf">-- </span><span class="nv">byte-array</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">":-"</span> split [ hex> ] B{ } <span class="nb">map-as </span><span class="k">;
</span></span></span></code></pre></div><p>Next, we need a way to construct the “magic packet” according to the
specification:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wake-on-lan-packet</span> <span class="nf">( </span><span class="nv">mac-address</span> <span class="nf">-- </span><span class="nv">bytearray</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">16 </span>] [ mac-address-bytes ] <span class="nb">bi* <array> concat
</span></span></span><span class="line"><span class="cl"> B{ <span class="m">0xff 0xff 0xff 0xff 0xff 0xff </span>} <span class="nb">prepend </span><span class="k">;
</span></span></span></code></pre></div><p>Now that we have the “magic packet”, we can create a datagram port
configured to send broadcast packets (using a <a href="https://github.com/factor/factor/commit/95e647fc1e302511147844a9d0ea48f6617719a4">recent
commit</a>
that made this easy and cross-platform):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wake-on-lan</span> <span class="nf">( </span><span class="nv">mac-address</span> <span class="nv">broadcast-ip</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ wake-on-lan-packet ] [ <span class="m">9 </span><inet4> ] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span><span class="m">0 </span><inet4> <broadcast> [ send ] with-disposal <span class="k">;
</span></span></span></code></pre></div><p>It’s easy enough to test this using Mac OS since most modern Mac
hardware supports Wake-on-LAN. You can configure it via the System
Preferences Energy Saver panel, in the Options tab. Marking the “Wake
for network access” checkbox enables Wake-on-LAN support. For other
platforms, read more about <a href="https://en.wikipedia.org/wiki/Wake-on-LAN#Receiving_the_magic_packet">receiving the magic
packet</a>.</p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/wake-on-lan/wake-on-lan.factor">GitHub</a>.</p>
Finding Duplicate Numbers
https://re.factorcode.org/2012/05/finding-duplicate-numbers.html
Mon, 14 May 2012 21:15:00 -0700https://re.factorcode.org/2012/05/finding-duplicate-numbers.html<p>One of my favorite interview questions goes something like this:</p>
<blockquote>
<p><em>Given an array of 1001 elements which contains integers from 1 to
1000 inclusive. The numbers are randomly stored in the array. Only one
number repeats itself. The candidate has to come up with an efficient
solution for finding that duplicate given that you can access the
elements of an array only once i.e., you can read the elements of the
array only once.</em></p>
</blockquote>
<p>I thought I would show some approaches to solving this, using both
<a href="https://python.org">Python</a> and <a href="https://factorcode.org">Factor</a>.</p>
<h3 id="numbers">Numbers</h3>
<p>First, we need to make a randomized array of our numbers (with one
duplicate):</p>
<p><strong>Python:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">make_numbers</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">seq</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">+</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1001</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">random</span><span class="o">.</span><span class="n">shuffle</span><span class="p">(</span><span class="n">seq</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">seq</span>
</span></span></code></pre></div><p><strong>Factor:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">make-numbers</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">1000 </span>[1..b] ] <span class="nb">dip suffix </span>randomize <span class="k">;
</span></span></span></code></pre></div><p>It’s worth pointing out that Factor has many “builtin” words that can be
helpful for solving this, making the shortest solution:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">12 </span>make-numbers duplicates <span class="nb">first </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">12
</span></span></span></code></pre></div><h3 id="enumeration">Enumeration</h3>
<p>While this solution is not linear, it is often the “obvious” first way
to solve this problem. It goes something like this: for each element in
the list, check if duplicated by any other element:</p>
<p><strong>Python:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">find_duplicate</span><span class="p">(</span><span class="n">seq</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">m</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">seq</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">seq</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">:]:</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">m</span> <span class="o">==</span> <span class="n">n</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">m</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">False</span>
</span></span></code></pre></div><p><strong>Factor:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-duplicate</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>'[ <span class="m">1 </span><span class="nb">+ </span>_ <span class="nb">index-from </span>] <span class="nb">find-index nip </span><span class="k">;
</span></span></span></code></pre></div><h3 id="sets">Sets</h3>
<p>Our first “linear” solution uses sets to track which elements we’ve
seen, stopping when we encounter our first duplicate. We will gloss over
the extra memory and time required to maintain the set.</p>
<p><strong>Python:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">find_duplicate</span><span class="p">(</span><span class="n">seq</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">d</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">m</span> <span class="ow">in</span> <span class="n">seq</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">m</span> <span class="ow">in</span> <span class="n">d</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">m</span>
</span></span><span class="line"><span class="cl"> <span class="n">d</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">m</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">False</span>
</span></span></code></pre></div><p><strong>Factor:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-duplicate</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">) </span>
</span></span><span class="line"><span class="cl"> <span class="no">f </span>fast-set '[ _ ?adjoin <span class="nb">not </span>] <span class="nb">find nip </span><span class="k">;
</span></span></span></code></pre></div><p>Both versions use a generic
<a href="https://docs.factorcode.org/content/article-hash-sets.html">hash-set</a>.
Knowing that our data is numeric values, we can use a
<a href="https://docs.factorcode.org/content/article-bit-sets.html">bit-set</a> and
gain some performance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-duplicate</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup length </span><bit-set> '[ _ ?adjoin <span class="nb">not </span>] <span class="nb">find nip </span><span class="k">;
</span></span></span></code></pre></div><h3 id="number-theory">Number Theory</h3>
<p>Taking advantage of the fact that our numbers are from 1 to 1000, we can
compute the <code>sum</code> of the numbers, and subtract the expected total (using
the formula <code>(n * n+1) / 2</code>) to find the duplicate:</p>
<p><strong>Python:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">find_duplicate</span><span class="p">(</span><span class="n">seq</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">n</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">seq</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">seq</span><span class="p">)</span> <span class="o">-</span> <span class="p">(</span><span class="n">n</span><span class="o">*</span><span class="p">(</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">))</span><span class="o">/</span><span class="mi">2</span>
</span></span></code></pre></div><p><strong>Factor:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-duplicate</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">sum </span>] [ <span class="nb">length dup </span><span class="m">1 </span><span class="nb">- * </span><span class="m">2 </span><span class="nb">/ </span>] <span class="nb">bi - </span><span class="k">;
</span></span></span></code></pre></div><p>What do you think? Any other good, fun, or unusual ways to solve this
problem?</p>
<p><em>Update: Zeev pointed out in the comments that another way would be to
XOR all the values in our sequence and the numbers 1 to 1000:</em></p>
<p><em>Python:</em></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">find_duplicate</span><span class="p">(</span><span class="n">seq</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">n1</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">seq</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">n1</span> <span class="o">^=</span> <span class="n">x</span>
</span></span><span class="line"><span class="cl"> <span class="n">n2</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1001</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">n2</span> <span class="o">^=</span> <span class="n">x</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">n1</span> <span class="o">^</span> <span class="n">n2</span>
</span></span></code></pre></div><p><em>Factor:</em></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-duplicate</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1000 </span>[1..b] [ <span class="m">0 </span>[ <span class="nb">bitxor </span>] <span class="nb">reduce </span>] <span class="nb">bi@ bitxor </span><span class="k">;
</span></span></span></code></pre></div>Random Name Generator
https://re.factorcode.org/2012/05/random-name-generator.html
Wed, 09 May 2012 18:11:00 -0700https://re.factorcode.org/2012/05/random-name-generator.html<p>In the spirit of the <a href="https://dorophone.blogspot.com/2012/02/almost-pure-random-demon-name-generator.html">(almost) pure random demon name
generator</a>,
I wanted to show a random name generator in
<a href="https://factorcode.org">Factor</a>.</p>
<p>Some time ago, I implemented a vocabulary for <a href="https://re.factorcode.org/2010/05/creating-fake-data.html">creating fake
data</a>
which could generate “fake names”. The way it worked was to make a name
by picking randomly from a list of valid first and last names. The
drawback with that approach is that you can only create names that
already exist in your list. It would be more interesting if you can use
a list of valid names to “seed” a random name generator which uses that
to create names that are similar to your list but do not appear in it.</p>
<h3 id="transition-tables">Transition Tables</h3>
<p>To do this, we will create a table of “transitions”. A transition is a
pair of characters that appear next to each other in a name. In
pseudo-code, it will be something like this:</p>
<ol>
<li>For each name from a list of valid names,</li>
<li>For every character in the name,</li>
<li>Update the table by adding the “transition” at the characters index.</li>
</ol>
<p>We can create a sequence of transitions for a given string by
<a href="https://docs.factorcode.org/content/word-clump,grouping.html">clumping</a>
each pair of characters together (as well as showing the last character
transitions to <code>f</code>, meaning the end of the word):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">transitions</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">enum</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { <span class="no">f </span>} { } <span class="nb">append-as </span><span class="m">2 </span>clump <span class="nb"><enum> </span><span class="k">;
</span></span></span></code></pre></div><p>Given a string and a transition table, we can update it with those
transitions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(transition-table)</span> <span class="nf">( </span><span class="nv">string</span> <span class="nv">assoc</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ transitions ] <span class="nb">dip </span>'[ <span class="nb">swap </span>_ <span class="nb">push-at </span>] <span class="nb">assoc-each </span><span class="k">;
</span></span></span></code></pre></div><p>So, given a sequence of strings, we can create the transition table
easily:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">transition-table</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> H{ } <span class="nb">clone </span>[ [ (transition-table) ] <span class="nb">curry each </span>] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div><p>You can try it out and it will look something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="s">"hello"</span> } transition-table <span class="m">.
</span></span></span><span class="line"><span class="cl">H{
</span></span><span class="line"><span class="cl"> { <span class="m">0 </span>V{ { <span class="sc">CHAR: h CHAR: e </span>} } }
</span></span><span class="line"><span class="cl"> { <span class="m">1 </span>V{ { <span class="sc">CHAR: e CHAR: l </span>} } }
</span></span><span class="line"><span class="cl"> { <span class="m">2 </span>V{ { <span class="sc">CHAR: l CHAR: l </span>} } }
</span></span><span class="line"><span class="cl"> { <span class="m">3 </span>V{ { <span class="sc">CHAR: l CHAR: o </span>} } }
</span></span><span class="line"><span class="cl"> { <span class="m">4 </span>V{ { <span class="sc">CHAR: o </span><span class="no">f </span>} } }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><h3 id="generating-names">Generating Names</h3>
<p>We can use the
<a href="https://docs.factorcode.org/content/article-namespaces-make.html">make</a>
vocabulary to randomly choose the next transition from our table given a
previous character, an index, and a transition table:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-char,</span> <span class="nf">( </span><span class="nv">prev</span> <span class="nv">index</span> <span class="nv">assoc</span> <span class="nf">-- </span><span class="nv">next</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">at swap </span>[ [ <span class="nb">nip = </span>] <span class="nb">curry assoc-filter </span>] <span class="nb">when*
</span></span></span><span class="line"><span class="cl"> random [ <span class="nb">first </span>, ] [ <span class="nb">second </span>] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>Generating the names is as easy as starting from zero and adding each
character until we hit an <code>f</code> indicating the end of a word.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">generate-name</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">name</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> transition-table [
</span></span><span class="line"><span class="cl"> <span class="no">f </span><span class="m">0 </span>[
</span></span><span class="line"><span class="cl"> [ <span class="nb">pick </span>next-char, ] [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">bi over
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop 3drop
</span></span></span><span class="line"><span class="cl"> ] <span class="s">""</span> make <span class="k">;
</span></span></span></code></pre></div><p>Generating a number of names is just:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">generate-names</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">seq</span> <span class="nf">-- </span><span class="nv">names</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ generate-name ] <span class="nb">curry replicate </span><span class="k">;
</span></span></span></code></pre></div><h3 id="try-it">Try It</h3>
<p>So, does it work? Well, if we load a <a href="https://en.wikipedia.org/wiki/List_of_Star_Trek_races">list of Star Trek
races</a>, we can
generate some new names that sound pretty good!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 </span>star-trek-races generate-names <span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> <span class="s">"Sooyllan"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Brakalan"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Napl"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Drizi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Tevorri"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Flladian"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Skadi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Cynocak"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Pamu"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Patha"</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/random-names/random-names.factor">GitHub</a>.</p>
Twin Primes
https://re.factorcode.org/2012/04/twin-primes.html
Thu, 19 Apr 2012 15:01:00 -0700https://re.factorcode.org/2012/04/twin-primes.html<p>The most recent <a href="https://programmingpraxis.com/2012/04/17/twin-primes/">programming
challenge</a> from
<em>Programming Praxis</em> is to:</p>
<blockquote>
<p><em>Pairs of prime numbers that differ by two are known as twin primes:
(3,5), (5,7), (11,13), (17,19), (29,31), (41,43), (59,61), (71,73),…
Your task is to write a function that finds the twin primes less than
a given input n.</em></p>
</blockquote>
<p><a href="https://www.rfc1149.net/blog/tag/factor/">Samuel Tardieu</a> has
contributed many improvements to Factor’s
<a href="https://docs.factorcode.org/content/article-math.primes.html">math.primes</a>
vocabulary, which we will be using to solve this puzzle.</p>
<p>We can solve this puzzle in the naive method by computing all prime
numbers up to a specified input, and then filtering them for pairs that
differ by two:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">twin-primes-upto</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> primes-upto <span class="m">2 </span><clumps> [ <span class="nb">first2 - abs </span><span class="m">2 </span><span class="nb">= </span>] <span class="nb">filter </span><span class="k">;
</span></span></span></code></pre></div><p>Another nice word would check to see whether any two numbers are twin
primes (using <a href="https://docs.factorcode.org/content/article-combinators.short-circuit.html">short-circuit
combinators</a>
to exit early if any of the conditions are not satisfied):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">twin-primes?</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { [ <span class="nb">- abs </span><span class="m">2 </span><span class="nb">= </span>] [ <span class="nb">nip </span>prime? ] [ <span class="nb">drop </span>prime? ] } 2&& <span class="k">;
</span></span></span></code></pre></div><p>The puzzle page suggests a <a href="https://programmingpraxis.com/2012/04/17/twin-primes/">more
efficient</a> method
for computing twin primes, which might be worth experimenting with…</p>
Faster Big Ratios!
https://re.factorcode.org/2012/04/faster-big-ratios.html
Thu, 05 Apr 2012 10:22:00 -0700https://re.factorcode.org/2012/04/faster-big-ratios.html<p>While working on a post about <a href="https://re.factorcode.org/2011/09/approximating-pi.html">approximating
pi</a>, I
noticed that the performance of <a href="https://factorcode.org">Factor’s</a> large
rational numbers was less than stellar.</p>
<p>Specifically, we defined this estimation function to find <code>pi</code> as a
<a href="https://docs.factorcode.org/content/article-rationals.html">rational
number</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">find-pi-to</span> <span class="nf">( </span><span class="nv">accuracy</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nv">approx</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1 4 </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">over </span>[ <span class="m">2 </span><span class="nb">* </span><span class="m">1 </span><span class="nb">+ </span>] [ <span class="nb">odd? </span>[ <span class="nb">neg </span>] <span class="nb">when </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">swap / </span>[ <span class="nb">+ </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> <span class="nb">abs </span>accuracy <span class="nb">>= </span>[ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">2dip
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop </span><span class="k">;
</span></span></span></code></pre></div><p>Using this for an accuracy of <code>0.001</code> was incredibly slow:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">0.001 </span>find-pi-to ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">1.692090043 </span>seconds
</span></span></code></pre></div><h3 id="bignum">Bignum</h3>
<p>In Factor, large integers are stored as arbitrary-precision “bignums”.
Similar to other languages such as Python and Scheme, Factor stores
these numbers as a sequence of large (either 30-bit or 62-bit) digits,
and then performs math on this sequence of digits.</p>
<p>It turns out that most of the ratios has both a bignum numerator and
bignum denominator. For most basic math operation on ratios, Factor
would compute the <a href="https://docs.factorcode.org/content/word-gcd,math.functions.html">greatest common
divisor</a>
to produce a normalized fraction (e.g., 6/4 would become 3/2).</p>
<p>For an accuracy of <code>0.001</code> in this example, the denominator had more
than 1,700 digits in base 10. As these numbers grew, the performance of
<a href="https://en.wikipedia.org/wiki/Euclidean_algorithm">Euclid’s GCD
algorithm</a> began
degrading particularly due to the (relatively) expensive calculation of
“mod” for bignum’s.</p>
<h3 id="lehmer-gcd">Lehmer GCD</h3>
<p>I noticed a <a href="https://bugs.python.org/issue1682">Python bug report</a> that
suggested addressing a similar performance problem for their rational
number implementation (in <code>fractions.py</code>) by implementing <a href="https://en.wikipedia.org/wiki/Lehmer's_GCD_algorithm">Lehmer’s GCD
algorithm</a>.</p>
<p>After implementing a
<a href="https://github.com/factor/factor/commit/629677b772a20e6e32f7dcd3e48c876ee7575627">bignum-gcd</a>
primitive that used Lehmer’s GCD, I created a
<a href="https://github.com/factor/factor/commit/1b8f1d9945adaaaa039bef3a6f07a8fbfe3cfb3b">fast-gcd</a>
word that used this for bignum’s and the current
<a href="https://docs.factorcode.org/content/word-gcd,math.functions.html">gcd</a>
word for other real numbers.</p>
<p>Performance improvement was impressive! After recompiling with these
patches, our original test case takes less than 10% of the time!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">0.001 </span>find-pi-to ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.156713844 </span>seconds
</span></span></code></pre></div><p>You can find this in the latest development version of Factor.</p>
Mega Millions
https://re.factorcode.org/2012/03/mega-millions.html
Fri, 30 Mar 2012 14:41:00 -0700https://re.factorcode.org/2012/03/mega-millions.html<p>With the <a href="https://www.megamillions.com">Mega Millions</a> jackpot over $640
million, you might be asking yourself:</p>
<blockquote>
<p><em>How do I use <a href="https://factorcode.org">Factor</a> to win the lottery???</em></p>
</blockquote>
<p>You can find a <a href="https://www.forbes.com/sites/baldwin/2012/03/29/500-million-jackpot-calculating-your-odds/">good
article</a>
on Forbes about calculating your odds of winning. We can also calculate
it with Factor. The lottery <a href="https://www.megamillions.com/howto/">works like
this</a>: you have to choose 5 “main”
numbers (1 through 56) and then pick a “mega” number (1 through 46).</p>
<p>We can use the “n choose k” formula in
<a href="https://docs.factorcode.org/content/vocab-math.combinatorics.html">math.combinatorics</a>
to compute the number of ways of picking “<em>5</em> unordered outcomes from
<em>56</em> numbers and then <em>1</em> of <em>46</em> possible mega numbers”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">56 5 </span>nCk <span class="m">46 </span><span class="nb">* </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">175711536
</span></span></span></code></pre></div><p>Sure enough, thats 175,711,536 possibilities. So if you buy one random
lottery ticket, you have a <em>1 in 175,711,536</em> chance of winning, or a
0.00000000569114597006% chance. Not that great! What are the other odds?</p>
<p>
<img src="https://re.factorcode.org/images/2012-03-30-mega-millions-odds_062205.gif" alt="" width="252" height="234" />
</p>
<p>Since the jackpot is so large, it’s got to be worth playing, right? In
fact, if you take the total jackpot and the odds of each winning
category, you can find the expected value of a ticket:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> {
</span></span><span class="line"><span class="cl"> 640000000/175711536
</span></span><span class="line"><span class="cl"> 250000/3904701
</span></span><span class="line"><span class="cl"> 10000/689065
</span></span><span class="line"><span class="cl"> 150/15313
</span></span><span class="line"><span class="cl"> 150/13781
</span></span><span class="line"><span class="cl"> 7/306
</span></span><span class="line"><span class="cl"> 10/844
</span></span><span class="line"><span class="cl"> 3/141
</span></span><span class="line"><span class="cl"> 2/75
</span></span><span class="line"><span class="cl"> } <span class="nb">sum </span><span class="s">"$%.2f"</span> printf
</span></span><span class="line"><span class="cl">$3.82
</span></span></code></pre></div><p>Wow, $3.82 of expected value (not counting sharing the jackpot with
someone who picked the same numbers, or the fact you can only win the
highest prize you qualify for)! But, how do we pick our numbers… well,
Factor to the rescue! Let’s randomly
<a href="https://docs.factorcode.org/content/word-sample,random.html">sample</a> our
5 main numbers and then pick a random mega number and output the result:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="k">:</span> <span class="nf">mega</span> <span class="nf">( -- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">56 </span>[1..b] <span class="m">5 </span>sample natural-sort
</span></span><span class="line"><span class="cl"> <span class="m">46 </span>[1..b] random <span class="nb">suffix </span><span class="k">;
</span></span></span></code></pre></div><p>In the spirit of <a href="https://fiverr.com/">fiver</a>, we can generate these
numbers to play:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">5 </span>[ mega ] <span class="nb">replicate </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> { <span class="m">2 8 25 30 43 29 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">6 15 31 41 47 33 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">11 20 26 29 33 15 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">3 6 19 23 32 15 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">16 25 31 44 50 24 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>And tonight we can check the <a href="https://www.megamillions.com/numbers/">winning
numbers</a> and see if it worked!</p>
Next Permutation
https://re.factorcode.org/2012/03/next-permutation.html
Fri, 02 Mar 2012 21:38:00 -0800https://re.factorcode.org/2012/03/next-permutation.html<p>Yesterday, I noticed a <a href="https://programmingpraxis.com/2012/02/28/next-greater-permutation-of-digits/">programming
challenge</a>
to find the “next greater permutation of digits” for a given number:</p>
<blockquote>
<p><em>Given a number, find the next higher number that uses the same set of
digits. For instance, given the number 38276, the next higher number
that uses the digits 2 3 6 7 8 is 38627.</em></p>
</blockquote>
<p>While reading the comments, I noticed that some of the C++ solutions
used the
<a href="https://www.cplusplus.com/reference/algorithm/next_permutation/">std::next_permutation</a>
function that returns the “lexicographically next greater permutation of
elements”. Noticing that the
<a href="https://docs.factorcode.org/content/vocab-math.combinatorics.html">math.combinatorics</a>
vocabulary lacks a <code>next-permutation</code> word, I thought it would be nice
to contribute one to the <a href="https://factorcode.org">Factor</a> standard
library.</p>
<h3 id="stdnext_permutation">std::next_permutation()</h3>
<p>It’s a useful place to start to get an <a href="https://wordaligned.org/articles/next-permutation">overview and
example</a> of how the
algorithm works. The C++ version is pretty dense and looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span><span class="o"><</span><span class="k">typename</span> <span class="n">Iter</span>
</span></span><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">next_permutation</span><span class="p">(</span><span class="n">Iter</span> <span class="n">first</span><span class="p">,</span> <span class="n">Iter</span> <span class="n">last</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">first</span> <span class="o">==</span> <span class="n">last</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">Iter</span> <span class="n">i</span> <span class="o">=</span> <span class="n">first</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="o">++</span><span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">==</span> <span class="n">last</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">i</span> <span class="o">=</span> <span class="n">last</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="o">--</span><span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(;;)</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">Iter</span> <span class="n">ii</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="o">--</span><span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">i</span> <span class="o"><</span> <span class="o">*</span><span class="n">ii</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">Iter</span> <span class="n">j</span> <span class="o">=</span> <span class="n">last</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="o">*</span><span class="n">i</span> <span class="o"><</span> <span class="o">*--</span><span class="n">j</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">iter_swap</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">reverse</span><span class="p">(</span><span class="n">ii</span><span class="p">,</span> <span class="n">last</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">==</span> <span class="n">first</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">reverse</span><span class="p">(</span><span class="n">first</span><span class="p">,</span> <span class="n">last</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="example">Example!</h3>
<p>Walking through an example of the algorithm is particularly helpful to
understand it:</p>
<blockquote>
<p>As an example consider finding the next permutation of:</p>
<p><strong>8342666411</strong></p>
<p>The longest monotonically decreasing tail is 666411, and the
corresponding head is 8342.</p>
<p><strong>8342 666411</strong></p>
<p>666411 is, by definition, reverse-ordered, and cannot be increased by
permuting its elements. To find the next permutation, we must increase
the head; a matter of finding the smallest tail element larger than
the head’s final 2.</p>
<p><strong>8342 666411</strong></p>
<p>Walking back from the end of tail, the first element greater than 2 is
4.</p>
<p><strong>8342 666411</strong></p>
<p>Swap the 2 and the 4</p>
<p><strong>8344 666211</strong></p>
<p>Since head has increased, we now have a greater permutation. To reduce
to the next permutation, we reverse tail, putting it into increasing
order.</p>
<p><strong>8344 112666</strong></p>
<p>Join the head and tail back together. The permutation one greater than
8342666411 is 8344112666.</p>
</blockquote>
<h3 id="implementation">Implementation</h3>
<p>We can use the steps in the example above to help organize our code.
First, we find the “cut point” which is the index to the left of the
longest monotonic tail:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">cut-point</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">last </span>] <span class="nb">keep </span>[ [ <span class="nb">> </span>] <span class="nb">keep swap </span>] <span class="nb">find-last drop nip </span><span class="k">;
</span></span></span></code></pre></div><p>Next, we want to find the smallest element larger than the element at
the “cut point” (searching from the end of the sequence):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">greater-from-last</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">seq</span> <span class="nf">-- </span><span class="nv">i</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">nip </span>] [ <span class="nb">nth </span>] <span class="nb">2bi </span>[ <span class="nb">> </span>] <span class="nb">curry find-last drop </span><span class="k">;
</span></span></span></code></pre></div><p>We then need a way to reverse the tail of a sequence (the <code>!</code> denotes
that this modifies the sequence in-place):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">reverse-tail!</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">swap </span><span class="m">1 </span><span class="nb">+ tail-slice reverse! drop </span>] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div><p>Putting this together gives us a word that looks a bit like our example.
I have decided that in the case where the sequence reaches its
lexicographic greatest order, we reverse it to its smallest ordering.
This allows it to cycle through all possible permutations no matter
where you start.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(next-permutation)</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>cut-point [
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span>[ greater-from-last ] <span class="nb">2keep
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">exchange </span>] [ reverse-tail! <span class="nb">nip </span>] <span class="nb">3bi
</span></span></span><span class="line"><span class="cl"> ] [ <span class="nb">reverse! </span>] <span class="nb">if* </span><span class="k">;
</span></span></span></code></pre></div><p>We wrap this with a simple check to make sure the sequence is not empty.
Arguably, we could instead check if the length is greater than 1 since a
single element sequence has only possible permutation.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-permutation</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ ] [ <span class="nb">drop </span>(next-permutation) ] <span class="nb">if-empty </span><span class="k">;
</span></span></span></code></pre></div><h3 id="testing">Testing</h3>
<p>Factor makes it easy to do unit tests. Here are some of the ones I’ve
used to test this code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="s">""</span> } [ <span class="s">""</span> next-permutation ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="s">"1"</span> } [ <span class="s">"1"</span> next-permutation ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="s">"21"</span> } [ <span class="s">"12"</span> next-permutation ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="s">"8344112666"</span> } [ <span class="s">"8342666411"</span> next-permutation ] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ <span class="s">"ABC"</span> <span class="s">"ACB"</span> <span class="s">"BAC"</span> <span class="s">"BCA"</span> <span class="s">"CAB"</span> <span class="s">"CBA"</span> <span class="s">"ABC"</span> }
</span></span><span class="line"><span class="cl">[ <span class="s">"ABC"</span> <span class="m">6 </span>[ <span class="nb">dup >string </span>next-permutation ] <span class="nb">times </span>] unit-test
</span></span></code></pre></div><p>This is available in the latest development branch of Factor.</p>
<h3 id="bonus">Bonus</h3>
<p>Solving the original programming challenge is as easy as:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">38276 </span>number>string next-permutation string>number <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">38627
</span></span></span></code></pre></div>TXON
https://re.factorcode.org/2012/02/txon.html
Tue, 21 Feb 2012 14:26:00 -0800https://re.factorcode.org/2012/02/txon.html<p>The <a href="https://www.hxa.name/txon">TXON</a>, also known as “Text Object
Notation”, is a proposed format for structured data.</p>
<p>Much less popular than other formats such as
<a href="https://en.wikipedia.org/wiki/JSON">JSON</a>,
<a href="https://en.wikipedia.org/wiki/XML">XML</a>, or even <a href="https://en.wikipedia.org/wiki/INI_file">INI
files</a> - I thought it would still
be fun to implement encode and decode words in
<a href="https://factorcode.org">Factor</a>.</p>
<p>An example <code>TXON</code> might look something like this:</p>
<pre tabindex="0"><code>Factor:`
url:`https://factorcode.org`
development:`Started in 2003`
license:`Open source (BSD license)`
influences:`Forth, Lisp, and Smalltalk`
`
</code></pre><h3 id="encoding">Encoding</h3>
<p>Since TXON uses “`” characters to delimit values, we need to escape
them:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">encode-value</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">string'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">R" `"</span> <span class="s">"\\`"</span> re-replace <span class="k">;
</span></span></span></code></pre></div><p>To implement encoding in a generic way, we dispatch on the type of
object being encoded:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">GENERIC:</span> <span class="nf">>txon</span> <span class="nf">( </span><span class="nv">object</span> <span class="nf">-- </span><span class="nv">string</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">sequence</span> <span class="nf">>txon</span>
</span></span><span class="line"><span class="cl"> [ >txon ] <span class="nb">map </span><span class="s">"\n"</span> <span class="nb">join </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">assoc</span> <span class="nf">>txon</span>
</span></span><span class="line"><span class="cl"> <span class="nb">>alist </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">first2 </span>[ encode-value ] [ >txon ] <span class="nb">bi* </span><span class="s">"%s:`%s`"</span> sprintf
</span></span><span class="line"><span class="cl"> ] <span class="nb">map </span><span class="s">"\n"</span> <span class="nb">join </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">string</span> <span class="nf">>txon</span>
</span></span><span class="line"><span class="cl"> encode-value <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">number</span> <span class="nf">>txon</span>
</span></span><span class="line"><span class="cl"> number>string >txon <span class="k">;
</span></span></span></code></pre></div><h3 id="decoding">Decoding</h3>
<p>Although the TXON specification includes an EBNF grammar, I am going to
show one way to build a parser from scratch. In the tradition of
<a href="https://concatenative.org">concatenative languages</a>, we will build our
decoder from several smaller words.</p>
<p>For symmetry with the <code>encode-value</code> word, we need a way to unescape the
<code>`</code> characters:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">decode-value</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">string'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">R" \\`"</span> <span class="s">"`"</span> re-replace <span class="k">;
</span></span></span></code></pre></div><p>Since the TXON format is a series of <code>name:`value`</code> pairs, we can
parse the name by finding the separator and then decoding the name
(which might contain escaped characters):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-name</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">remain</span> <span class="nv">name</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">":`"</span> split1 <span class="nb">swap </span>decode-value <span class="k">;
</span></span></span></code></pre></div><p>To build a word that finds the first (unescaped) <code>`</code> character, we
will first make a word that looks at adjacent characters, returning true
if the second character is an unescaped <code>`</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">`?</span> <span class="nf">( </span><span class="nv">ch1</span> <span class="nv">ch2</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ CHAR: <span class="no">\ =</span> <span class="nb">not </span>] [ <span class="sc">CHAR: ` </span><span class="nb">= </span>] <span class="nb">bi* and </span><span class="k">;
</span></span></span></code></pre></div><p>By
<a href="https://docs.factorcode.org/content/word-clump%2Cgrouping.html">grouping</a>
the string into adjacent characters, we can find the first unescaped
<code>`</code> (specially handling the case where the first character is an
<code>`</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(find-`)</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">n/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">2 </span>clump [ <span class="nb">first2 </span>`? ] <span class="nb">find drop </span>[ <span class="m">1 </span><span class="nb">+ </span>] [ <span class="no">f </span>] <span class="nb">if* </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-`</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">n/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup ?first </span><span class="sc">CHAR: ` </span><span class="nb">= </span>[ <span class="nb">drop </span><span class="m">0 </span>] [ (find-`) ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Parsing the value is slightly complicated by the fact that TXON supports
values which might themselves be a single value, a sequence of values,
or a series of name/value pairs. Basically, that means we need to:</p>
<ol>
<li>find the first <code>`</code> character</li>
<li>checks if the previous character is a <code>:</code> (indicating a name/value)</li>
<li>parse all name/values if so, otherwise decode the value(s)</li>
</ol>
<p>That algorithm can be translated into this code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">DEFER:</span> <span class="nf">name/values</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(parse-value)</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">values</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> decode-value string-lines <span class="nb">dup length </span><span class="m">1 </span><span class="nb">= </span>[ <span class="nb">first </span>] <span class="nb">when </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-value</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">remain</span> <span class="nv">value</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>find-` [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">1 </span><span class="nb">- pick ?nth </span><span class="sc">CHAR: : </span><span class="nb">=
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>name/values ] [ <span class="nb">cut swap </span>(parse-value) ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">rest </span>[ blank? ] <span class="nb">trim-head </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> ] [ <span class="no">f </span><span class="nb">swap </span>] <span class="nb">if* </span><span class="k">;
</span></span></span></code></pre></div><p>We want to parse a “name=value” pair, which should be as easy as parsing
the name, then the value, then
<a href="https://docs.factorcode.org/content/word-associate,hashtables.html">associating</a>
into a hashtable:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(name=value)</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">remain</span> <span class="nv">term</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> parse-name [ parse-value ] <span class="nb">dip </span>associate <span class="k">;
</span></span></span></code></pre></div><p>The string might contain a “name=value” pair, or just a single value:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">name=value</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">remain</span> <span class="nv">term</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ blank? ] <span class="nb">trim
</span></span></span><span class="line"><span class="cl"> <span class="s">":`"</span> <span class="nb">over subseq? </span>[ (name=value) ] [ <span class="no">f </span><span class="nb">swap </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>We finish by building a word to produce all “name=value” pairs, used in
the <code>parse-value</code> word earlier.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">name/values</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">remain</span> <span class="nv">terms</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>{ [ <span class="nb">empty? not </span>] [ <span class="nb">first </span><span class="sc">CHAR: ` </span><span class="nb">= not </span>] } 1&& ]
</span></span><span class="line"><span class="cl"> [ name=value ] <span class="nb">produce assoc-combine </span><span class="k">;
</span></span></span></code></pre></div><p>Putting all of that together, we can make a word to parse a TXON string,
producing “name=value” pairs until exhausted:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-txon</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">objects</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup empty? not </span>] [ name=value ] <span class="nb">produce nip </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">txon></span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">object</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> parse-txon <span class="nb">dup length </span><span class="m">1 </span><span class="nb">= </span>[ <span class="nb">first </span>] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><h3 id="try-it">Try It</h3>
<p>You can try this out in the listener:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> H{ { <span class="s">"a"</span> <span class="s">"123"</span> } } >txon <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"a:`123`"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"a:`123`"</span> txon> <span class="m">.
</span></span></span><span class="line"><span class="cl">H{ { <span class="s">"a"</span> <span class="s">"123"</span> } }
</span></span></code></pre></div><p>Can you improve on this? Maybe by using the <code>peg.ebnf</code> vocabulary to
create an EBNF parsing word?</p>
<p>The code for this (and a bunch of tests) are on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/txon/txon.factor">GitHub</a>.</p>
Readability
https://re.factorcode.org/2012/02/readability.html
Mon, 13 Feb 2012 14:00:00 -0800https://re.factorcode.org/2012/02/readability.html<p>James O’Beirne wrote a great blog post on <a href="https://jameso.be/2012/02/11/language-matters.html">why languages
matter</a> with some
thoughts on predictability, readability, and compactness. In it, he
compares some examples of code in dynamic languages such as PHP, Python,
and Groovy.</p>
<p>I wanted to compare his simple “readability” examples with
<a href="https://factorcode.org">Factor</a>, to show <a href="https://evincarofautumn.blogspot.com/2012/02/why-concatenative-programming-matters.html">why concatenative programming
matters</a>.</p>
<h3 id="php">PHP</h3>
<p>This example of some PHP code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o"><?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl"><span class="nv">$x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$nums</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">40</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$res</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="nv">$nums</span> <span class="k">as</span> <span class="nv">$n</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nv">$n</span> <span class="o">></span> <span class="mi">15</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">$res</span> <span class="o">-=</span> <span class="nv">$n</span><span class="o">*</span><span class="mi">2</span> <span class="o">+</span> <span class="nv">$x</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="groovy">Groovy</h3>
<p>He compares the PHP code favorably to this Groovy code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl"><span class="kt">def</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="kt">def</span> <span class="n">nums</span> <span class="o">=</span> <span class="o">[</span><span class="mi">10</span><span class="o">,</span> <span class="mi">20</span><span class="o">,</span> <span class="mi">30</span><span class="o">,</span> <span class="mi">40</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="kt">def</span> <span class="n">res</span> <span class="o">=</span> <span class="n">nums</span><span class="o">.</span><span class="na">findAll</span> <span class="o">{</span> <span class="n">it</span> <span class="o">></span> <span class="mi">15</span> <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="o">.</span><span class="na">collect</span> <span class="o">{</span> <span class="n">it</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="n">x</span> <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="o">.</span><span class="na">inject</span><span class="o">(</span><span class="mi">0</span><span class="o">)</span> <span class="o">{</span><span class="n">accum</span><span class="o">,</span> <span class="n">val</span> <span class="o">-></span> <span class="n">accum</span> <span class="o">-</span> <span class="n">val</span><span class="o">}</span>
</span></span></code></pre></div><h3 id="python">Python</h3>
<p>Although James doesn’t show a Python example, it could look something
like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">x</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="n">nums</span> <span class="o">=</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span><span class="mi">20</span><span class="p">,</span><span class="mi">30</span><span class="p">,</span><span class="mi">40</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">res</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">nums</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">n</span> <span class="o">></span> <span class="mi">15</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">res</span> <span class="o">-=</span> <span class="p">(</span><span class="n">n</span><span class="o">*</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="n">x</span>
</span></span></code></pre></div><p>We could improve this by using a generator expression (or, equivalently,
a list comprehension)):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">>>></span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">nums</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span> <span class="k">return</span> <span class="o">-</span><span class="nb">sum</span><span class="p">(</span><span class="n">n</span><span class="o">*</span><span class="mi">2</span><span class="o">+</span><span class="n">x</span> <span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">nums</span> <span class="k">if</span> <span class="n">n</span> <span class="o">></span> <span class="mi">15</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">>>></span> <span class="n">foo</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span><span class="mi">20</span><span class="p">,</span><span class="mi">30</span><span class="p">,</span><span class="mi">40</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="mi">183</span>
</span></span></code></pre></div><h3 id="factor">Factor</h3>
<p>I would argue that a <a href="https://factorcode.org">Factor</a> version is pretty
readable:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">10 20 30 40 </span>} [ <span class="m">15 </span><span class="nb">> </span>] <span class="nb">filter
</span></span></span><span class="line"><span class="cl"> [ <span class="m">2 </span><span class="nb">* </span><span class="m">1 </span><span class="nb">+ </span>] <span class="nb">map sum neg </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">-183
</span></span></span></code></pre></div><p>If you wanted to factor (ahem) this into a reusable word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">foo</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">nums</span> <span class="nf">-- </span><span class="nv">res</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">15 </span><span class="nb">> </span>] <span class="nb">filter </span>[ <span class="m">2 </span><span class="nb">* + </span>] <span class="nb">with map sum neg </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1 </span>{ <span class="m">10 20 30 40 </span>} foo <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">-183
</span></span></span></code></pre></div>copy
https://re.factorcode.org/2012/02/copy.html
Thu, 02 Feb 2012 19:05:00 -0800https://re.factorcode.org/2012/02/copy.html<p>I’ve used <a href="https://factorcode.org">Factor</a> to build several common unix
programs including
<a href="https://re.factorcode.org/2010/08/building-cat.html">cat</a>,
<a href="https://re.factorcode.org/2010/09/fortune-telling.html">fortune</a>,
<a href="https://re.factorcode.org/2011/11/wc-l.html">wc</a>, and
<a href="https://github.com/mrjbq7/re-factor/tree/master/unix-tools">others</a>.</p>
<p>Today, I wanted to show how to build the <code>cp</code> (“copy”) program using the
simple <a href="https://re.factorcode.org/2011/09/manipulating-files.html">file
manipulation</a>
words. If we look at the <a href="https://linux.die.net/man/1/cp">man page</a>, we
can see that its usage is two-fold:</p>
<ol>
<li>Copy several source files to a destination directory</li>
<li>Copy a source file to a destination file or directory</li>
</ol>
<p>We can make a nice usage string to display if the arguments are not
correct:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">usage</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"Usage: copy source ... target"</span> <span class="nb">print </span><span class="k">;
</span></span></span></code></pre></div><p>We can implement the first usage, <code>copy-to-dir</code>, by checking to see that
the destination is a directory before calling
<a href="https://docs.factorcode.org/content/word-copy-files-into,io.directories.html">copy-files-into</a>,
or printing the usage if it is not:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">copy-to-dir</span> <span class="nf">( </span><span class="nv">args</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup last </span>file-info directory?
</span></span><span class="line"><span class="cl"> [ <span class="nb">unclip-last </span>copy-files-into ] [ <span class="nb">drop </span>usage ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>The second usage, <code>copy-to-file</code>, first checks if the destination exists
and is a directory (if so calling our <code>copy-to-dir</code> word), otherwise
calling
<a href="https://docs.factorcode.org/content/word-copy-file,io.directories.html">copy-file</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">copy-to-file</span> <span class="nf">( </span><span class="nv">args</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup last </span>{ [ exists? ] [ file-info directory? ] } 1&&
</span></span><span class="line"><span class="cl"> [ copy-to-dir ] [ <span class="nb">first2 </span>copy-file ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Putting it all together, we can implement our program by checking the
number of arguments and assuming the two-argument version is
<code>copy-to-file</code> and more arguments are <code>copy-to-dir</code> (anything less gets
the usage):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">run-copy</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> command-line <span class="nb">get dup length </span>{
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">2 </span><span class="nb">> </span>] [ <span class="nb">drop </span>copy-to-dir ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">2 </span><span class="nb">= </span>] [ <span class="nb">drop </span>copy-to-file ] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">2drop </span>usage ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">run-copy</span>
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/unix-tools/copy/copy.factor">GitHub</a>.</p>
Colored Timestamps
https://re.factorcode.org/2012/01/colored-timestamps.html
Wed, 25 Jan 2012 18:24:00 -0800https://re.factorcode.org/2012/01/colored-timestamps.html<p>I noticed a <a href="https://coding.pressbin.com/123/Second-Color-maps-seconds-to-RGBA-colors">fun
post</a>
in early December that implements a mapping between current time and a
“unique” <a href="https://en.wikipedia.org/wiki/RGBA_color_space">RGBA color</a>. I
thought it might be fun to use <a href="https://factorcode.org">Factor</a> to
implement a colored clock.</p>
<p>The basic concept is to map the 4,294,967,296 unique RGBA colors to
seconds, which gives just over 136 years of unique colors.</p>
<h3 id="timestamprgba">timestamp>rgba</h3>
<p>We calculate timestamps as an offset from <a href="https://en.wikipedia.org/wiki/Dennis_Ritchie">Dennis
Ritchie</a>’s birthday:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">start-date</span> <span class="nf">( -- </span><span class="nv">timestamp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1941 9 9 </span><date> <span class="k">; inline
</span></span></span></code></pre></div><p>The offset is an elapsed number of seconds from the start date:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">elapsed</span> <span class="nf">( </span><span class="nv">timestamp</span> <span class="nf">-- </span><span class="nv">seconds</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> start-date time- duration>seconds <span class="nb">>integer </span><span class="k">;
</span></span></span></code></pre></div><p>The conversion from a timestamp into a unique RGBA color does successive
<a href="https://docs.factorcode.org/content/word-__slash__mod,math.html">divmod</a>
operations to map into Red, Green, Blue, and Alpha values:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">timestamp>rgba</span> <span class="nf">( </span><span class="nv">timestamp</span> <span class="nf">-- </span><span class="nv">color/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> elapsed <span class="nb">dup </span><span class="m">0 32 </span><span class="nb">2^ </span>between? [
</span></span><span class="line"><span class="cl"> <span class="m">24 </span><span class="nb">2^ /mod </span><span class="m">16 </span><span class="nb">2^ /mod </span><span class="m">8 </span><span class="nb">2^ /mod
</span></span></span><span class="line"><span class="cl"> [ <span class="m">255 </span><span class="nb">/f </span>] <span class="m">4 </span>napply <rgba>
</span></span><span class="line"><span class="cl"> ] [ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>You can try it for yourself, showing how the values change over time:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> start-date timestamp>rgba <span class="m">.
</span></span></span><span class="line"><span class="cl">T{ rgba
</span></span><span class="line"><span class="cl"> { red <span class="m">0.0 </span>}
</span></span><span class="line"><span class="cl"> { green <span class="m">0.0 </span>}
</span></span><span class="line"><span class="cl"> { blue <span class="m">0.0 </span>}
</span></span><span class="line"><span class="cl"> { alpha <span class="m">0.0 </span>}
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> now timestamp>rgba <span class="m">.
</span></span></span><span class="line"><span class="cl">T{ rgba
</span></span><span class="line"><span class="cl"> { red <span class="m">0.5176470588235295 </span>}
</span></span><span class="line"><span class="cl"> { green <span class="m">0.3803921568627451 </span>}
</span></span><span class="line"><span class="cl"> { blue <span class="m">0.4313725490196079 </span>}
</span></span><span class="line"><span class="cl"> { alpha <span class="m">0.3333333333333333 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><h3 id="rgba-clock"><rgba-clock></h3>
<p>Let’s use the <code>timestamp>rgba</code> word to make an updating “colored clock”.
Specifically, we can use an <a href="https://docs.factorcode.org/content/article-models.arrow.html">arrow
model</a> to
update a
<a href="https://docs.factorcode.org/content/article-ui.gadgets.labels.html">label</a>
every second to create an RGBA clock:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">update-colors</span> <span class="nf">( </span><span class="nv">color</span> <span class="nv">label</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ font>> background<< ]
</span></span><span class="line"><span class="cl"> [ [ <solid> ] <span class="nb">dip </span>[ interior<< ] [ boundary<< ] <span class="nb">2bi </span>]
</span></span><span class="line"><span class="cl"> <span class="nb">2bi </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><rgba-clock></span> <span class="nf">( -- </span><span class="nv">gadget</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span><label-control>
</span></span><span class="line"><span class="cl"> time <span class="nb">get over </span>'[
</span></span><span class="line"><span class="cl"> [ timestamp>rgba _ update-colors ]
</span></span><span class="line"><span class="cl"> [ timestamp>hms ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] <arrow> >>model
</span></span><span class="line"><span class="cl"> <span class="s">"HH:MM:SS"</span> >>string
</span></span><span class="line"><span class="cl"> monospace-font >>font <span class="k">;
</span></span></span></code></pre></div><p>Use the
<a href="https://docs.factorcode.org/content/word-gadget.,ui.gadgets.panes.html">gadget.</a>
word to try it in your listener, and watch it update:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <rgba-clock> gadget.
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/second-color/second-color.factor">GitHub</a>.</p>
Friday the 13th
https://re.factorcode.org/2012/01/friday-the-13th.html
Fri, 13 Jan 2012 16:07:00 -0800https://re.factorcode.org/2012/01/friday-the-13th.html<p>In honor of January 13, 2012, a <a href="https://en.wikipedia.org/wiki/Friday_the_13th">Friday the
13th</a>, I thought it might
be fun to use <a href="https://factorcode.org">Factor</a> to explore similar dates
in past and future history. According to Wikipedia, such a day “<em>occurs
at least once, but at most three times a year</em>”.</p>
<h3 id="friday-13th">friday-13th?</h3>
<p>A day is “Friday the 13th” if it is both <em>(a)</em> Friday and <em>(b)</em> the
13th:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">friday-13th?</span> <span class="nf">( </span><span class="nv">timestamp</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ day>> <span class="m">13 </span><span class="nb">= </span>] [ friday? ] <span class="nb">bi and </span><span class="k">;
</span></span></span></code></pre></div><p>Trying it for today and tomorrow, to make sure it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> now friday-13th? <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">t
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="k">:</span> <span class="nf">tomorrow</span> <span class="nf">( -- </span><span class="nv">timestamp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> now <span class="m">1 </span>days time+ <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> tomorrow friday-13th? <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">f
</span></span></span></code></pre></div><h3 id="friday-13ths">friday-13ths</h3>
<p>Getting all Friday the 13th’s for a given year:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">friday-13ths</span> <span class="nf">( </span><span class="nv">year</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">12 </span>[0,b) [
</span></span><span class="line"><span class="cl"> <span class="m">13 </span><date> <span class="nb">dup </span>friday? [ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">with map sift </span><span class="k">;
</span></span></span></code></pre></div><p>Or, for a range of years:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-friday-13ths</span> <span class="nf">( </span><span class="nv">start-year</span> <span class="nv">end-year</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [a..b] [ friday-13ths ] <span class="nb">map concat </span><span class="k">;
</span></span></span></code></pre></div><p>Trying it for 2012:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2012 </span>friday-13ths <span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> T{ timestamp
</span></span><span class="line"><span class="cl"> { year <span class="m">2012 </span>}
</span></span><span class="line"><span class="cl"> { month <span class="m">1 </span>}
</span></span><span class="line"><span class="cl"> { day <span class="m">13 </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ timestamp
</span></span><span class="line"><span class="cl"> { year <span class="m">2012 </span>}
</span></span><span class="line"><span class="cl"> { month <span class="m">4 </span>}
</span></span><span class="line"><span class="cl"> { day <span class="m">13 </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> T{ timestamp
</span></span><span class="line"><span class="cl"> { year <span class="m">2012 </span>}
</span></span><span class="line"><span class="cl"> { month <span class="m">7 </span>}
</span></span><span class="line"><span class="cl"> { day <span class="m">13 </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><h3 id="next-friday-13th">next-friday-13th</h3>
<p>We can iterate, looking for the next Friday the 13th:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-friday-13th</span> <span class="nf">( </span><span class="nv">timestamp</span> <span class="nf">-- </span><span class="nv">date</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>day>> <span class="m">13 </span><span class="nb">>= </span>[ <span class="m">1 </span>months time+ ] <span class="nb">when </span><span class="m">13 </span>>>day
</span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>friday? <span class="nb">not </span>] [ <span class="m">1 </span>months time+ ] <span class="nb">while </span><span class="k">;
</span></span></span></code></pre></div><p>Trying it for today, shows the next Friday the 13th is April, 13, 2012:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> now next-friday-13th <span class="m">.
</span></span></span><span class="line"><span class="cl">T{ timestamp
</span></span><span class="line"><span class="cl"> { year <span class="m">2012 </span>}
</span></span><span class="line"><span class="cl"> { month <span class="m">4 </span>}
</span></span><span class="line"><span class="cl"> { day <span class="m">13 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>The code (and some tests) for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/friday-13th/friday-13th.factor">GitHub</a>.</p>
Duplicate Files
https://re.factorcode.org/2012/01/duplicate-files.html
Tue, 03 Jan 2012 18:45:00 -0800https://re.factorcode.org/2012/01/duplicate-files.html<p>A few months ago, Jon Cooper wrote a <a href="https://blog.carbonfive.com/2011/10/04/explorations-in-go-a-dupe-checker-in-go-and-ruby/">duplicate file
checker</a>
in <a href="https://golang.org">Go</a> and <a href="https://ruby-lang.org">Ruby</a>.</p>
<p>Below, I contribute a simple version in <a href="https://factorcode.org">Factor</a>
that runs faster than both Go and Ruby solutions. In the spirit of the
original article, I have separated the logic into steps.</p>
<h3 id="argument-parsing">Argument Parsing</h3>
<p>The <a href="https://docs.factorcode.org/content/article-cli.html">command-line</a>
vocabulary gives us the arguments passed to the script. We check for the
<em>verbose</em> flag and the root directory to traverse:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">arg?</span> <span class="nf">( </span><span class="nv">name</span> <span class="nv">args</span> <span class="nf">-- </span><span class="nv">args'</span> <span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">2dup member? </span>[ <span class="nb">remove </span><span class="no">t </span>] [ <span class="nb">nip </span><span class="no">f </span>] <span class="nb">if </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-args</span> <span class="nf">( -- </span><span class="nv">verbose?</span> <span class="nv">root</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"--verbose"</span> command-line <span class="nb">get </span>arg? <span class="nb">swap first </span><span class="k">;
</span></span></span></code></pre></div><h3 id="filesystem-traversal">Filesystem Traversal</h3>
<p>We can traverse the filesystem with the
<a href="https://docs.factorcode.org/content/word-each-file,io.directories.search.html">each-file</a>
word (choosing breadth-first instead of depth-first). In our case, we
want to collect these files into a map of all paths that share a common
filename:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">collect-files</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="no">t </span>H{ } <span class="nb">clone </span>[
</span></span><span class="line"><span class="cl"> '[ <span class="nb">dup </span>file-name _ <span class="nb">push-at </span>] each-file
</span></span><span class="line"><span class="cl"> ] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div><p>Our duplicate files are those files that share a common filename:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">duplicate-files</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">dupes</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> collect-files [ <span class="nb">nip length </span><span class="m">1 </span><span class="nb">> </span>] <span class="nb">assoc-filter! </span><span class="k">;
</span></span></span></code></pre></div><h3 id="md5-hashing-files">MD5 Hashing Files</h3>
<p>Using the
<a href="https://docs.factorcode.org/content/vocab-checksums.md5.html">checksums.md5</a>
vocabulary, it is quite simple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">md5-file</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">string</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> md5 checksum-file hex-string <span class="k">;
</span></span></span></code></pre></div><h3 id="printing-results">Printing Results</h3>
<p>If <em>verbose</em> is selected, then we print each filename and the MD5
checksum for each full path:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">print-md5</span> <span class="nf">( </span><span class="nv">name</span> <span class="nv">paths</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"%s:\n"</span> printf ] [
</span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>md5-file <span class="s">" %s\n %s\n"</span> printf ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">bi* </span><span class="k">;
</span></span></span></code></pre></div><p>We put this all together by calculating the possible duplicate files,
optionally printing verbose MD5 checksums, and then print the total
number of duplicates detected:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">run-dupe</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> parse-args duplicate-files <span class="nb">swap
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>[ print-md5 ] <span class="nb">assoc-each </span>] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> <span class="nb">assoc-size </span><span class="s">"Total duped files found: %d\n"</span> printf <span class="k">;
</span></span></span></code></pre></div><h3 id="performance">Performance</h3>
<p>I tested performance using two directory trees, one with over 500 files
and another with almost 36,000 files. While the original article focuses
more on syntax than speed, it is nice to see that the Factor solution is
faster than the Go and Ruby versions.</p>
<table>
<thead>
<tr class="header">
<th>Duplicates</th>
<th>Factor</th>
<th>Go</th>
<th>Ruby</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>583</td>
<td><strong>1.453</strong></td>
<td>2.298</td>
<td>3.861</td>
</tr>
<tr class="even">
<td>35,953</td>
<td><strong>19.084</strong></td>
<td>24.452</td>
<td>30.597</td>
</tr>
</tbody>
</table>
<p><em>Note: The above time is seconds on my laptop.</em></p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/dupe/dupe.factor">GitHub</a>.</p>
Picomath
https://re.factorcode.org/2011/12/picomath.html
Sat, 31 Dec 2011 16:08:00 -0800https://re.factorcode.org/2011/12/picomath.html<p>The <a href="https://picomath.org">Picomath</a> project holds some reusable math
functions inspired by John D. Cook’s <a href="https://www.johndcook.com/stand_alone_code.html">Stand-alone code for numerical
computing</a>, including:</p>
<ul>
<li>Error function</li>
<li>Phi (standard normal CDF)</li>
<li>Phi inverse</li>
<li>Gamma</li>
<li>Log Gamma</li>
<li>exp(x) - 1 (for small x)</li>
<li>log(n!)</li>
</ul>
<p>These functions are <a href="https://github.com/ghewgill/picomath">implemented</a>
in an impressive list of languages: Ada, C++, C#, D, Erlang, Go,
Haskell, Java, Javascript, Lua, Pascal, Perl, PHP, Python (2.x and 3.x),
Ruby, Scheme, and Tcl.</p>
<p>And now <a href="https://factorcode.org">Factor</a>!</p>
<p>You can find the code (and a bunch of tests) for this on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/picomath">GitHub</a>.</p>
Slot Machines
https://re.factorcode.org/2011/12/slot-machines.html
Fri, 30 Dec 2011 14:48:00 -0800https://re.factorcode.org/2011/12/slot-machines.html<p>Playing <a href="https://en.wikipedia.org/wiki/Slot_machine">slot machines</a> can
be pretty fun, but don’t be fooled by claims that the casino has the
“loosest slots”, odds are probably still against you. I thought it would
be fun (<em>and cheaper!</em>) to build a slot machine simulator using
<a href="https://factorcode.org">Factor</a>.</p>
<p>Our slot machine is going to be a <a href="https://en.wikipedia.org/wiki/Console_application">console
application</a> that will
look something like this:</p>
<p>
<img src="https://re.factorcode.org/images/2011-12-30-slot-machines-slot-machine.png" alt="" width="220" height="190" />
</p>
<h3 id="spinning">Spinning</h3>
<p>Even though our slot machine is text-only, we can still make use of some
nice unicode characters to be our symbols:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">SYMBOLS</span> <span class="s">"☀☁☂☃"</span>
</span></span></code></pre></div><p>Each spin chooses a different symbol at random (each being equally
likely):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">spin</span> <span class="nf">( </span><span class="nv">value</span> <span class="nf">-- </span><span class="nv">value'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> SYMBOLS <span class="nb">remove </span>random <span class="k">;
</span></span></span></code></pre></div><p>To reproduce the feel of spinning a slot machine, we will introduce a
slight delay so that the wheel spins fast at the beginning and then
slower and slower until it stops on a symbol:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">spin-delay</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="m">15 </span><span class="nb">* </span><span class="m">25 </span><span class="nb">+ </span>milliseconds sleep <span class="k">;
</span></span></span></code></pre></div><p>Spinning the slot machine takes a spin number, delays for a bit, then
rotates each wheel (we stop spinning the first column after 10 spins,
the second after 15, and the last after 20):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">spin-slots</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ spin-delay ]
</span></span><span class="line"><span class="cl"> [ <span class="m">10 </span><span class="nb">< </span>[ [ spin ] <span class="nb">2dip </span>] <span class="nb">when </span>]
</span></span><span class="line"><span class="cl"> [ <span class="m">15 </span><span class="nb">< </span>[ [ spin ] <span class="nb">dip </span>] <span class="nb">when </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>spin ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span><span class="k">;
</span></span></span></code></pre></div><h3 id="display">Display</h3>
<p>Each “spin” of the slot machine will be printed out. Using <a href="https://ascii-table.com/ansi-escape-sequences.php">ANSI escape
sequences</a>, we move
the cursor to the top left (“0,0”) of the screen and then issue a clear
screen instruction. Then we print out the current display and flush the
output to screen:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">print-spin</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nf">-- </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"\e[0;0H\e[2J"</span> <span class="nb">write
</span></span></span><span class="line"><span class="cl"> <span class="s">"Welcome to the Factor slot machine!"</span> <span class="nb">print nl
</span></span></span><span class="line"><span class="cl"> <span class="s">" +--------+"</span> <span class="nb">print
</span></span></span><span class="line"><span class="cl"> <span class="s">" | CASINO |"</span> <span class="nb">print
</span></span></span><span class="line"><span class="cl"> <span class="s">" |--------| *"</span> <span class="nb">print
</span></span></span><span class="line"><span class="cl"> <span class="nb">3dup </span><span class="s">" |%c |%c |%c | |\n"</span> printf
</span></span><span class="line"><span class="cl"> <span class="s">" |--------|/"</span> <span class="nb">print
</span></span></span><span class="line"><span class="cl"> <span class="s">" | [_] |"</span> <span class="nb">print
</span></span></span><span class="line"><span class="cl"> <span class="s">" +--------+"</span> <span class="nb">print flush </span><span class="k">;
</span></span></span></code></pre></div><h3 id="playing">Playing</h3>
<p>The player will have won if, after all the spins, the “pay line” shows
three of the same characters:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">winner?</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">3array </span>all-equal? <span class="nb">nl </span><span class="s">"You WIN!"</span> <span class="s">"You LOSE!"</span> <span class="nb">? print nl </span><span class="k">;
</span></span></span></code></pre></div><p>Playing the slot machine consists of spinning the wheels 20 times,
displaying each spin to the user, then checking if the user has won the
game.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">play-slots</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="no">f f f </span><span class="m">20 </span><iota> [ spin-slots print-spin ] <span class="nb">each </span>winner? <span class="k">;
</span></span></span></code></pre></div><p>Since our casino wants the user to keep playing, we make it really easy
to just hit ENTER to continue:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">continue?</span> <span class="nf">( -- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"Press ENTER to play again."</span> <span class="nb">write flush readln </span><span class="k">;
</span></span></span></code></pre></div><p>And, to finish it off, we define a “MAIN” entry point that will be run
when the script is executed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">main-slots</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> [ play-slots continue? ] <span class="nb">loop </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">main-slots</span>
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/slot-machine/slot-machine.factor">GitHub</a>.</p>
Elementology
https://re.factorcode.org/2011/12/elementology.html
Wed, 28 Dec 2011 12:56:00 -0800https://re.factorcode.org/2011/12/elementology.html<p><strong>Question:</strong> What do the words <em>bamboo</em>, <em>crunchy</em>, <em>finance</em>,
<em>genius</em>, and <em>tenacious</em> have in common? I’ll give you a hint: its the
same thing they have in common with the words <em>who</em>, <em>what</em>, <em>when</em>,
<em>where</em>, and <em>how</em>?</p>
<p>Stumped? Well, it’s not that these are all English words.</p>
<p><strong>Answer:</strong> All of these words can be spelled using elements from the
periodic table!</p>
<p>I was recently inspired by the <a href="https://www.thinkgeek.com/tshirts-apparel/unisex/sciencemath/ea07/?srp=2">Periodic GeNiUS
T-shirt</a>
from <a href="https://www.thinkgeek.com">ThinkGeek</a> and a
<a href="https://www.lmntology.com/">website</a> that can “make any words out of
elements in the periodic table”. I thought it would be fun to use
<a href="https://factorcode.org">Factor</a> to see how many other words can be
spelled using the symbols for chemical elements.</p>
<p>
<img src="https://re.factorcode.org/images/2011-12-28-elementology-periodic_genius.jpg" alt="" width="600" height="342" />
</p>
<p>First, we need a list of elements:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">elements</span> <span class="nf">( -- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> H{
</span></span><span class="line"><span class="cl"> { <span class="s">"H"</span> <span class="s">"Hydrogen"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"He"</span> <span class="s">"Helium"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"Li"</span> <span class="s">"Lithium"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"Be"</span> <span class="s">"Beryllium"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"B"</span> <span class="s">"Boron"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"C"</span> <span class="s">"Carbon"</span> }
</span></span><span class="line"><span class="cl"> ...
</span></span><span class="line"><span class="cl"> { <span class="s">"Uut"</span> <span class="s">"Ununtrium"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"Uuq"</span> <span class="s">"Ununquadium"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"Uup"</span> <span class="s">"Ununpentium"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"Uuh"</span> <span class="s">"Ununhexium"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"Uus"</span> <span class="s">"Ununseptium"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"Uuo"</span> <span class="s">"Ununoctium"</span> }
</span></span><span class="line"><span class="cl"> } [ [ >lower ] <span class="nb">dip </span>] <span class="nb">assoc-map </span><span class="k">;
</span></span></span></code></pre></div><p>Next, a word that checks if a particular substring is the symbol of an
element:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">element?</span> <span class="nf">( </span><span class="nv">from</span> <span class="nv">to</span> <span class="nv">word</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">2dup length > </span>[ <span class="nb">3drop </span><span class="no">f </span>] [ <span class="nb">subseq </span>elements <span class="nb">key? </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>We know that symbols are only ever one, two, or three characters. A word
is considered “periodic” if it can be composed of any number of
(possibly repeating) element symbols. We build a recursive solution that
starts with the first character and continues as long as element symbols
are a match or until the end of the word is reached:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(periodic?)</span> <span class="nf">( </span><span class="nv">word</span> <span class="nv">from</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap length = </span>]
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> { <span class="m">1 2 3 </span>} [
</span></span><span class="line"><span class="cl"> <span class="nb">dupd + </span>[ <span class="nb">pick </span>element? ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> '[ <span class="nb">dup </span>_ (periodic?) ] [ <span class="no">f </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">with any? nip
</span></span></span><span class="line"><span class="cl"> ]
</span></span><span class="line"><span class="cl"> } 2|| <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">periodic?</span> <span class="nf">( </span><span class="nv">word</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> >lower <span class="m">0 </span>(periodic?) <span class="k">;
</span></span></span></code></pre></div><p>It’s easy to get a list of dictionary words from most Unix systems:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">dict-words</span> <span class="nf">( -- </span><span class="nv">words</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"/usr/share/dict/words"</span> ascii file-lines <span class="k">;
</span></span></span></code></pre></div><p>And then a list of all “periodic words”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">periodic-words</span> <span class="nf">( -- </span><span class="nv">words</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> dict-words [ periodic? ] <span class="nb">filter </span><span class="k">;
</span></span></span></code></pre></div><p>So, how many words are “periodic words”? About 13.7% of them.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> dict-words <span class="nb">length </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">235886
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> periodic-words <span class="nb">length </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">32407
</span></span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/periodic-words/periodic-words.factor">GitHub</a>.</p>
wc -l
https://re.factorcode.org/2011/11/wc-l.html
Thu, 10 Nov 2011 14:45:00 -0800https://re.factorcode.org/2011/11/wc-l.html<p>The <code>wc</code> program is a utility that can count the number of lines in a
file. It has a number of options that are described on its <a href="https://linux.die.net/man/1/wc">man
page</a> that can change its function to
count characters or word or produce the length of the longest line.</p>
<p>For the last month, Joe Groff has been improving the performance of
<a href="https://factorcode.org">Factor’s</a> I/O libraries. Yesterday, we were
<a href="https://github.com/factor/factor/issues/376">investigating</a> slow
performance when doing lots of small reads from a file. Joe was able to
make a number of nice speedups, which will be in the next release of
Factor.</p>
<p>After suggesting that we use <code>wc -l</code> as a benchmark to aspire to, we
came up with several approaches with varying performance. I want to
demonstrate these, using timing information from running it on my
computer. Although the Factor image file is binary data, we are going to
count the number of newline characters in it.</p>
<blockquote>
<p><em>Note: some of these require the latest development version of Factor
to run.</em></p>
</blockquote>
<p>Our “gold standard” will be <code>wc -l</code>, which takes just over 0.1 seconds:</p>
<pre tabindex="0"><code>$ time wc -l factor.image
6149212 factor.image
real 0m0.111s
user 0m0.090s
sys 0m0.020s
</code></pre><p>Our first attempt in Factor is the shortest amount of code but takes 5.8
seconds (you can time this by running
“<code>USE: system [ image wc-file-lines ] time</code>”):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wc-file-lines</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> binary file-lines <span class="nb">length </span><span class="k">;
</span></span></span></code></pre></div><p>We don’t really need the lines, just their count, so perhaps just
increment <code>each-line</code> in a loop. This is an improvement at just over 3
seconds:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wc-each-line</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> binary [ <span class="m">0 </span>[ <span class="nb">drop </span><span class="m">1 </span><span class="nb">+ </span>] <span class="nb">each-line </span>] with-file-reader <span class="k">;
</span></span></span></code></pre></div><p>Trying to use <code>read-until</code> to look for the next newline, is a bit slower
at 3.4 seconds:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wc-read-until</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> binary [
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>[ <span class="s">"\n"</span> <span class="nb">read-until </span>[ <span class="nb">drop </span><span class="m">1 </span><span class="nb">+ </span>] <span class="nb">dip </span>] <span class="nb">loop
</span></span></span><span class="line"><span class="cl"> ] with-file-reader <span class="k">;
</span></span></span></code></pre></div><p>Instead of reading each line at a time, we can just read 65,536 byte
blocks and count the number of newlines. This takes about 1.5 seconds:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wc-each-block</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> binary [
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>[ [ <span class="sc">CHAR: \n </span><span class="nb">= </span>] <span class="nb">count + </span>] <span class="nb">each-block
</span></span></span><span class="line"><span class="cl"> ] with-file-reader <span class="k">;
</span></span></span></code></pre></div><p>Since we are only counting characters in each block, we don’t need to
allocate and copy the bytes out of the I/O buffer. Instead, we can look
at a <code>slice</code>. This takes about 1 second:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wc-each-block-slice</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> binary [
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>[ [ <span class="sc">CHAR: \n </span><span class="nb">= </span>] <span class="nb">count + </span>] <span class="nb">each-block-slice
</span></span></span><span class="line"><span class="cl"> ] with-file-reader <span class="k">;
</span></span></span></code></pre></div><p>The stream functions (such as <code>read</code>) operate on an <code>input-stream</code>
dynamic variable, which introduces some overhead. If we remove that, the
compiler can eliminate some of the dynamic dispatches. taking 0.320
seconds:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wc-fast</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> binary <file-reader> [
</span></span><span class="line"><span class="cl"> <span class="m">0 </span><span class="nb">swap </span>[
</span></span><span class="line"><span class="cl"> [ <span class="sc">CHAR: \n </span><span class="nb">= </span>] <span class="nb">count +
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-stream-block-slice
</span></span></span><span class="line"><span class="cl"> ] with-disposal <span class="k">;
</span></span></span></code></pre></div><p>If we make an assumption that the number of lines in a file will fit
into a <code>fixnum</code>, then we can get a bit faster at 0.240 seconds:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wc-faster</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> binary <file-reader> [
</span></span><span class="line"><span class="cl"> <span class="m">0 </span><span class="nb">swap </span>[
</span></span><span class="line"><span class="cl"> [ <span class="sc">CHAR: \n </span><span class="nb">= </span>] <span class="nb">count + >fixnum
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-stream-block-slice
</span></span></span><span class="line"><span class="cl"> ] with-disposal <span class="k">;
</span></span></span></code></pre></div><p>And, if we cheat and just run the <code>wc -l</code> process directly, we can get
to 0.210 seconds:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wc-system</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"wc -l "</span> <span class="nb">prepend </span>utf8 [
</span></span><span class="line"><span class="cl"> <span class="nb">readln </span><span class="s">" "</span> split <span class="nb">harvest first </span>string>number
</span></span><span class="line"><span class="cl"> ] with-process-reader <span class="k">;
</span></span></span></code></pre></div><p>Overall, not too bad! <a href="https://factorcode.org">Factor</a> is getting within
shouting distance of programs written in “faster” languages. Probably
with a few more hours, we could close the gap, but thats enough for
today.</p>
<p><em>Update: using <a href="https://en.wikipedia.org/wiki/SIMD">SIMD</a>, Joe was able
to get the time down to 0.120 seconds!</em></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">aligned-slices</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">head</span> <span class="nv">tail</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup length </span><span class="m">15 </span><span class="nb">bitnot bitand cut-slice </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wc-simd</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="m">0 </span><span class="nb">swap </span>binary <file-reader> &dispose [
</span></span><span class="line"><span class="cl"> aligned-slices [
</span></span><span class="line"><span class="cl"> uchar-16 cast-array <span class="nb">swap
</span></span></span><span class="line"><span class="cl"> [ <span class="m">10 </span>uchar-16-with v= vcount <span class="nb">+ >fixnum </span>] <span class="nb">reduce
</span></span></span><span class="line"><span class="cl"> ] [ [ <span class="m">10 </span><span class="nb">= </span>] <span class="nb">count + >fixnum </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-stream-block-slice
</span></span></span><span class="line"><span class="cl"> ] with-destructors <span class="k">;
</span></span></span></code></pre></div>Disassemble
https://re.factorcode.org/2011/10/disassemble.html
Wed, 26 Oct 2011 11:34:00 -0700https://re.factorcode.org/2011/10/disassemble.html<p>A neat trick that <a href="https://factorcode.org">Factor</a> provides is the
ability to disassemble functions into the machine code that is generated
by the compiler. In 2008, Slava Pestov <a href="https://factor-language.blogspot.com/2008/02/invoking-gdb-disassembler-to.html">created a
disassembler</a>,
and has improved it a bit since then (switching to
<a href="https://udis86.sourceforge.net/">udis86</a> for its implementation).</p>
<h3 id="constant-folding">Constant Folding</h3>
<p>The compiler performs constant folding, using the
<a href="https://docs.factorcode.org/content/vocab-compiler.tree.debugger.html">compiler.tree.debugger</a>
vocabulary, you can output the optimized form of a quotation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">2 2 </span><span class="nb">+ </span>] optimized.
</span></span><span class="line"><span class="cl">[ <span class="m">4 </span>]
</span></span></code></pre></div><p>Using the disassembler, you can see the machine code this generates:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">2 2 </span><span class="nb">+ </span>] disassemble
</span></span><span class="line"><span class="cl">011c1a5530: 4983c608 add r14, <span class="m">0x8
</span></span></span><span class="line"><span class="cl">011c1a5534: 49c70640000000 mov qword [r14], <span class="m">0x40
</span></span></span><span class="line"><span class="cl">011c1a553b: c3 ret
</span></span><span class="line"><span class="cl">011c1a553c: <span class="m">0000 </span> add [rax], al
</span></span><span class="line"><span class="cl">011c1a553e: <span class="m">0000 </span> add [rax], al
</span></span></code></pre></div><h3 id="local-variables">Local Variables</h3>
<p>One of the questions that comes up sometimes is whether <a href="https://docs.factorcode.org/content/article-locals.html">local
variables</a>
affect performance. We can examine two words that add numbers together,
one using locals and one just using the stack:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="k">:</span> <span class="nf">foo</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nf">-- </span><span class="nv">z</span> <span class="nf">) </span><span class="nb">+ </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="k">::</span> <span class="nf">bar</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nf">-- </span><span class="nv">z</span> <span class="nf">) </span>x y <span class="nb">+ </span><span class="k">;
</span></span></span></code></pre></div><p>The “optimized output” looks a little different:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="no">\ foo</span> optimized.
</span></span><span class="line"><span class="cl">[ <span class="nb">+ </span>]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="no">\ bar</span> optimized.
</span></span><span class="line"><span class="cl">[ <span class="s">"COMPLEX SHUFFLE"</span> <span class="s">"COMPLEX SHUFFLE"</span> R> <span class="nb">+ </span>]
</span></span></code></pre></div><p>But, the machine code that is generated is identical:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="no">\ foo</span> disassemble
</span></span><span class="line"><span class="cl">01115de7b0: 488d1d05000000 lea rbx, [rip+0x5]
</span></span><span class="line"><span class="cl">01115de7b7: e9e49439ff jmp <span class="m">0x110977ca0 </span>(+)
</span></span><span class="line"><span class="cl">01115de7bc: <span class="m">0000 </span> add [rax], al
</span></span><span class="line"><span class="cl">01115de7be: <span class="m">0000 </span> add [rax], al
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="no">\ bar</span> disassemble
</span></span><span class="line"><span class="cl">01115ef620: 488d1d05000000 lea rbx, [rip+0x5]
</span></span><span class="line"><span class="cl">01115ef627: e9748638ff jmp <span class="m">0x110977ca0 </span>(+)
</span></span><span class="line"><span class="cl">01115ef62c: <span class="m">0000 </span> add [rax], al
</span></span><span class="line"><span class="cl">01115ef62e: <span class="m">0000 </span> add [rax], al
</span></span></code></pre></div><h3 id="dynamic-variables">Dynamic Variables</h3>
<p>Another frequently used feature is <a href="https://docs.factorcode.org/content/article-namespaces.html">dynamic
variables</a>,
implemented by the <code>namespaces</code> vocabulary. For example, the definition
of the <code>print</code> word looks for the current value of the <code>output-stream</code>
variable and then calls <code>stream-print</code> on it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="no">\ print</span> see
</span></span><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">namespaces</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">io</span>
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">print</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- ) </span><span class="nb">output-stream get stream-print </span><span class="k">; inline
</span></span></span></code></pre></div><p>The optimized output inlines the implementation of
<a href="https://docs.factorcode.org/content/word-get%2Cnamespaces.html">get</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"Hello, world"</span> <span class="nb">print </span>] optimized.
</span></span><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl"> <span class="s">"Hello, world"</span> <span class="no">\ output-stream</span> <span class="m">0 </span>context-object <span class="nb">assoc-stack
</span></span></span><span class="line"><span class="cl"> <span class="nb">stream-print
</span></span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><p>You can inspect the machine code generated, seeing references to the
factor words that are being called:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"Hello, world"</span> <span class="nb">print </span>] disassemble
</span></span><span class="line"><span class="cl">011c0c6c40: 4c8d1df9ffffff lea r11, [rip-0x7]
</span></span><span class="line"><span class="cl">011c0c6c47: <span class="m">6820000000 </span> <span class="nb">push </span>dword <span class="m">0x20
</span></span></span><span class="line"><span class="cl">011c0c6c4c: <span class="m">4153 </span> <span class="nb">push </span>r11
</span></span><span class="line"><span class="cl">011c0c6c4e: 4883ec08 sub rsp, <span class="m">0x8
</span></span></span><span class="line"><span class="cl">011c0c6c52: 4983c618 add r14, <span class="m">0x18
</span></span></span><span class="line"><span class="cl">011c0c6c56: 48b8dbc5a31a01000000 mov rax, <span class="m">0x11aa3c5db
</span></span></span><span class="line"><span class="cl">011c0c6c60: 498946f0 mov [r14-0x10], rax
</span></span><span class="line"><span class="cl">011c0c6c64: 498b4500 mov rax, [r13+0x0]
</span></span><span class="line"><span class="cl">011c0c6c68: 488b4040 mov rax, [rax+0x40]
</span></span><span class="line"><span class="cl">011c0c6c6c: <span class="m">498906 </span> mov [r14], rax
</span></span><span class="line"><span class="cl">011c0c6c6f: 48b86c91810e01000000 mov rax, <span class="m">0x10e81916c
</span></span></span><span class="line"><span class="cl">011c0c6c79: 498946f8 mov [r14-0x8], rax
</span></span><span class="line"><span class="cl">011c0c6c7d: e8de4e36ff <span class="nb">call </span><span class="m">0x11b42bb60 </span>(assoc-stack)
</span></span><span class="line"><span class="cl">011c0c6c82: 4883c418 add rsp, <span class="m">0x18
</span></span></span><span class="line"><span class="cl">011c0c6c86: 488d1d05000000 lea rbx, [rip+0x5]
</span></span><span class="line"><span class="cl">011c0c6c8d: e94e5264ff jmp <span class="m">0x11b70bee0 </span>(stream-print)
</span></span><span class="line"><span class="cl">011c0c6c92: <span class="m">0000 </span> add [rax], al
</span></span><span class="line"><span class="cl">011c0c6c94: <span class="m">0000 </span> add [rax], al
</span></span><span class="line"><span class="cl">011c0c6c96: <span class="m">0000 </span> add [rax], al
</span></span><span class="line"><span class="cl">011c0c6c98: <span class="m">0000 </span> add [rax], al
</span></span><span class="line"><span class="cl">011c0c6c9a: <span class="m">0000 </span> add [rax], al
</span></span><span class="line"><span class="cl">011c0c6c9c: <span class="m">0000 </span> add [rax], al
</span></span><span class="line"><span class="cl">011c0c6c9e: <span class="m">0000 </span> add [rax], al
</span></span></code></pre></div>Optimizing 2^x
https://re.factorcode.org/2011/10/optimizing-2-x.html
Wed, 12 Oct 2011 22:59:00 -0700https://re.factorcode.org/2011/10/optimizing-2-x.html<p>A great little article was posted last year about <a href="https://falasol.net/2-pow-x-optimization-for-double-type">optimizing 2^x for
doubles</a> (by
approximation). The author gets 40+% performance improvements in C#. I
wondered if we could get similar improvements in
<a href="https://factorcode.org">Factor</a>.</p>
<p>The basic idea is to use the fact that a number, <code>ab+c</code> can be factored
into <code>ab * ac</code>. If we separate a floating point number into two
components: an integer and a fractional part, we can show that:</p>
<blockquote>
<p><code>2n = 2integer(n) * 2fractional(n)</code>.</p>
</blockquote>
<p>We are going to approximate this value by using a lookup table to
compute the fractional part (within a specified precision). For example,
to compute within a 0.001 precision, we need 1000 lookup values,
essentially performing this calculation:</p>
<pre tabindex="0"><code>2n = ( 1 << Int(n) ) * Table[ (int) ( Frac(n) * 1000 ) ];
</code></pre><h3 id="implementation">Implementation</h3>
<p>So, we need a word that can split a floating point number into those two
values:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">float>parts</span> <span class="nf">( </span><span class="nv">x</span> <span class="nf">-- </span><span class="nv">float</span> <span class="nv">int</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup >integer </span>[ <span class="nb">- </span>] <span class="nb">keep </span><span class="k">; inline
</span></span></span></code></pre></div><p>Instead of one table with 1000 values, we will copy the original authors
decision to use three lookup tables for additional precision. The
following code calculates these lookup tables:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">BITS1</span> <span class="m">10
</span></span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">BITS2</span> $[ BITS1 <span class="m">2 </span><span class="nb">* </span>]
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">BITS3</span> $[ BITS1 <span class="m">3 </span><span class="nb">* </span>]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">PRECISION1</span> $[ <span class="m">1 </span>BITS1 <span class="nb">shift </span>]
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">PRECISION2</span> $[ <span class="m">1 </span>BITS2 <span class="nb">shift </span>]
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">PRECISION3</span> $[ <span class="m">1 </span>BITS3 <span class="nb">shift </span>]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">MASK</span> $[ PRECISION1 <span class="m">1 </span><span class="nb">- </span>]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">FRAC1</span> $[ <span class="m">2 </span>PRECISION1 <iota> [ PRECISION1 <span class="nb">/ </span>^ ] <span class="nb">with map </span>]
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">FRAC2</span> $[ <span class="m">2 </span>PRECISION1 <iota> [ PRECISION2 <span class="nb">/ </span>^ ] <span class="nb">with map </span>]
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">FRAC3</span> $[ <span class="m">2 </span>PRECISION1 <iota> [ PRECISION3 <span class="nb">/ </span>^ ] <span class="nb">with map </span>]
</span></span></code></pre></div><p>The function <code>pow2</code> looks pretty similar to our original mathematical
definition:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pow2</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">2^n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">>float </span>2^int 2^frac <span class="nb">* >float </span><span class="k">;
</span></span></span></code></pre></div><p>The guts of the implementation is in the <code>2^int</code> and <code>2^frac</code> words:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">2^int</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">2^int</span> <span class="nv">frac</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ float>parts ] <span class="nb">keep </span><span class="m">0 </span><span class="nb">>= </span>[ <span class="m">1 </span><span class="nb">swap shift </span>] [
</span></span><span class="line"><span class="cl"> <span class="nb">over </span><span class="m">0 </span><span class="nb">< </span>[ [ <span class="m">1 </span><span class="nb">+ </span>] [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">bi* </span>] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="nb">swap neg shift </span><span class="m">1.0 </span><span class="nb">swap /
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if swap </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">2^frac</span> <span class="nf">( </span><span class="nv">frac</span> <span class="nf">-- </span><span class="nv">2^frac</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> PRECISION3 <span class="nb">* >fixnum
</span></span></span><span class="line"><span class="cl"> [ BITS2 <span class="nb">neg shift </span>FRAC1 nth-unsafe ]
</span></span><span class="line"><span class="cl"> [ BITS1 <span class="nb">neg shift </span>MASK <span class="nb">bitand </span>FRAC2 nth-unsafe ]
</span></span><span class="line"><span class="cl"> [ MASK <span class="nb">bitand </span>FRAC3 nth-unsafe ] <span class="nb">tri * * </span><span class="k">; inline
</span></span></span></code></pre></div><h3 id="testing">Testing</h3>
<p>Let’s try it and see how well it works for small values:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2 1.5 </span>^ <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">2.82842712474619
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1.5 </span>pow2 <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">2.82842712474619
</span></span></span></code></pre></div><p>It seem’s to work, how about larger values:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2 16.3 </span>^ <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">80684.28027297248
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">16.3 </span>pow2 <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">80684.28026255539
</span></span></span></code></pre></div><p>The error is clearly detectable, but to test that it really works the
way we expect it too, we will need to calculate relative error:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">relative-error</span> <span class="nf">( </span><span class="nv">approx</span> <span class="nv">value</span> <span class="nf">-- </span><span class="nv">relative-error</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">- abs </span>] <span class="nb">keep / </span><span class="k">;
</span></span></span></code></pre></div><p>Our test case will generate random values, compute 2<sup>x</sup> using
our approximation and verify the relative error is less than 0.000000001
when compared with the correct result:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">[ <span class="no">t </span>] [
</span></span><span class="line"><span class="cl"> <span class="m">10000 </span>[ <span class="m">-20 20 </span>uniform-random-float ] <span class="nb">replicate
</span></span></span><span class="line"><span class="cl"> [ [ pow2 ] [ <span class="m">2 </span><span class="nb">swap </span>^ ] <span class="nb">bi </span>relative-error ] <span class="nb">map
</span></span></span><span class="line"><span class="cl"> <span class="nb">supremum </span><span class="m">1e-9 </span><span class="nb"><
</span></span></span><span class="line"><span class="cl">] unit-test
</span></span></code></pre></div><p>And to verify performance, we will benchmark it against the built-in
(and more accurate <code>^</code> word):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pow2-test</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">new</span> <span class="nv">old</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ pow2 <span class="nb">drop </span>] [ <span class="nb">each </span>] benchmark ]
</span></span><span class="line"><span class="cl"> [ <span class="m">2 </span><span class="nb">swap </span>[ ^ <span class="nb">drop </span>] <span class="nb">with </span>[ <span class="nb">each </span>] benchmark ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>We got almost a 40% performance improvement at the cost of a small loss
of precision - not bad!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10000 </span>[ <span class="m">-20 20 </span>uniform-random-float ] <span class="nb">replicate
</span></span></span><span class="line"><span class="cl"> pow2-test <span class="nb">/ >float </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">0.6293153754782392
</span></span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/fast-pow/fast-pow.factor">GitHub</a>.</p>
Approximating Pi
https://re.factorcode.org/2011/09/approximating-pi.html
Sun, 25 Sep 2011 16:18:00 -0700https://re.factorcode.org/2011/09/approximating-pi.html<p>A few days ago, it was
<a href="https://blog.wolfram.com/2011/09/15/from-pi-to-puzzles/">announced</a> on
the Wolfram Blog that a 13-year-old had made a record calculating 458
million terms for the continued fraction of <code>pi</code>. In the spirit of that,
I thought I would show how to solve a question that sometimes gets asked
at interviews:</p>
<blockquote>
<p><em>Given that Pi can be estimated using the function 4 * (1 - 1/3 +
1/5 - 1/7 + …) with more terms giving greater accuracy, write a
function that calculates Pi to an accuracy of 5 decimal places.</em></p>
</blockquote>
<p>Using <a href="https://factorcode.org">Factor</a>, we can calculate the <code>nth</code>
approximation of <code>pi</code> using <a href="https://docs.factorcode.org/content/article-math-vectors-arithmetic.html">vector
arithmetic</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">approximate-pi</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">approx</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [1..b] <span class="m">2 </span>v*n <span class="m">1 </span>v-n <span class="m">1 </span><span class="nb">swap </span>n/v
</span></span><span class="line"><span class="cl"> [ <span class="nb">odd? </span>[ <span class="nb">neg </span>] <span class="nb">when </span>] <span class="nb">map-index sum </span><span class="m">4 </span><span class="nb">* </span><span class="k">;
</span></span></span></code></pre></div><p>This isn’t ideal if we want to try an increasing number of terms (looking
for a particularly accuracy), since a lot of the work would be redone
unnecessarily. Instead, we can write a word that adds successive terms until
the difference between the previous approximation and the current
approximation is less than our requested accuracy.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-term</span> <span class="nf">( </span><span class="nv">approx</span> <span class="nv">i</span> <span class="nf">-- </span><span class="nv">approx'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">2 </span><span class="nb">* </span><span class="m">1 </span><span class="nb">+ </span>] [ <span class="nb">odd? </span>[ <span class="nb">neg </span>] <span class="nb">when </span>] <span class="nb">bi </span><span class="m">4.0 </span><span class="nb">swap / + </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">find-pi-to</span> <span class="nf">( </span><span class="nv">accuracy</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nv">approx</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1 4.0 </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">dup pick </span>next-term [ <span class="nb">- </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> <span class="nb">swap abs </span>accuracy <span class="nb">>= </span>[ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">2dip
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop </span><span class="k">;
</span></span></span></code></pre></div><p>To show its performance, we can
<a href="https://docs.factorcode.org/content/word-time,tools.time.html">time</a> it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">0.00001 </span>find-pi-to ] time <span class="m">.
</span></span></span><span class="line"><span class="cl">Running time: <span class="m">0.026030341 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="m">3.141597653564762
</span></span></span></code></pre></div><p>An equivalent function in Python might look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">find_pi_to</span><span class="p">(</span><span class="n">accuracy</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="n">approx</span> <span class="o">=</span> <span class="mf">4.0</span>
</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="mi">1</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">term</span> <span class="o">=</span> <span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">i</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">term</span> <span class="o">=</span> <span class="o">-</span><span class="n">term</span>
</span></span><span class="line"><span class="cl"> <span class="n">new</span> <span class="o">=</span> <span class="n">approx</span> <span class="o">+</span> <span class="mf">4.0</span><span class="o">/</span><span class="n">term</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">abs</span><span class="p">(</span><span class="n">new</span> <span class="o">-</span> <span class="n">approx</span><span class="p">)</span> <span class="o"><</span> <span class="n">accuracy</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">approx</span> <span class="o">=</span> <span class="n">new</span>
</span></span><span class="line"><span class="cl"> <span class="k">break</span>
</span></span><span class="line"><span class="cl"> <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="n">approx</span> <span class="o">=</span> <span class="n">new</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">approx</span>
</span></span></code></pre></div><p>But, if we time this version (not counting startup or compile time), it
takes 0.134 seconds. Doing the math shows that Factor is 5 times faster
than Python in this case. Not bad.</p>
Really Big Numbers
https://re.factorcode.org/2011/09/really-big-numbers.html
Thu, 22 Sep 2011 17:32:00 -0700https://re.factorcode.org/2011/09/really-big-numbers.html<p><a href="https://factorcode.org">Factor</a> supports both <code>fixnum</code> (fixed size
integers, typically 32- or 64-bit values) and <code>bignum</code> (arbitrarily
large integers). Recently, I discovered that Factor did not have support
for calculating the
<a href="https://en.wikipedia.org/wiki/Natural_logarithm">logarithm</a> of <em>really
big numbers</em> (those larger than 2<sup>1024</sup>).</p>
<p>You can define a simple factorial function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">factorial</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">n!</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">1 </span>] [ [1..b] <span class="nb">product </span>] <span class="nb">if-zero </span><span class="k">;
</span></span></span></code></pre></div><p>But if you tried to calculate the logarithm of <code>1000 factorial</code>, it
produces the wrong answer.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1000 </span>factorial log <span class="m">.
</span></span></span><span class="line"><span class="cl">1/0.
</span></span></code></pre></div><p>The reason for this is that Factor attempts to convert a <code>bignum</code> into a
double-precision floating point number and take the logarithm of that.
Unfortunately, the value in this case is too large. What do other
languages do in this case?</p>
<p>We could look at Ruby, but it has the same problem as Factor:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">>></span> <span class="no">Math</span><span class="o">::</span><span class="n">log</span><span class="p">((</span><span class="mi">1</span><span class="o">..</span><span class="mi">1000</span><span class="p">)</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="ss">:*</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="n">irb</span><span class="p">):</span><span class="mi">8</span><span class="p">:</span> <span class="ss">warning</span><span class="p">:</span> <span class="no">Bignum</span> <span class="n">out</span> <span class="n">of</span> <span class="nb">Float</span> <span class="n">range</span>
</span></span><span class="line"><span class="cl"><span class="o">=></span> <span class="no">Infinity</span>
</span></span></code></pre></div><p>However, you can get the <a href="https://www.wolframalpha.com/input/?i=log%28factorial%281000%29%29">right
answer</a>
in Python:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">>>></span> <span class="k">def</span> <span class="nf">factorial</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span> <span class="n">r</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span> <span class="k">while</span> <span class="n">n</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span> <span class="n">r</span> <span class="o">*=</span> <span class="n">n</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span> <span class="n">n</span> <span class="o">-=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span> <span class="k">return</span> <span class="n">r</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="o">>>></span> <span class="n">math</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">factorial</span><span class="p">(</span><span class="mi">1000</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="mf">5912.128178488163</span>
</span></span></code></pre></div><p>If you look under the covers, you will see that Python handles this case
by calling <a href="https://linux.die.net/man/3/frexp">frexp</a> to split a value
into a fraction (<code>x</code>) and a power of two (<code>exp</code>). The original value can
be calculated as <code>x*2exp</code>. Using this, the logarithm can be computed as
<code>log(x) + log(2) * exp</code>.</p>
<p>After discussing this on
<a href="https://concatenative.org/wiki/view/Concatenative%20IRC%20channel">#concatenative</a>,
Joe Groff and I came up with a solution for this. I’m not going to go
over all the details, but if you’re curious, you can look at the
<a href="https://github.com/factor/factor/issues/160">discussion</a>.</p>
<p>First, we implemented a cross-platform version of <code>frexp</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">GENERIC:</span> <span class="nf">frexp</span> <span class="nf">( </span><span class="nv">x</span> <span class="nf">-- </span><span class="nv">y</span> <span class="nv">exp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">float</span> <span class="nf">frexp</span>
</span></span><span class="line"><span class="cl"> <span class="nb">dup fp-special? </span>[ <span class="nb">dup zero? </span>] <span class="nb">unless* </span>[ <span class="m">0 </span>] [
</span></span><span class="line"><span class="cl"> <span class="nb">double>bits
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="m">0x800f,ffff,ffff,ffff </span><span class="nb">bitand
</span></span></span><span class="line"><span class="cl"> <span class="m">0.5 </span><span class="nb">double>bits bitor bits>double
</span></span></span><span class="line"><span class="cl"> ] [ <span class="m">-52 </span><span class="nb">shift </span><span class="m">0x7ff </span><span class="nb">bitand </span><span class="m">1022 </span><span class="nb">- </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">integer</span> <span class="nf">frexp</span>
</span></span><span class="line"><span class="cl"> [ <span class="m">0.0 0 </span>] [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb">> </span>[ <span class="m">1 </span>] [ <span class="nb">abs </span><span class="m">-1 </span>] <span class="nb">if swap dup log2 </span>[
</span></span><span class="line"><span class="cl"> <span class="m">52 </span><span class="nb">swap - shift </span><span class="m">0x000f,ffff,ffff,ffff </span><span class="nb">bitand
</span></span></span><span class="line"><span class="cl"> <span class="m">0.5 </span><span class="nb">double>bits bitor bits>double
</span></span></span><span class="line"><span class="cl"> ] [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">bi </span>[ <span class="nb">* </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-zero </span><span class="k">; inline
</span></span></span></code></pre></div><p>Next, we added support for <code>log</code> and <code>log10</code> of <code>bignum</code>. If the number
can be represented as a float, we continue to process it as before, but
if it is larger, we calculate it similar to Python (with some caching of
the <code>log(2)</code> and <code>log10(2)</code> values for performance):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">most-negative-finite-float</span> <span class="nf">( -- </span><span class="nv">x</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> -0x1.ffff,ffff,ffff,fp1023 <span class="nb">>integer </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">most-positive-finite-float</span> <span class="nf">( -- </span><span class="nv">x</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> 0x1.ffff,ffff,ffff,fp1023 <span class="nb">>integer </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">log-2</span> 0x1.62e42fefa39efp-1
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">log10-2</span> 0x1.34413509f79ffp-2
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(representable-as-float?)</span> <span class="nf">( </span><span class="nv">x</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> most-negative-finite-float
</span></span><span class="line"><span class="cl"> most-positive-finite-float between? <span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(bignum-log)</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">log-quot:</span> <span class="nf">( </span><span class="nv">x</span> <span class="nf">-- </span><span class="nv">y</span> <span class="nf">) </span><span class="nv">log-2</span> <span class="nf">-- </span><span class="nv">log</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>] <span class="nb">dip </span>'[
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>(representable-as-float?)
</span></span><span class="line"><span class="cl"> [ <span class="nb">>float </span>@ ] [ frexp [ @ ] [ _ <span class="nb">* </span>] <span class="nb">bi* + </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">call </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">bignum</span> <span class="nf">log</span> [ log ] log-2 (bignum-log) <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">bignum</span> <span class="nf">log10</span> [ log10 ] log10-2 (bignum-log) <span class="k">;
</span></span></span></code></pre></div><p>And now, in the listener you can get the answer!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1000 </span>factorial log <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">5912.128178488163
</span></span></span></code></pre></div><p>This change is now in the Factor
<a href="https://github.com/factor/factor">repository</a> (if you’d like to
update), and will be in the next release.</p>
Enigma Machines
https://re.factorcode.org/2011/09/enigma-machines.html
Tue, 20 Sep 2011 16:13:00 -0700https://re.factorcode.org/2011/09/enigma-machines.html<p>I noticed a fun blog post about <a href="https://www.stealthcopter.com/blog/2011/05/recreating-the-enigma-in-python/">recreating the Enigma
machine</a>
in Python. For those who need a refresher, an <a href="https://en.wikipedia.org/wiki/Enigma_machine">Enigma
machine</a> was an encryption
device used before and during World War II. I thought it would be fun to
implement this in <a href="https://factorcode.org">Factor</a>.</p>
<h3 id="build-it">Build It</h3>
<p>Our alphabet will be the 26 letters in the <a href="https://en.wikipedia.org/wiki/English_alphabet">English
alphabet</a>, indexed from 0
to 25 (“a” to “z”):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><alphabet></span> <span class="nf">( -- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">26 </span><iota> <span class="nb">>array </span><span class="k">;
</span></span></span></code></pre></div><p>The Enigma machine is made up of a number of “cogs”, which contain an
encoding. We will create a cog using a randomized alphabet:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><cog></span> <span class="nf">( -- </span><span class="nv">cog</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <alphabet> randomize <span class="k">;
</span></span></span></code></pre></div><p>We need to create a utility function to remove a random element from a
sequence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">remove-random</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">elt</span> <span class="nv">seq'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>random ] <span class="nb">keep </span>[ <span class="nb">nth </span>] [ <span class="nb">remove-nth </span>] <span class="nb">2bi </span><span class="k">;
</span></span></span></code></pre></div><p>A special rotor called a “reflector” connected pairs of outputs,
ensuring that encryption and decryption were the same process. It also
gave the Enigma machine the property that no letter encrypted to itself
(a flaw which was used to help break the code). We can create a
reflector by taking the alphabet, and randomly exchanging pairs of
elements:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><reflector></span> <span class="nf">( -- </span><span class="nv">reflector</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <alphabet> <span class="nb">dup length </span><iota> [ <span class="nb">dup empty? </span>] [
</span></span><span class="line"><span class="cl"> remove-random remove-random [ <span class="nb">pick exchange </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">until drop </span><span class="k">;
</span></span></span></code></pre></div><p>We can now create an Enigma machine with a number of cogs and a
reflector:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">enigma</span> <span class="nv">cogs</span> <span class="nv">prev-cogs</span> <span class="nv">reflector</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><enigma></span> <span class="nf">( </span><span class="nv">num-cogs</span> <span class="nf">-- </span><span class="nv">enigma</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <cog> ] <span class="nb">replicate dup clone </span><reflector> enigma <span class="nb">boa </span><span class="k">;
</span></span></span></code></pre></div><p>We need a way to check for special characters (those not in the 26
letter alphabet):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">special?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">25 </span><span class="nb">> </span>] [ <span class="m">0 </span><span class="nb">< </span>] <span class="nb">bi or </span><span class="k">;
</span></span></span></code></pre></div><p>We need a utility function to change several elements of a sequence at
the same time:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">change-nths</span> <span class="nf">( </span><span class="nv">indices</span> <span class="nv">seq</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">elt</span> <span class="nf">-- </span><span class="nv">elt'</span> <span class="nf">) -- )
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">change-nth </span>] <span class="nb">2curry each </span><span class="k">; inline
</span></span></span></code></pre></div><p>Encoding a piece of text is where all the work is performed. The main
strategy is to apply each character (that isn’t special) through the
cogs and then the reflector to find the encoded character, and then
cycle the cogs to prepare for the next character.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">encode</span> <span class="nf">( </span><span class="nv">text</span> <span class="nv">enigma</span> <span class="nf">-- </span><span class="nv">cipher-text</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>:> ln!
</span></span><span class="line"><span class="cl"> enigma cogs>> :> cogs
</span></span><span class="line"><span class="cl"> enigma reflector>> :> reflector
</span></span><span class="line"><span class="cl"> text >lower [
</span></span><span class="line"><span class="cl"> <span class="sc">CHAR: a </span><span class="nb">mod dup </span>special? [
</span></span><span class="line"><span class="cl"> ln <span class="m">1 </span><span class="nb">+ </span>ln!
</span></span><span class="line"><span class="cl"> cogs [ <span class="nb">nth </span>] <span class="nb">each </span>reflector <span class="nb">nth
</span></span></span><span class="line"><span class="cl"> cogs <span class="nb">reverse </span>[ <span class="nb">index </span>] <span class="nb">each </span><span class="sc">CHAR: a </span><span class="nb">+
</span></span></span><span class="line"><span class="cl"> cogs <span class="nb">length </span><iota> [ <span class="m">6 </span><span class="nb">* </span><span class="m">1 </span><span class="nb">+ </span>ln <span class="nb">mod zero? </span>] <span class="nb">filter
</span></span></span><span class="line"><span class="cl"> cogs [ <span class="nb">unclip prefix </span>] change-nths
</span></span><span class="line"><span class="cl"> ] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><blockquote>
<p><em>Note: this converts the text to lowercase before encoding. The
<code>encode</code> word could be expanded to support uppercase, but its much
simpler this way.</em></p>
</blockquote>
<p>If we make a word to reset the cogs back to the original configuration,
we can verify that the encrypted text can be decrypted back to the
original.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">reset-cogs</span> <span class="nf">( </span><span class="nv">enigma</span> <span class="nf">-- </span><span class="nv">enigma</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>prev-cogs>> >>cogs <span class="k">;
</span></span></span></code></pre></div><h3 id="try-it">Try It</h3>
<p>We can experiment with this in the Listener, creating a 4-cog Enigma
machine and then encoding, resetting, and encoding again:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">4 </span><enigma>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"hello, world"</span> <span class="nb">over </span>encode [ <span class="nb">print </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl">luhhn, xnzha
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">over </span>reset-cogs encode <span class="nb">print
</span></span></span><span class="line"><span class="cl">hello, world
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/enigma/enigma.factor">GitHub</a>.</p>
Most Pressed Keys
https://re.factorcode.org/2011/09/most-pressed-keys.html
Sun, 18 Sep 2011 08:53:00 -0700https://re.factorcode.org/2011/09/most-pressed-keys.html<p>Mahdi Yusuf made a fun <a href="https://www.mahdiyusuf.com/post/9947002105/most-pressed-keys-and-programming-syntaxes">blog
post</a>
about which keys get pressed the most in various programming languages.
I thought I would generate one for <a href="https://factorcode.org">Factor</a>:</p>
<p>
<img src="https://re.factorcode.org/images/2011-09-18-most-pressed-keys-factor-heatmap-small.png" alt="" width="512" height="239" />
</p>
<p>This was assembled by taking all the source code (not documentation or
tests) from <code>core/</code> and <code>basis/</code> in the Factor
<a href="https://github.com/factor/factor">codebase</a> and passing it through
<a href="https://www.patrick-wied.at/projects/heatmap-keyboard/">heatmap.js</a>.</p>
Manipulating Files
https://re.factorcode.org/2011/09/manipulating-files.html
Sun, 11 Sep 2011 08:51:00 -0700https://re.factorcode.org/2011/09/manipulating-files.html<p>Java has had historically frustrating API’s for interacting with files.
In Java 7, these API’s were cleaned up a little bit. A blog post
<a href="https://java.dzone.com/articles/manipulating-files-java-7">demonstrates</a>
some examples of using these new API’s. I would say, though, that
<a href="https://factorcode.org">Factor</a> demonstrates a much simpler
cross-platform API:</p>
<h3 id="creating-and-deleting-files">Creating and Deleting Files</h3>
<p>To create a file, simply:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/path/to/file"</span> touch-file
</span></span></code></pre></div><p>To update file permissions (on unix systems), you can use the
<a href="https://docs.factorcode.org/content/article-unix-file-permissions.html">io.files.info.unix</a>
vocabulary:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/path/to/file"</span> OCT: <span class="m">666 </span>set-file-permissions
</span></span></code></pre></div><blockquote>
<p><em>Note: it would be nice to support parsing “rw-rw-rw-” and “g+x” and
similar permission strings, and probably not very difficult.</em></p>
</blockquote>
<p>To delete a file, simply:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/path/to/file"</span> delete-file
</span></span></code></pre></div><h3 id="copying-and-moving-files">Copying and Moving Files</h3>
<p>To copy a file from one path to another:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/path/from"</span> <span class="s">"/path/to"</span> copy-file
</span></span></code></pre></div><p>To copy a file, and preserve its file permissions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/path/from"</span> <span class="s">"/path/to"</span> copy-file-and-info
</span></span></code></pre></div><p>To copy a file into a directory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/dir1/file"</span> <span class="s">"/dir2"</span> copy-file-into
</span></span></code></pre></div><p>To move a file from one path to another:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/path/from"</span> <span class="s">"/path/to"</span> move-file
</span></span></code></pre></div><p>To move a file into a directory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/dir1/file"</span> <span class="s">"/dir2"</span> move-file-into
</span></span></code></pre></div>echo
https://re.factorcode.org/2011/09/echo.html
Tue, 06 Sep 2011 19:14:00 -0700https://re.factorcode.org/2011/09/echo.html<p>One of the most basic unix utilities is
<a href="https://manpages.ubuntu.com/manpages/lucid/man1/echo.1.html">echo</a>,
which is used to “display a line of text”. Below, I show how to
implement this in <a href="https://factorcode.org">Factor</a>.</p>
<p>The usage for <code>echo</code> is usually written like this:</p>
<pre tabindex="0"><code>echo [-n] [string ...]
</code></pre><p>The <code>-n</code> option is to “<em>not print the trailing newline character</em>”. We
can make a word that checks the first argument for <code>-n</code>, and returns a
boolean and the remaining arguments for formatting:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">-n?</span> <span class="nf">( </span><span class="nv">args</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nv">args'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">first </span><span class="s">"-n"</span> <span class="nb">= </span>] <span class="nb">keep over </span>[ <span class="nb">rest </span>] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><p>Since the string arguments are separated by a single blank space, we can
<a href="https://docs.factorcode.org/content/word-join,sequences.html">join</a> and
<a href="https://docs.factorcode.org/content/word-write,io.html">write</a> them,
optionally printing a trailing newline.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">echo-args</span> <span class="nf">( </span><span class="nv">args</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> -n? <span class="s">" "</span> <span class="nb">join write </span>[ <span class="nb">nl </span>] <span class="nb">unless </span><span class="k">;
</span></span></span></code></pre></div><p>We can define a “main” word that allows our vocabulary to be directly
<a href="https://docs.factorcode.org/content/word-run%2Cvocabs.loader.html">run</a>
or
<a href="https://docs.factorcode.org/content/article-tools.deploy.usage.html">deployed</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">run-echo</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> command-line <span class="nb">get </span>[ <span class="nb">nl </span>] [ echo-args ] <span class="nb">if-empty </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">run-echo</span>
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/unix-tools/echo/echo.factor">GitHub</a>.</p>
Fun with WAV
https://re.factorcode.org/2011/09/fun-with-wav.html
Sat, 03 Sep 2011 11:25:00 -0700https://re.factorcode.org/2011/09/fun-with-wav.html<p>Last year, a “<a href="https://yannesposito.com/Scratch/en/blog/2010-10-14-Fun-with-wav/">Fun with
wav</a>”
post got a lot of visibility. The author was trying to extract the
header from a audio file in the <a href="https://en.wikipedia.org/wiki/WAV">WAV
format</a> and output the sum of the
remaining data in the file. He gives the disclaimer several times that
this was a specific hack and not a generalized solution.</p>
<p>Although the original solution was in C, he
<a href="https://www.reddit.com/r/programming/comments/dr5pv/when_c_is_easier_cleaner_funnier_than_ruby/">received</a>
other possible solutions on Reddit. The “winner” by <a href="https://codegolf.com/">code
golf</a> rules is probably
<a href="https://ruby-lang.org/">Ruby</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">data</span> <span class="o">=</span> <span class="no">ARGF</span><span class="o">.</span><span class="n">read</span>
</span></span><span class="line"><span class="cl"><span class="n">keys</span> <span class="o">=</span> <span class="sx">%w[id totallength wavefmt format
</span></span></span><span class="line"><span class="cl"><span class="sx"> pcm channels frequency bytes_per_second
</span></span></span><span class="line"><span class="cl"><span class="sx"> bytes_by_capture bits_per_sample
</span></span></span><span class="line"><span class="cl"><span class="sx"> data bytes_in_data sum
</span></span></span><span class="line"><span class="cl"><span class="sx">]</span>
</span></span><span class="line"><span class="cl"><span class="n">values</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="n">unpack</span> <span class="s1">'Z4 i Z8 i s s i i s s Z4 i s*'</span>
</span></span><span class="line"><span class="cl"><span class="n">sum</span> <span class="o">=</span> <span class="n">values</span><span class="o">.</span><span class="n">drop</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:abs</span><span class="p">)</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="ss">:+</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">keys</span><span class="o">.</span><span class="n">zip</span><span class="p">(</span><span class="n">values</span><span class="o">.</span><span class="n">take</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span> <span class="o"><<</span> <span class="n">sum</span><span class="p">)</span> <span class="p">{</span><span class="o">|</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="nb">puts</span> <span class="s2">"</span><span class="si">#{</span><span class="n">k</span><span class="o">.</span><span class="n">ljust</span> <span class="mi">17</span><span class="si">}</span><span class="s2">: </span><span class="si">#{</span><span class="n">v</span><span class="si">}</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="build-it">Build It</h3>
<p>While not attempting to “golf”, I wanted to show how this might be
implemented in <a href="https://factorcode.org">Factor</a>. First, some imports:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">alien.c-types</span> <span class="nn">classes.struct</span> <span class="nn">kernel</span> <span class="nn">io</span>
</span></span><span class="line"><span class="cl"><span class="nn">io.encodings.binary</span> <span class="nn">io.files</span> <span class="nn">math</span> <span class="nn">specialized-arrays</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FROM:</span> <span class="nn">sequences</span> => <span class="nf">map-sum</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">wavsum</span>
</span></span></code></pre></div><p>Each WAV file begins with a “master RIFF chunk” followed by format
information and the sampled data. We could read each field specifically,
or we can capture this header information directly into a packed
structure (I <a href="https://github.com/mrjbq7/re-factor/blob/4a92988078e0f96e372e0915a3bbbd0682c97c71/classes/struct/packed/packed.factor">added
support</a>
for these in January and recently merged it into the main Factor
repository).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">PACKED-STRUCT: header
</span></span><span class="line"><span class="cl"> { id char[4] }
</span></span><span class="line"><span class="cl"> { totallength int }
</span></span><span class="line"><span class="cl"> { wavefmt char[8] }
</span></span><span class="line"><span class="cl"> { format int }
</span></span><span class="line"><span class="cl"> { pcm <span class="nb">short </span>}
</span></span><span class="line"><span class="cl"> { channels <span class="nb">short </span>}
</span></span><span class="line"><span class="cl"> { frequency int }
</span></span><span class="line"><span class="cl"> { bytes_per_second int }
</span></span><span class="line"><span class="cl"> { bytes_by_capture <span class="nb">short </span>}
</span></span><span class="line"><span class="cl"> { bits_per_sample <span class="nb">short </span>}
</span></span><span class="line"><span class="cl"> { data char[4] }
</span></span><span class="line"><span class="cl"> { bytes_in_data int } <span class="k">;
</span></span></span></code></pre></div><p>We can easily read from an input stream directly into this structure:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-header</span> <span class="nf">( -- </span><span class="nv">header</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> header [ heap-size <span class="nb">read </span>] [ memory>struct ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>The original solution then produced a sum of the remaining file, treated
as a sequence of shorts (16-bit integers).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">SPECIALIZED-ARRAY: <span class="nb">short
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">sum-contents</span> <span class="nf">( -- </span><span class="nv">sum</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">contents </span>short-array-cast [ <span class="nb">abs </span>] <span class="nb">map-sum </span><span class="k">;
</span></span></span></code></pre></div><p>Producing a “wavsum” from a file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wavsum</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">header</span> <span class="nv">sum</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> binary [ read-header sum-contents ] with-file-reader <span class="k">;
</span></span></span></code></pre></div><h3 id="try-it">Try It</h3>
<p>We can try it on a sample wav file that I included with the vocabulary
and we get the same output as the Ruby and C versions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"vocab:wavsum/truck.wav"</span> wavsum [ <span class="m">. </span>] <span class="nb">bi@
</span></span></span><span class="line"><span class="cl">S{ header
</span></span><span class="line"><span class="cl"> { id char-array{ <span class="m">82 73 70 70 </span>} }
</span></span><span class="line"><span class="cl"> { totallength <span class="m">66888 </span>}
</span></span><span class="line"><span class="cl"> { wavefmt char-array{ <span class="m">87 65 86 69 102 109 116 32 </span>} }
</span></span><span class="line"><span class="cl"> { format <span class="m">50 </span>}
</span></span><span class="line"><span class="cl"> { pcm <span class="m">2 </span>}
</span></span><span class="line"><span class="cl"> { channels <span class="m">1 </span>}
</span></span><span class="line"><span class="cl"> { frequency <span class="m">22050 </span>}
</span></span><span class="line"><span class="cl"> { bytes_per_second <span class="m">10752 </span>}
</span></span><span class="line"><span class="cl"> { bytes_by_capture <span class="m">512 </span>}
</span></span><span class="line"><span class="cl"> { bits_per_sample <span class="m">4 </span>}
</span></span><span class="line"><span class="cl"> { data char-array{ <span class="m">32 0 -12 3 </span>} }
</span></span><span class="line"><span class="cl"> { bytes_in_data <span class="m">16777223 </span>}
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl"><span class="m">392717699
</span></span></span></code></pre></div><p>It might be useful to add some validation to this example (much like the
original C version) for such things as endianness and the 16-bit WAV
format. Alternatively, we could improve it to be more general to handle
8-bit or 24-bit encodings, as well as other header formats (not just the
“extended WAV” format).</p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/wavsum/wavsum.factor">GitHub</a>.</p>
Thesaurus
https://re.factorcode.org/2011/08/thesaurus.html
Sun, 28 Aug 2011 10:22:00 -0700https://re.factorcode.org/2011/08/thesaurus.html<p>Steve Hanov blogged about building a
<a href="https://stevehanov.ca/blog/index.php?id=123">thesaurus</a> using a “zero
load time” file formats. Below, we translate his implementation into
<a href="https://factorcode.org">Factor</a>.</p>
<p>You can <a href="https://www.hanovsolutions.com/thesaurus.dat">download</a> the 11
MB thesaurus data file we will be using (containing over 100,000 words
and their lists of related words). It is implemented as a single file
with a custom binary file format that looks like this:</p>
<pre tabindex="0"><code>[ header ]
4 bytes: number of words
[ index section ]
# The words are listed in alphabetical order, so you
# can look one up using binary search.
for each word:
4 byte pointer to word record
[ word section ]
for each word:
null terminated text
4 bytes: number of related words
for each link:
pointer to linked word record
</code></pre><h3 id="build-it">Build It</h3>
<p>The data file consists of 4 byte “pointers” and null-terminated strings.
We can build words to read an integer or a string from a particular
location in the file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-int</span> <span class="nf">( </span><span class="nv">ptr</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">seek-absolute seek-input </span><span class="m">4 </span><span class="nb">read </span>le> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-string</span> <span class="nf">( </span><span class="nv">ptr</span> <span class="nf">-- </span><span class="nv">string</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">seek-absolute seek-input </span><span class="s">"\0"</span> <span class="nb">read-until drop >string </span><span class="k">;
</span></span></span></code></pre></div><p>The number of words in the thesaurus is at the beginning of the file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">#words</span> <span class="nf">( -- </span><span class="nv">n</span> <span class="nf">) </span><span class="m">0 </span>read-int <span class="k">;
</span></span></span></code></pre></div><p>The position of each word is found by reading the “nth” index:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">word-position</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">ptr</span> <span class="nf">) </span><span class="m">4 </span><span class="nb">* </span><span class="m">4 </span><span class="nb">+ </span>read-int <span class="k">;
</span></span></span></code></pre></div><p>The “nth” word is the string found at the specified word position:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">nth-word</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">word</span> <span class="nf">) </span>word-position read-string <span class="k">;
</span></span></span></code></pre></div><p>Now for the fun part. Knowing that the index is sorted, we can build a
word that performs a binary search for a particular word using the
index.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">find-word</span> <span class="nf">( </span><span class="nv">word</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> #words :> high! <span class="m">-1 </span>:> low! <span class="no">f </span>:> candidate!
</span></span><span class="line"><span class="cl"> [ high low <span class="nb">- </span><span class="m">1 </span><span class="nb">> </span>] [
</span></span><span class="line"><span class="cl"> high low <span class="nb">+ </span><span class="m">2 </span><span class="nb">/i </span>:> probe
</span></span><span class="line"><span class="cl"> probe nth-word candidate!
</span></span><span class="line"><span class="cl"> candidate word <=> {
</span></span><span class="line"><span class="cl"> { +eq+ [ probe high! probe low! ] }
</span></span><span class="line"><span class="cl"> { +lt+ [ probe low! ] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>probe high! ]
</span></span><span class="line"><span class="cl"> } <span class="nb">case
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while </span>candidate word <span class="nb">= </span>[ high ] [ <span class="no">f </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Once we found the word that we are looking for, we can read its related
words.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">find-related</span> <span class="nf">( </span><span class="nv">word</span> <span class="nf">-- </span><span class="nv">words</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> word find-word [
</span></span><span class="line"><span class="cl"> word-position word <span class="nb">length + </span><span class="m">1 </span><span class="nb">+ </span>:> ptr
</span></span><span class="line"><span class="cl"> ptr read-int :> #related
</span></span><span class="line"><span class="cl"> ptr #related [1..b] <span class="m">4 </span>v*n n+v
</span></span><span class="line"><span class="cl"> [ read-int read-string ] <span class="nb">map
</span></span></span><span class="line"><span class="cl"> ] [ { } ] <span class="nb">if* </span><span class="k">;
</span></span></span></code></pre></div><p>Putting this all together, we can construct a <a href="https://docs.factorcode.org/content/word-__lt__file-reader__gt__%2Cio.files.html">file
reader</a>
from the thesaurus file, a convenience word to run a quotation with the
thesaurus as its input stream, and our “related words” function.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><thesaurus-reader></span> <span class="nf">( -- </span><span class="nv">reader</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"vocab:thesaurus/thesaurus.dat"</span> binary <file-reader> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">with-thesaurus</span> <span class="nf">( </span><span class="nv">quot</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <thesaurus-reader> ] <span class="nb">dip with-input-stream </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">related-words</span> <span class="nf">( </span><span class="nv">word</span> <span class="nf">-- </span><span class="nv">words</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ find-related ] with-thesaurus <span class="k">;
</span></span></span></code></pre></div><h3 id="try-it">Try It</h3>
<p>If it is all working properly, you should be able to lookup the words
that are related to any word that is in our thesaurus file.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"food"</span> related-words <span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> <span class="s">"aliment"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"bread"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"chow"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"comestibles"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"commons"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"eatables"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"eats"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"edibles"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"feed"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"foodstuff"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"foodstuffs"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"grub"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"meat"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"nourishment"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"nurture"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"nutriment"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"pabulum"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"pap"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"provender"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"provisions"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"rations"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"scoff"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"subsistence"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"sustenance"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"tuck"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"viands"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"victuals"</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>As for performance, it takes just over one millisecond on my laptop to
lookup a single word. Not too shabby! The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/thesaurus/thesaurus.factor">GitHub</a>.</p>
Successor
https://re.factorcode.org/2011/08/successor.html
Wed, 24 Aug 2011 12:38:00 -0700https://re.factorcode.org/2011/08/successor.html<p>A few days ago, I wrote about <a href="https://re.factorcode.org/2011/08/human-numbers.html">translating the humanize
function</a> from
the <a href="https://devongovett.github.com/slang/">slang.js</a> “string utility”
library into <a href="https://factorcode.org">Factor</a>. While looking through the
other functions defined in that library, I came across the
<a href="https://devongovett.github.com/slang/#section-30">successor</a> function.</p>
<p>The “successor” of a string is defined to be a kind of “alphanum
increment”. It’s easiest to show a few examples of how it works:</p>
<pre tabindex="0"><code>successor("a") == "b"
successor("1") == "2"
successor("abcd") == "abce"
successor("THX1138") == "THX1139"
successor("<<koala>>") == "<<koalb>>"
successor("1999zzz") == "2000aaa"
successor("ZZZ9999") == "AAAA0000"
</code></pre><p>We are going to implement this in Factor, using the <a href="https://devongovett.github.com/slang/#section-30">slang.js
documentation</a> as a
guide:</p>
<blockquote>
<p>“Returns the successor to str. The successor is calculated by
incrementing characters starting from the rightmost alphanumeric (or
the rightmost character if there are no alphanumerics) in the string.
Incrementing a digit always results in another digit, and incrementing
a letter results in another letter of the same case.</p>
<p>If the increment generates a carry, the character to the left of it is
incremented. This process repeats until there is no carry, adding an
additional character if necessary.”</p>
</blockquote>
<p>To start, we should handle the “carry” logic. There are two kinds of
carries: digits and letters. Both involve checking if a character has
exceeded a range (resetting it to the beginning of the range if it has).
We can build a word that does this, returning the new character value as
well as a boolean flag indicating if a carry occurred:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">carry</span> <span class="nf">( </span><span class="nv">elt</span> <span class="nv">last</span> <span class="nv">first</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nv">elt'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ _ <span class="nb">> dup </span>_ ] <span class="nb">keep ? </span><span class="k">;
</span></span></span></code></pre></div><p>Using this to carry digits is pretty easy (using the <code>0</code> to <code>9</code> range):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-digit</span> <span class="nf">( </span><span class="nv">ch</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nv">ch'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="nb">+ </span><span class="sc">CHAR: 9 CHAR: 0 </span>carry <span class="k">;
</span></span></span></code></pre></div><p>To carry letters, we need to make sure that the carry preserves the
original case (uppercase or lowercase) of the letter:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-letter</span> <span class="nf">( </span><span class="nv">ch</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nv">ch'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ ch>lower <span class="m">1 </span><span class="nb">+ </span><span class="sc">CHAR: z CHAR: a </span>carry ] [ LETTER? ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> [ ch>upper ] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><p>And, finally, to generalize this to all characters, we check if it is a
digit or a letter and dispatch to the proper function, or pass the
character through if it is neither:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-char</span> <span class="nf">( </span><span class="nv">ch</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nv">ch'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>digit? ] [ next-digit ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>Letter? ] [ next-letter ] }
</span></span><span class="line"><span class="cl"> [ <span class="no">t </span><span class="nb">swap </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="k">;
</span></span></span></code></pre></div><p>This leaves the core of the algorithm, which starts at the end of the
string, incrementing each character (continuing if the carry flag is
true), and then handling the case where we need to carry the first
character:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(successor)</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup length </span><span class="no">t </span>[ <span class="nb">over </span><span class="m">0 </span><span class="nb">> dupd and </span>] [
</span></span><span class="line"><span class="cl"> <span class="nb">drop </span><span class="m">1 </span><span class="nb">- dup pick </span>[ next-char ] <span class="nb">change-nth
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while nip </span>[ <span class="nb">dup first prefix </span>] <span class="nb">when </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">successor</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup empty? </span>[ (successor) ] <span class="nb">unless </span><span class="k">;
</span></span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/successor/successor.factor">GitHub</a>,
and compares favorably at 30 lines of code versus the original 50. The
<code>(successor)</code> function uses a fair amount of stack shuffling, can you
improve it?</p>
Unique
https://re.factorcode.org/2011/08/unique.html
Sat, 20 Aug 2011 21:40:00 -0700https://re.factorcode.org/2011/08/unique.html<p>A few days ago, I noticed this example from the
<a href="https://racket-lang.org/">Racket</a> website, for reporting “each unique
line from <code>stdin</code>”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-racket" data-lang="racket"><span class="line"><span class="cl"><span class="c1">;; Report each unique line from stdin</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">let</span> <span class="p">([</span><span class="n">saw</span> <span class="p">(</span><span class="nb">make-hash</span><span class="p">)])</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">for</span> <span class="p">([</span><span class="n">line</span> <span class="p">(</span><span class="nb">in-lines</span><span class="p">)])</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">unless</span> <span class="p">(</span><span class="nb">hash-ref</span> <span class="n">saw</span> <span class="n">line</span> <span class="no">#f</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">displayln</span> <span class="n">line</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">hash-set!</span> <span class="n">saw</span> <span class="n">line</span> <span class="no">#t</span><span class="p">)))</span><span class="err">
</span></span></span></code></pre></div><p>We can implement the same functionality in
<a href="https://factorcode.org">Factor</a>, reading each unique line from an input
stream:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">unique-lines</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">lines </span>members [ <span class="nb">print </span>] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>The <a href="https://docs.factorcode.org/content/word-lines,io.html">lines</a> word
acts on the “current input stream”, so we can use a <a href="https://docs.factorcode.org/content/word-with-file-reader,io.files.html">file
reader</a>
as an input stream to print out all unique lines in a file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">unique-file</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> utf8 [ unique-lines ] with-file-reader <span class="k">;
</span></span></span></code></pre></div><p>If we wanted to make this print and flush each unique line of input as
it is read, we could have used the
<a href="https://docs.factorcode.org/content/word-each-line,io.html">each-line</a>
word to implement it in a line-by-line fashion:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">unique-lines</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> HS{ } <span class="nb">clone </span>'[
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>_ ?adjoin [
</span></span><span class="line"><span class="cl"> <span class="nb">print flush
</span></span></span><span class="line"><span class="cl"> ] [ <span class="nb">drop </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-line </span><span class="k">;
</span></span></span></code></pre></div>Human Numbers
https://re.factorcode.org/2011/08/human-numbers.html
Sun, 14 Aug 2011 19:21:00 -0700https://re.factorcode.org/2011/08/human-numbers.html<p>I noticed a project on GitHub called
<a href="https://devongovett.github.com/slang/">slang.js</a>. It includes a number
of “string utility” functions that might be useful to Javascript
developers. One that struck me as interesting was the
<a href="https://devongovett.github.com/slang/#section-25">humanize</a> function for
turning numbers into “humanized” strings such as “1st, 2nd, 3rd or
4th”.</p>
<blockquote>
<p><em>Note: this function is sometimes called “ordinalize” - for example,
in the
<a href="https://docs.djangoproject.com/en/dev/ref/contrib/humanize/">django.contrib.humanize</a>
module or the
<a href="https://bitbucket.org/ixmatus/inflector/overview">inflector</a> python
project.</em></p>
</blockquote>
<p>We are going to translate this original version into
<a href="https://factorcode.org/">Factor</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">humanize</span><span class="p">(</span><span class="nx">number</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="nx">number</span> <span class="o">%</span> <span class="mi">100</span> <span class="o">>=</span> <span class="mi">11</span> <span class="o">&&</span> <span class="nx">number</span> <span class="o">%</span> <span class="mi">100</span> <span class="o"><=</span> <span class="mi">13</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">number</span> <span class="o">+</span> <span class="s2">"th"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">switch</span><span class="p">(</span><span class="nx">number</span> <span class="o">%</span> <span class="mi">10</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="mi">1</span><span class="o">:</span> <span class="k">return</span> <span class="nx">number</span> <span class="o">+</span> <span class="s2">"st"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="mi">2</span><span class="o">:</span> <span class="k">return</span> <span class="nx">number</span> <span class="o">+</span> <span class="s2">"nd"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="mi">3</span><span class="o">:</span> <span class="k">return</span> <span class="nx">number</span> <span class="o">+</span> <span class="s2">"rd"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">number</span> <span class="o">+</span> <span class="s2">"th"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>If we keep the same structure (although, without the advantage that
early returns can provide), it looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">humanize</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">100 </span><span class="nb">mod </span><span class="m">11 13 </span>between? [ <span class="s">"th"</span> ] [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">10 </span><span class="nb">mod </span>{
</span></span><span class="line"><span class="cl"> { <span class="m">1 </span>[ <span class="s">"st"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">2 </span>[ <span class="s">"nd"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">3 </span>[ <span class="s">"rd"</span> ] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">"th"</span> ]
</span></span><span class="line"><span class="cl"> } <span class="nb">case
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span>[ number>string ] [ <span class="nb">append </span>] <span class="nb">bi* </span><span class="k">;
</span></span></span></code></pre></div><p>And, then build some tests to make sure it works.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="s">"1st"</span> } [ <span class="m">1 </span>humanize ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"2nd"</span> } [ <span class="m">2 </span>humanize ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"3rd"</span> } [ <span class="m">3 </span>humanize ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"4th"</span> } [ <span class="m">4 </span>humanize ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"11th"</span> } [ <span class="m">11 </span>humanize ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"12th"</span> } [ <span class="m">12 </span>humanize ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"13th"</span> } [ <span class="m">13 </span>humanize ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"21st"</span> } [ <span class="m">21 </span>humanize ] unit-test
</span></span></code></pre></div>Printf
https://re.factorcode.org/2011/08/printf.html
Wed, 10 Aug 2011 16:15:00 -0700https://re.factorcode.org/2011/08/printf.html<p>The venerable <a href="https://linux.die.net/man/3/printf">printf</a> function is
available in most languages. Used for “formatted printing”, it allows
you to convert most basic data types to a string. Several years ago, I
contributed an implementation for <a href="https://factorcode.org/">Factor</a> that
currently lives in the
<a href="https://docs.factorcode.org/content/article-formatting.html">formatting</a>
vocabulary. Using it looks a bit like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">12 </span><span class="s">"There are %d monkeys"</span> printf
</span></span><span class="line"><span class="cl">There are <span class="m">12 </span>monkeys
</span></span></code></pre></div><h3 id="implementation">Implementation</h3>
<p>One of the neat things about this version, is that the format string is
parsed and code to format the arguments is generated at compile-time.
Below, I’ve created a simplified version of
<a href="https://docs.factorcode.org/content/word-printf,formatting.html">printf</a>
to show how this works.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">io</span> <span class="nn">io.streams.string</span> <span class="nn">kernel</span> <span class="nn">macros</span> <span class="nn">make</span> <span class="nn">math</span> <span class="nn">math.parser</span>
</span></span><span class="line"><span class="cl"><span class="nn">peg.ebnf</span> <span class="nn">present</span> <span class="nn">quotations</span> <span class="nn">sequences</span> <span class="nn">strings</span> <span class="k">;
</span></span></span></code></pre></div><p>We use the
<a href="https://docs.factorcode.org/content/article-peg.ebnf.html">peg.ebnf</a>
vocabulary to parse the format string into a sequence of quotations
(either strings or format instructions). Each quotation uses the
<a href="https://docs.factorcode.org/content/article-namespaces-make.html">make</a>
vocabulary to add these strings to a sequence (to be written out):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">EBNF: parse-printf
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">fmt-% <span class="nb">= </span><span class="s">"%"</span> => [[ [ <span class="s">"%"</span> ] ]]
</span></span><span class="line"><span class="cl">fmt-c <span class="nb">= </span><span class="s">"c"</span> => [[ [ <span class="nb">1string </span>] ]]
</span></span><span class="line"><span class="cl">fmt-s <span class="nb">= </span><span class="s">"s"</span> => [[ [ present ] ]]
</span></span><span class="line"><span class="cl">fmt-d <span class="nb">= </span><span class="s">"d"</span> => [[ [ <span class="nb">>integer </span>number>string ] ]]
</span></span><span class="line"><span class="cl">fmt-f <span class="nb">= </span><span class="s">"f"</span> => [[ [ <span class="nb">>float </span>number>string ] ]]
</span></span><span class="line"><span class="cl">fmt-x <span class="nb">= </span><span class="s">"x"</span> => [[ [ >hex ] ]]
</span></span><span class="line"><span class="cl">unknown <span class="nb">= </span>(.)* => [[ <span class="nb">>string throw </span>]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">strings <span class="nb">= </span>fmt-c|fmt-s
</span></span><span class="line"><span class="cl">numbers <span class="nb">= </span>fmt-d|fmt-f|fmt-x
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">formats <span class="nb">= </span><span class="s">"%"</span>~ (strings|numbers|fmt-%|unknown)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">plain-text <span class="nb">= </span>(!("%").)+
</span></span><span class="line"><span class="cl"> => [[ <span class="nb">>string </span>1quotation ]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">text <span class="nb">= </span>(formats|plain-text)*
</span></span><span class="line"><span class="cl"> => [[ [ <span class="no">\ ,</span> <span class="nb">suffix </span>] <span class="nb">map </span>]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">;EBNF
</span></span></code></pre></div><p>You can see the EBNF output by trying it in the
<a href="https://docs.factorcode.org/content/article-listener.html">listener</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"There are %d monkeys"</span> parse-printf <span class="m">.
</span></span></span><span class="line"><span class="cl">V{
</span></span><span class="line"><span class="cl"> [ <span class="s">"There are "</span> , ]
</span></span><span class="line"><span class="cl"> [ <span class="nb">>integer </span>number>string , ]
</span></span><span class="line"><span class="cl"> [ <span class="s">" monkeys"</span> , ]
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>The “printf”
<a href="https://docs.factorcode.org/content/article-macros.html">macro</a> takes
the parsed output, reverses it (so the elements to be formatted can be
passed on the stack in their natural order), applies each format
quotation to the elements on the stack, and then writes them back in the
original order.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MACRO:</span> <span class="nf">printf</span> <span class="nf">( </span><span class="nv">format-string</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> parse-printf <span class="nb">reverse </span> [ ] <span class="nb">concat-as </span>[
</span></span><span class="line"><span class="cl"> { } make <span class="nb">reverse </span>[ <span class="nb">write </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">curry </span><span class="k">;
</span></span></span></code></pre></div><p>You can use
<a href="https://docs.factorcode.org/content/word-expand-macros,macros.expander.html">expand-macros</a>
to see the code the macro generates:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"There are %d monkeys"</span> printf ] expand-macros <span class="m">.
</span></span></span><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl"> [ <span class="s">" monkeys"</span> , <span class="nb">>integer </span>number>string , <span class="s">"There are "</span> , ]
</span></span><span class="line"><span class="cl"> { } make <span class="nb">reverse </span>[ <span class="nb">write </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><p>Implementing
<a href="https://docs.factorcode.org/content/word-sprintf,formatting.html">sprintf</a>
is easy using <a href="https://docs.factorcode.org/content/article-io.streams.string.html">string
streams</a>
to capture the output into a string object:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">sprintf</span> <span class="nf">( </span><span class="nv">format-string</span> <span class="nf">-- </span><span class="nv">result</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ printf ] with-string-writer <span class="k">; inline
</span></span></span></code></pre></div><h3 id="tests">Tests</h3>
<p>We can write some unit tests to show that it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="s">""</span> } [ <span class="s">""</span> sprintf ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"asdf"</span> } [ <span class="s">"asdf"</span> sprintf ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"10"</span> } [ <span class="m">10 </span><span class="s">"%d"</span> sprintf ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"-10"</span> } [ <span class="m">-10 </span><span class="s">"%d"</span> sprintf ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"ff"</span> } [ <span class="m">0xff </span><span class="s">"%x"</span> sprintf ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"Hello, World!"</span> } [ <span class="s">"Hello, World!"</span> <span class="s">"%s"</span> sprintf ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"printf test"</span> } [ <span class="s">"printf test"</span> sprintf ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"char a = 'a'"</span> } [ <span class="sc">CHAR: a </span><span class="s">"char %c = 'a'"</span> sprintf ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"0 message(s)"</span> } [ <span class="m">0 </span><span class="s">"message"</span> <span class="s">"%d %s(s)"</span> sprintf ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"10%"</span> } [ <span class="m">10 </span><span class="s">"%d%%"</span> sprintf ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="s">"[monkey]"</span> } [ <span class="s">"monkey"</span> <span class="s">"[%s]"</span> sprintf ] unit-test
</span></span></code></pre></div><p>This implementation doesn’t support various format parameters such as
width, alignment, padding characters, uppercase/lowercase, decimal
digits, or scientific notation. Nor does it support formatting sequences
and assocs, like the <a href="https://docs.factorcode.org/content/word-printf,formatting.html">official
version</a>.
However, adding those features is straightforward once you understand
the basic mechanics.</p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/printf-example/printf-example.factor">GitHub</a>.</p>
FizzBuzz
https://re.factorcode.org/2011/08/fizzbuzz.html
Sat, 06 Aug 2011 08:36:00 -0700https://re.factorcode.org/2011/08/fizzbuzz.html<p>The “new classic” programming test seems to be the FizzBuzz problem. I
think it was first proposed in a <a href="https://www.codinghorror.com/blog/2007/02/why-cant-programmers-program.html">blog
post</a>
from 2007.</p>
<p>Now that it has garnered so much awareness (even videos on YouTube!),
it’s probably not a good interview question anymore. However, now that
implementations have been <a href="https://rosettacode.org/wiki/FizzBuzz">written in many
languages</a>, it can be used to
learn and compare the syntax of new languages.</p>
<h3 id="implementation">Implementation</h3>
<p>We can see how it works in <a href="https://python.org/">Python</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">>>></span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">101</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="ow">not</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">15</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span> <span class="s2">"FizzBuzz"</span>
</span></span><span class="line"><span class="cl"> <span class="k">elif</span> <span class="ow">not</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">3</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span> <span class="s2">"Fizz"</span>
</span></span><span class="line"><span class="cl"> <span class="k">elif</span> <span class="ow">not</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">5</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span> <span class="s2">"Buzz"</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span> <span class="n">i</span>
</span></span></code></pre></div><p>A similar version in <a href="https://clojure.org/">Clojure</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="nv">=></span><span class="p">(</span><span class="kd">defn </span><span class="nv">multiple?</span> <span class="p">[</span><span class="nv">n</span> <span class="nv">div</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">= </span><span class="mi">0</span> <span class="p">(</span><span class="nf">mod</span> <span class="nv">n</span> <span class="nv">div</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">=></span><span class="p">(</span><span class="nb">doseq </span><span class="p">[</span><span class="nv">i</span> <span class="p">(</span><span class="nb">range </span><span class="mi">1</span> <span class="mi">101</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">cond </span><span class="p">(</span><span class="nb">and </span><span class="p">(</span><span class="nf">multiple?</span> <span class="nv">i</span> <span class="mi">3</span><span class="p">)(</span><span class="nf">multiple?</span> <span class="nv">i</span> <span class="mi">5</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">println </span><span class="s">"FizzBuzz"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">multiple?</span> <span class="nv">i</span> <span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">println </span><span class="s">"Fizz"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">multiple?</span> <span class="nv">i</span> <span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">println </span><span class="s">"Buzz"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:else</span> <span class="p">(</span><span class="nb">println </span><span class="nv">i</span><span class="p">)))</span>
</span></span></code></pre></div><p>And, finally, a version in <a href="https://factorcode.org/">Factor</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">100 </span>[1..b] [
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">15 </span>divisor? ] [ <span class="nb">drop </span><span class="s">"FizzBuzz"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">3 </span> divisor? ] [ <span class="nb">drop </span><span class="s">"Fizz"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">5 </span> divisor? ] [ <span class="nb">drop </span><span class="s">"Buzz"</span> ] }
</span></span><span class="line"><span class="cl"> [ present ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond print
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span></code></pre></div><h3 id="improvements">Improvements</h3>
<p>Let’s see if we can improve the <a href="https://factorcode.org/">Factor</a> version
a bit. First, we can “factor out” the FizzBuzz logic into its own
function (showing how it would look if you were to code this directly
into <a href="https://docs.factorcode.org/content/article-listener.html">the
listener</a>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="k">:</span> <span class="nf">fizzbuzz</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">15 </span>divisor? ] [ <span class="nb">drop </span><span class="s">"FizzBuzz"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">3 </span> divisor? ] [ <span class="nb">drop </span><span class="s">"Fizz"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">5 </span> divisor? ] [ <span class="nb">drop </span><span class="s">"Buzz"</span> ] }
</span></span><span class="line"><span class="cl"> [ present ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond print </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">100 </span>[1..b] [ fizzbuzz ] <span class="nb">each
</span></span></span></code></pre></div><p>To avoid all the <code>dup</code> and <code>drop</code> words, we could build a variation of
<a href="https://docs.factorcode.org/content/word-cond,combinators.html">cond</a>
that acts a bit like a
<a href="https://docs.factorcode.org/content/word-case,combinators.html">case</a>.
The “cond-case” word was
<a href="https://www.mail-archive.com/[email protected]/msg05285.html">suggested</a>
on the <a href="https://lists.sourceforge.net/lists/listinfo/factor-talk%20Factor%20mailing%20list">Factor mailing
list</a>,
and this is one variant of it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MACRO:</span> <span class="nf">cond-case</span> <span class="nf">( </span><span class="nv">assoc</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>callable? <span class="nb">not </span>[
</span></span><span class="line"><span class="cl"> [ <span class="nb">first </span>[ <span class="nb">dup </span>] <span class="nb">prepose </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">second </span>[ <span class="nb">drop </span>] <span class="nb">prepose </span>] <span class="nb">bi 2array
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map </span>[ <span class="nb">cond </span>] <span class="nb">curry </span><span class="k">;
</span></span></span></code></pre></div><p>Using <code>cond-case</code>, we can improve the original version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fizzbuzz</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ <span class="m">15 </span>divisor? ] [ <span class="s">"FizzBuzz"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="m">3 </span> divisor? ] [ <span class="s">"Fizz"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="m">5 </span> divisor? ] [ <span class="s">"Buzz"</span> ] }
</span></span><span class="line"><span class="cl"> [ present ]
</span></span><span class="line"><span class="cl"> } cond-case <span class="nb">print </span><span class="k">;
</span></span></span></code></pre></div><p>If we realize that the check for divisible by 15 is the same as checking
for divisible by 3 <em>and</em> divisible by 5, we can implement it slightly
differently, without a <code>cond</code> or <code>case</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fizz</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">str/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">3 </span>divisor? <span class="s">"Fizz"</span> <span class="nb">and </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">buzz</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">str/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">5 </span>divisor? <span class="s">"Buzz"</span> <span class="nb">and </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fizzbuzz</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ fizz ] [ buzz ] <span class="nb">bi </span><span class="s">""</span> <span class="nb">append-as
</span></span></span><span class="line"><span class="cl"> [ present ] [ <span class="nb">nip </span>] <span class="nb">if-empty print </span><span class="k">;
</span></span></span></code></pre></div><p>Is it better? Can you think of any way to make it simpler? Perhaps by
using or inventing some higher-level concepts like we did with
<code>cond-case</code>?</p>
Robohash
https://re.factorcode.org/2011/07/robohash.html
Fri, 29 Jul 2011 14:56:00 -0700https://re.factorcode.org/2011/07/robohash.html<p>A few days ago, I read about <a href="https://robohash.org/">Robohash</a>, a website
for creating unique images (of robots) from any text. The author was
thinking of using it for icons on a forum, but there are probably other
use-cases for this. For example, as a variation of the <a href="https://mattt.github.com/Chroma-Hash/">color password
hash</a> to help users avoid typos in
their passwords or, perhaps, as a visual representation of a
user-submitted “secret code”.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">images.http</span> <span class="nn">kernel</span> <span class="nn">sequences</span> <span class="nn">urls</span> <span class="nn">urls.encoding</span> <span class="k">;
</span></span></span></code></pre></div><p>First, we need to create URLs of the form
<code>https://robohash.org/YOUR_TEXT</code>, as instructed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">robohash-url</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">url</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> url-encode <span class="s">"https://robohash.org/"</span> <span class="nb">prepend </span>>url <span class="k">;
</span></span></span></code></pre></div><p>Next, we would like to support the different “image sets” that Robohash
supports.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(robohash)</span> <span class="nf">( </span><span class="nv">str</span> <span class="nv">type</span> <span class="nf">-- </span><span class="nv">image</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ robohash-url ] [ <span class="s">"set"</span> set-query-param ] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> load-http-image <span class="k">;
</span></span></span></code></pre></div><p>Using this, we can create image loaders for each set (currently three
sets: “set1”, “set2”, and “set3”):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">robohash1</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">image</span> <span class="nf">) </span><span class="s">"set1"</span> (robohash) <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">robohash2</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">image</span> <span class="nf">) </span><span class="s">"set2"</span> (robohash) <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">robohash3</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">image</span> <span class="nf">) </span><span class="s">"set3"</span> (robohash) <span class="k">;
</span></span></span></code></pre></div><p>You can try it out and see that it
<a href="https://robohash.org/hello%20world?set=set1">works</a>:</p>
<p>
<img src="https://re.factorcode.org/images/2011-07-29-robohash-robohash.png" alt="" width="351" height="317" />
</p>
<p>Robohash also supports custom backgrounds, changing image sizes, and
varying image formats (e.g., JPG or BMP). Adding support for that is an
exercise to the reader.</p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/robohash/robohash.factor">GitHub</a>.</p>
Majority Vote
https://re.factorcode.org/2011/07/majority-vote.html
Sat, 23 Jul 2011 13:58:00 -0700https://re.factorcode.org/2011/07/majority-vote.html<p>A <a href="https://www.cs.utexas.edu/~moore/best-ideas/mjrty/index.html">Linear Time Majority Vote
Algorithm</a>
was invented in 1980 by <a href="https://en.wikipedia.org/wiki/Bob_Boyer">Bob
Boyer</a> and <a href="https://en.wikipedia.org/wiki/J_Strother_Moore">J Strother
Moore</a> (inventors of the
popular <a href="https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm">Boyer-Moore string search
algorithm</a>).
Not seeing this available in <a href="https://www.factorcode.org/">Factor</a>, I
thought to contribute one.</p>
<blockquote>
<p><em>Note: this is also called the “Moore’s Voting Algorithm”.</em></p>
</blockquote>
<p>The algorithm simply looks at each element of the sequence:</p>
<p>Keep a candidate element and a counter (initially unknown and zero,
respectively).</p>
<p>As we move across the sequence, look at each element:</p>
<ul>
<li>If the counter is 0: the element is the candidate and the counter is 1.</li>
<li>If the counter is not 0: increment if the element is the candidate, decrement if not.</li>
</ul>
<p>When we are done, the candidate is the majority element, if there is a
majority.</p>
<p>Using this specification, we can implement the algorithm:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">majority</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">elt/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="no">f </span><span class="m">0 </span>] <span class="nb">dip </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">over zero? </span>[ <span class="nb">2nip </span><span class="m">1 </span>] [
</span></span><span class="line"><span class="cl"> <span class="nb">pick = </span>[ <span class="m">1 </span><span class="nb">+ </span>] [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each zero? </span>[ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><p>A few simple tests show that this is working:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="no">f </span>} [ { } majority ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="no">f </span>} [ { <span class="m">1 2 </span>} majority ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">1 </span>} [ { <span class="m">1 1 2 </span>} majority ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="no">f </span>} [ { <span class="m">1 1 2 2 </span>} majority ] unit-test
</span></span><span class="line"><span class="cl">{ <span class="m">2 </span>} [ { <span class="m">1 1 2 2 2 </span>} majority ] unit-test
</span></span></code></pre></div><p>This is perhaps not quite <em>idiomatic</em> Factor, can you improve it?</p>
One-Liners
https://re.factorcode.org/2011/07/one-liners.html
Tue, 19 Jul 2011 14:05:00 -0700https://re.factorcode.org/2011/07/one-liners.html<p>Inspired by a <a href="https://freegeek.in/blog/2011/06/10-clojure-one-liners/">blog
post</a> about
“one-liners” in Clojure, I thought I’d demonstrate a few small pieces of
Factor code doing some similar things:</p>
<p>Using the
<a href="https://docs.factorcode.org/content/word-map,sequences.html">map</a> word,
we can apply a “doubling”
<a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a>
to each element.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">4 8 15 16 23 42 </span>} [ <span class="m">2 </span><span class="nb">* </span>] <span class="nb">map </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">8 16 30 32 46 84 </span>}
</span></span></code></pre></div><p>We can easily calculate the
<a href="https://docs.factorcode.org/content/word-sum,sequences.html">sum</a> of a
sequence (here the numbers 1 through 1000):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1000 </span>[1..b] <span class="nb">sum </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">500500
</span></span></span></code></pre></div><p>We check if
<a href="https://docs.factorcode.org/content/word-any__que__,sequences.html">any</a>
of a list of words are
<a href="https://docs.factorcode.org/content/word-subseq__que__,sequences.html">within</a>
a string:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="s">"factor"</span> <span class="s">"concatenative"</span> <span class="s">"stack-based"</span> }
</span></span><span class="line"><span class="cl"> [ <span class="s">"factor is awesome"</span> <span class="nb">subseq? </span>] <span class="nb">any?
</span></span></span></code></pre></div><p>You can easily read the
<a href="https://docs.factorcode.org/content/word-file-contents,io.files.html">file-contents</a>,
or
<a href="https://docs.factorcode.org/content/word-file-lines,io.files.html">file-lines</a>,
with a specified encoding (e.g., UTF-8):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/path/to/file.txt"</span> utf8 file-contents
</span></span></code></pre></div><p>Sing the four verses to the “Happy Birthday” song:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">4 </span>[1..b] [
</span></span><span class="line"><span class="cl"> <span class="s">"Happy Birthday "</span> <span class="nb">write
</span></span></span><span class="line"><span class="cl"> <span class="m">3 </span><span class="nb">= </span><span class="s">"dear NAME"</span> <span class="s">"to You"</span> <span class="nb">? print
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl">Happy Birthday to You
</span></span><span class="line"><span class="cl">Happy Birthday to You
</span></span><span class="line"><span class="cl">Happy Birthday dear NAME
</span></span><span class="line"><span class="cl">Happy Birthday to You
</span></span></code></pre></div><p>Use
<a href="https://docs.factorcode.org/content/word-filter,sequences.html">filter</a>
(or even
<a href="https://docs.factorcode.org/content/word-partition,sequences.html">partition</a>)
with some selection criteria:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">49 58 76 82 88 90 </span>} [ <span class="m">60 </span><span class="nb">> </span>] <span class="nb">filter </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">76 82 88 90 </span>}
</span></span></code></pre></div><p>Use the
<a href="https://docs.factorcode.org/content/vocab-http.client.html">http.client</a>
vocabulary to access a web service, and then the
<a href="https://docs.factorcode.org/content/vocab-xml.html">xml</a> vocabulary to
parse the result from a string. You could use the
<a href="https://docs.factorcode.org/content/vocab-json.reader.html">json.reader</a>
vocabulary to parse JSON responses.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"https://search.twitter.com/search.atom?q=factor"</span>
</span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>string>xml
</span></span></code></pre></div><p>Check the HTTP headers to find the version of a server used by a web
server:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"https://apple.com"</span> http-get <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> header>> <span class="s">"server"</span> <span class="nb">swap at </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"Apache/2.2.3 (Oracle)"</span>
</span></span></code></pre></div><p>Use
<a href="https://docs.factorcode.org/content/word-infimum,sequences.html">infimum</a>
and
<a href="https://docs.factorcode.org/content/word-supremum,sequences.html">supremum</a>
to find the minimum and maximum, respectively, of a list (alternatively,
you could use my <a href="https://re.factorcode.org/2011/02/maximum-minimum.html">maximum/minimum
functions</a>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">14 36 -7 46 98 </span>} <span class="nb">infimum </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">-7
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">14 36 -7 46 98 </span>} <span class="nb">supremum </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">98
</span></span></span></code></pre></div><p>Parse a string into groups of two characters, then interpret those as
hex values of characters, mapping the output as a string:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"474e552773204e6f7420556e6978"</span>
</span></span><span class="line"><span class="cl"> <span class="m">2 </span><groups> [ <span class="m">16 </span>base> ] <span class="s">""</span> <span class="nb">map-as </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"GNU's Not Unix"</span>
</span></span></code></pre></div><p>Use
<a href="https://docs.factorcode.org/content/vocab-concurrency.combinators.html">concurrency.combinators</a>
to perform certain tasks in parallel with
<a href="https://docs.factorcode.org/content/article-concurrency.futures.html">futures</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1 0 -1 </span>} [ <span class="m">2 </span><span class="nb">+ </span>] parallel-map <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">3 2 1 </span>}
</span></span></code></pre></div>Detecting Plagiarism
https://re.factorcode.org/2011/07/detecting-plagiarism.html
Fri, 15 Jul 2011 09:56:00 -0700https://re.factorcode.org/2011/07/detecting-plagiarism.html<p>About a month ago, Tom Moertel wrote a <a href="https://blog.moertel.com/articles/2011/06/16/writing-a-simple-plagiarism-detector-in-haskell">simple plagiarism
detector</a>
in Haskell. I wanted to replicate his functionality using
<a href="https://www.factorcode.org/">Factor</a>, to contrast the two solutions.</p>
<p>The strategy in this plagiarism detector is fairly simple: calculate
“long enough” <a href="https://en.wikipedia.org/wiki/N-gram">n-grams</a> that should
be fairly unique and see if they are present in a particular piece of
“suspect text”, converting the common pieces into UPPERCASE so that we
can visually see what might be plagiarized.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">command-line</span> <span class="nn">grouping</span> <span class="nn">io</span> <span class="nn">io.encodings.utf8</span> <span class="nn">io.files</span>
</span></span><span class="line"><span class="cl"><span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">math.parser</span> <span class="nn">ranges</span> <span class="nn">namespaces</span> <span class="nn">regexp</span> <span class="nn">sequences</span>
</span></span><span class="line"><span class="cl"><span class="nn">sets</span> <span class="nn">splitting</span> <span class="nn">unicode.case</span> <span class="nn">unicode.categories</span> <span class="nn">unicode.data</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">plagiarism</span>
</span></span></code></pre></div><p>We can split text (using
the <a href="https://docs.factorcode.org/content/vocab-grouping.html">grouping</a> vocabulary) into
consecutive groups of “n” words:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">n-grams</span> <span class="nf">( </span><span class="nv">str</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ blank? ] split-when <span class="nb">harvest </span>] [ <clumps> ] <span class="nb">bi* </span><span class="k">;
</span></span></span></code></pre></div><p>Given a piece of text suspected to be plagiarized and some sources to
compare against, we can compute the common n-grams between the two
pieces of text:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">common-n-grams</span> <span class="nf">( </span><span class="nv">suspect</span> <span class="nv">sources</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">n-grams</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ n-grams ] <span class="nb">curry dup </span>[ <span class="nb">map concat </span>] <span class="nb">curry bi* </span>intersect <span class="k">;
</span></span></span></code></pre></div><p>For each common n-gram found, we use a <a href="https://en.wikipedia.org/wiki/Regular_expression">regular
expression</a> to find the
matching part of the suspect text. The regular expression we will use,
looks something like <em>a space or start of line followed by our n-gram
and ending with a space or end of line</em>, allowing varying whitespace
between words, being case insensitive, and ignoring non-letters within a
word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">n-gram>regexp</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">regexp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ Letter? <span class="nb">not </span>] split-when <span class="s">"[\\W\\S]"</span> <span class="nb">join </span>] <span class="nb">map
</span></span></span><span class="line"><span class="cl"> <span class="s">"\\s+"</span> <span class="nb">join </span><span class="s">"(\\s|^)"</span> <span class="s">"(\\s|$)"</span> <span class="nb">surround
</span></span></span><span class="line"><span class="cl"> <span class="s">"i"</span> <optioned-regexp> <span class="k">;
</span></span></span></code></pre></div><p>The <a href="https://docs.factorcode.org/content/vocab-sequences.html">sequences</a>
vocabulary contains a
<a href="https://docs.factorcode.org/content/word-change-nth,sequences.html">change-nth</a>
word to modify a particular element in a sequence. We can create a word
to modify several elements easily:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">change-nths</span> <span class="nf">( </span><span class="nv">indices</span> <span class="nv">seq</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">elt</span> <span class="nf">-- </span><span class="nv">elt'</span> <span class="nf">) -- )
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">change-nth </span>] <span class="nb">2curry each </span><span class="k">; inline
</span></span></span></code></pre></div><p>Using <code>change-nths</code> and the “n-gram regexp”, we can
<a href="https://docs.factorcode.org/content/word-ch__gt__upper,unicode.data.html">ch>upper</a>
each matching portion of text:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">upper-matches</span> <span class="nf">( </span><span class="nv">str</span> <span class="nv">regexp</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ [ [a..b) ] <span class="nb">dip </span>[ ch>upper ] change-nths ] each-match <span class="k">;
</span></span></span></code></pre></div><p>Using these building blocks, we can build a simple plagiarism
detector:</p>
<ol>
<li>Compute the common n-grams between suspect and source texts</li>
<li>Create a regular expression for each common n-gram</li>
<li>For each match, change the matching characters to uppercase</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">detect-plagiarism</span> <span class="nf">( </span><span class="nv">suspect</span> <span class="nv">sources</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">suspect'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dupd </span>] <span class="nb">dip </span>common-n-grams [
</span></span><span class="line"><span class="cl"> <span class="nb">dupd </span>n-gram>regexp upper-matches
</span></span><span class="line"><span class="cl"> ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>A “main method” enables this program to run from the command line:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">run-plagiarism</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> command-line <span class="nb">get dup length </span><span class="m">3 </span><span class="nb">< </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">drop </span><span class="s">"USAGE: plagiarism N suspect.txt source.txt..."</span> <span class="nb">print
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> [ <span class="nb">rest </span>[ utf8 file-contents ] <span class="nb">map unclip swap </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">first </span>string>number ] <span class="nb">bi </span>detect-plagiarism <span class="nb">print
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">run-plagiarism</span>
</span></span></code></pre></div><p>You can see it work by trying to find common 4-grams on some simple text
(in this case, I added the word “really”):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"this is a really long piece of text"</span>
</span></span><span class="line"><span class="cl"> { <span class="s">"this is a long piece of text"</span> }
</span></span><span class="line"><span class="cl"> <span class="m">4 </span>detect-plagiarism <span class="nb">print
</span></span></span><span class="line"><span class="cl">this is a really LONG PIECE OF TEXT
</span></span></code></pre></div><p>It’s fast enough for small examples, but not that fast for complete
novels, particularly when run on texts with many common n-grams. One
idea for improving the speed might be to examine the <code>common-n-grams</code>
algorithm to return sequences of “n or more”. This way, if the text
contains a common 7-gram, and you are looking at common 4-grams, then it
would have one entry instead of three.</p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/plagiarism/plagiarism.factor">GitHub</a>.</p>
Concatenative Thinking
https://re.factorcode.org/2011/07/concatenative-thinking.html
Tue, 12 Jul 2011 20:57:00 -0700https://re.factorcode.org/2011/07/concatenative-thinking.html<p>I’ve written about the
<a href="https://re.factorcode.org/2011/04/verbosity.html">conciseness</a> of
<a href="https://www.factorcode.org/">Factor</a> before. Yesterday, I noticed a link
to a functional programming tutorial called <a href="https://www.ibm.com/developerworks/library/j-ft1/">“Functional
Thinking”</a> that was
posted two months ago.</p>
<p>The tutorial develops a program for classifying numbers based on the sum
of its factors into <em>perfect</em>, <em>abundant</em>, or <em>deficient</em> numbers. The
program goes through three improvements, ending up at this “best”
solution in Java (using the <a href="https://functionaljava.org/">Functional
Java</a> library):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">FNumberClassifier</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">isFactor</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">number</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">potential_factor</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">number</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="n">potential_factor</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">List</span><span class="o"><</span><span class="n">Integer</span><span class="o">></span><span class="w"> </span><span class="nf">factors</span><span class="p">(</span><span class="kd">final</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">number</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">range</span><span class="p">(</span><span class="n">1</span><span class="p">,</span><span class="w"> </span><span class="n">number</span><span class="o">+</span><span class="n">1</span><span class="p">).</span><span class="na">filter</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">F</span><span class="o"><</span><span class="n">Integer</span><span class="p">,</span><span class="w"> </span><span class="n">Boolean</span><span class="o">></span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">Boolean</span><span class="w"> </span><span class="nf">f</span><span class="p">(</span><span class="kd">final</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="n">i</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">number</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">sum</span><span class="p">(</span><span class="n">List</span><span class="o"><</span><span class="n">Integer</span><span class="o">></span><span class="w"> </span><span class="n">factors</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">factors</span><span class="p">.</span><span class="na">foldLeft</span><span class="p">(</span><span class="n">fj</span><span class="p">.</span><span class="na">function</span><span class="p">.</span><span class="na">Integers</span><span class="p">.</span><span class="na">add</span><span class="p">,</span><span class="w"> </span><span class="n">0</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">isPerfect</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">number</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">sum</span><span class="p">(</span><span class="n">factors</span><span class="p">(</span><span class="n">number</span><span class="p">))</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">number</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">number</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">isAbundant</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">number</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">sum</span><span class="p">(</span><span class="n">factors</span><span class="p">(</span><span class="n">number</span><span class="p">))</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">number</span><span class="w"> </span><span class="o">></span><span class="w"> </span><span class="n">number</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">isDeficient</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">number</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">sum</span><span class="p">(</span><span class="n">factors</span><span class="p">(</span><span class="n">number</span><span class="p">))</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">number</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">number</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>While the proffered solution is much (much) better than the original
“imperative” solution, I thought I would show what it could look like in
Factor:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">factor?</span> <span class="nf">( </span><span class="nv">m</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">mod zero? </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">factors</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[1..b] [ factor? ] <span class="nb">with filter </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">perfect?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ factors <span class="nb">sum </span>] [ <span class="nb">- </span>] [ <span class="nb">= </span>] <span class="nb">tri </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">abundant?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ factors <span class="nb">sum </span>] [ <span class="nb">- </span>] [ <span class="nb">> </span>] <span class="nb">tri </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">deficient?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ factors <span class="nb">sum </span>] [ <span class="nb">- </span>] [ <span class="nb">< </span>] <span class="nb">tri </span><span class="k">;
</span></span></span></code></pre></div><p>What do you think?</p>
Substrings
https://re.factorcode.org/2011/07/substrings.html
Sun, 10 Jul 2011 11:18:00 -0700https://re.factorcode.org/2011/07/substrings.html<p>One year ago, I wrote about some <a href="https://re.factorcode.org/2010/07/new-combinatoric-functions.html">new combinatoric
functions</a>
that I wrote. I had a recent need for a couple of “substring” functions.
Since I couldn’t find them in <a href="https://www.factorcode.org/">Factor’s</a>
standard library, I thought I would contribute these:</p>
<h3 id="all-subseqs">all-subseqs</h3>
<p>Our first word takes a sequence, and then returns all (consecutive)
subsequences that can be found. In Factor, the
<a href="https://docs.factorcode.org/content/vocab-grouping.html">grouping</a>
vocabulary provides a
<a href="https://docs.factorcode.org/content/word-clump%2Cgrouping.html">clumping</a>
feature that splits a sequence into overlapping, fixed-length,
subsequences. We can use this to find clumps of every possible length to
solve this problem:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">grouping</span> <span class="nn">kernel</span> <span class="nn">ranges</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-subseqs</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seqs</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>[1..b] [ <clumps> ] <span class="nb">with map concat </span><span class="k">;
</span></span></span></code></pre></div><p>You can see how this works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"abcd"</span> all-subseqs <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"a"</span> <span class="s">"b"</span> <span class="s">"c"</span> <span class="s">"d"</span> <span class="s">"ab"</span> <span class="s">"bc"</span> <span class="s">"cd"</span> <span class="s">"abc"</span> <span class="s">"bcd"</span> <span class="s">"abcd"</span> }
</span></span></code></pre></div><p><em>Note: we specifically don’t include the “empty” string in the
results.</em></p>
<h3 id="longest-subseq">longest-subseq</h3>
<p>Several <a href="https://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Longest_common_substring">algorithms and
implementations</a>
can be found for the <a href="https://en.wikipedia.org/wiki/Longest_common_substring_problem">longest common substring
problem</a>.
Basically, we want a word that returns the longest (consecutive)
substring that is common between two strings.</p>
<p>Using <a href="https://docs.factorcode.org/content/article-locals.html">locals</a>,
I translated the <a href="https://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Longest_common_substring#Python">Python
solution</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">arrays</span> <span class="nn">kernel</span> <span class="nn">locals</span> <span class="nn">math</span> <span class="nn">ranges</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">longest-subseq</span> <span class="nf">( </span><span class="nv">seq1</span> <span class="nv">seq2</span> <span class="nf">-- </span><span class="nv">subseq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> seq1 <span class="nb">length </span>:> len1
</span></span><span class="line"><span class="cl"> seq2 <span class="nb">length </span>:> len2
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>:> n!
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>:> end!
</span></span><span class="line"><span class="cl"> len1 <span class="m">1 </span><span class="nb">+ </span>[ len2 <span class="m">1 </span><span class="nb">+ </span><span class="m">0 </span><span class="nb"><array> </span>] <span class="nb">replicate </span>:> table
</span></span><span class="line"><span class="cl"> len1 [1..b] [| x |
</span></span><span class="line"><span class="cl"> len2 [1..b] [| y |
</span></span><span class="line"><span class="cl"> x <span class="m">1 </span><span class="nb">- </span>seq1 <span class="nb">nth
</span></span></span><span class="line"><span class="cl"> y <span class="m">1 </span><span class="nb">- </span>seq2 <span class="nb">nth = </span>[
</span></span><span class="line"><span class="cl"> y <span class="m">1 </span><span class="nb">- </span>x <span class="m">1 </span><span class="nb">- </span>table <span class="nb">nth nth </span><span class="m">1 </span><span class="nb">+ </span>:> len
</span></span><span class="line"><span class="cl"> len y x table <span class="nb">nth set-nth
</span></span></span><span class="line"><span class="cl"> len n <span class="nb">> </span>[ len n! x end! ] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> ] [ <span class="m">0 </span>y x table <span class="nb">nth set-nth </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span>end n <span class="nb">- </span>end seq1 <span class="nb">subseq </span><span class="k">;
</span></span></span></code></pre></div><p>Below, you can see how it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"abc"</span> <span class="s">"def"</span> longest-subseq <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">""</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"abcd"</span> <span class="s">"abcde"</span> longest-subseq <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"abcd"</span>
</span></span></code></pre></div><p><em>Note: don’t confuse this with the <a href="https://en.wikipedia.org/wiki/Longest_common_subsequence_problem">longest common subsequence
problem</a>
(see <a href="https://en.wikipedia.org/wiki/Subsequence#Substring_vs._subsequence">substring vs.
subsequence</a>
for more details), which is implemented in Factor by the
<a href="https://docs.factorcode.org/content/vocab-lcs.html">lcs</a> vocabulary.</em></p>
99 Bottles
https://re.factorcode.org/2011/07/99-bottles.html
Thu, 07 Jul 2011 09:04:00 -0700https://re.factorcode.org/2011/07/99-bottles.html<p>The <a href="https://99-bottles-of-beer.net">99 Bottles Of Beer</a> project has an
<a href="https://99-bottles-of-beer.net/language-factor-1249.html">entry</a> for
<a href="https://www.factorcode.org">Factor</a>. Unfortunately, it’s for version
0.83 (latest released version is 0.94) and some minor changes have come
into the language since then. Below, I contribute an updated version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">formatting</span> <span class="nn">io</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">ranges</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">verse</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"%d bottles of beer on the wall, "</span> printf
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"%d bottles of beer.\n"</span> printf
</span></span><span class="line"><span class="cl"> <span class="s">"Take one down and pass it around, "</span> <span class="nb">write
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="nb">- </span><span class="s">"%d bottles of beer on the wall.\n"</span> printf <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">verse-1</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"1 bottle of beer on the wall, "</span> <span class="nb">write
</span></span></span><span class="line"><span class="cl"> <span class="s">"1 bottle of beer."</span> <span class="nb">print
</span></span></span><span class="line"><span class="cl"> <span class="s">"Take one down and pass it around, "</span> <span class="nb">write
</span></span></span><span class="line"><span class="cl"> <span class="s">"no more bottles of beer on the wall."</span> <span class="nb">print </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">verse-0</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"No more bottles of beer on the wall, "</span> <span class="nb">write
</span></span></span><span class="line"><span class="cl"> <span class="s">"no more bottles of beer."</span> <span class="nb">print
</span></span></span><span class="line"><span class="cl"> <span class="s">"Go to the store and buy some more, "</span> <span class="nb">write
</span></span></span><span class="line"><span class="cl"> <span class="s">"99 bottles of beer on the wall."</span> <span class="nb">print </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">99bottles</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="m">99 2 </span>[a..b] [ verse ] <span class="nb">each </span>verse-1 verse-0 <span class="k">;
</span></span></span></code></pre></div><h3 id="shorter">Shorter?</h3>
<p>We can shorten this a bit by taking a few poetic liberties with the
song. While doing that, we can make it flexible to allow any (positive)
number of bottles:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">formatting</span> <span class="nn">io</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">ranges</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">verse</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"%d bottles of beer on the wall, "</span> printf
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">"%d bottles of beer.\n"</span> printf
</span></span><span class="line"><span class="cl"> <span class="s">"Take one down and pass it around, "</span> <span class="nb">write
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="nb">- </span><span class="s">"%d bottles of beer on the wall.\n"</span> printf <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">last-verse</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"Go to the store and buy some more, "</span> <span class="nb">write
</span></span></span><span class="line"><span class="cl"> <span class="s">"no more bottles of beer on the wall!"</span> <span class="nb">print </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bottles</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span>[a..b] [ verse ] <span class="nb">each </span>last-verse <span class="k">;
</span></span></span></code></pre></div><p>This solution is similar to the one
<a href="https://rosettacode.org/wiki/99_Bottles_of_Beer#Factor">posted</a> on the
Rosetta Code project.</p>
<h3 id="longer">Longer!</h3>
<p>You might notice that a lot of the text is duplicative, so perhaps we
can improve this solution by factoring out parts of the text into small
and reusable functions.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">combinators</span> <span class="nn">formatting</span> <span class="nn">io</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">#bottles</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { <span class="m">1 </span>[ <span class="s">"1 bottle"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0 </span>[ <span class="s">"no more bottles"</span> ] }
</span></span><span class="line"><span class="cl"> [ <span class="s">"%d bottles"</span> sprintf ]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="s">" of beer"</span> <span class="nb">append </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">on-the-wall</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> #bottles <span class="nb">dup </span><span class="s">"%s on the wall, %s.\n"</span> printf <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">take-one-down</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"Take one down and pass it around, "</span> <span class="nb">write
</span></span></span><span class="line"><span class="cl"> #bottles <span class="s">"%s on the wall.\n"</span> printf <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">take-bottles</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup zero? </span>] [
</span></span><span class="line"><span class="cl"> [ on-the-wall ] [ <span class="m">1 </span><span class="nb">- dup </span>take-one-down ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">until </span>on-the-wall <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">go-to-store</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"Go to the store and buy some more, "</span> <span class="nb">write
</span></span></span><span class="line"><span class="cl"> #bottles <span class="s">"%s on the wall.\n"</span> printf <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bottles</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ take-bottles ] [ go-to-store ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>It’s a bit longer than the original and, in several ways, not as easy to
understand. If we wanted to reuse this functionality elsewhere, it’s a
clear win. But, if we want to simply generate the “bottles song”,
perhaps the first or second way is better.</p>
<h3 id="extra-credit">Extra Credit</h3>
<p>For fun, I thought we could use our computer to sing to us (if you’re
using Mac OS):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ 99bottles ] with-string-writer
</span></span><span class="line"><span class="cl"> <span class="s">"say \"%s\""</span> sprintf try-process
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/99bottles/99bottles.factor">GitHub</a>.</p>
Fourth of July
https://re.factorcode.org/2011/07/fourth-of-july.html
Mon, 04 Jul 2011 11:25:00 -0700https://re.factorcode.org/2011/07/fourth-of-july.html<p>Today is <a href="https://en.wikipedia.org/wiki/Independence_Day_(United_States)">Independence
Day</a> in
the United States. Several lists of <a href="https://www.11points.com/Misc/11_Fantastic_Fourth_of_July_Facts">fantastic
facts</a>
are being shared, including that today is the “biggest beer-selling
holiday of the year”. However, while not as fantastic, here are a few
fun facts about today that can be derived using
<a href="https://www.factorcode.org">Factor</a>.</p>
<p>Using the
<a href="https://docs.factorcode.org/content/article-calendar.html">calendar</a>
vocabulary, we can create a
<a href="https://docs.factorcode.org/content/word-timestamp,calendar.html">timestamp</a>
object for today’s date:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2011 7 4 </span><date> <span class="m">.
</span></span></span><span class="line"><span class="cl">T{ timestamp
</span></span><span class="line"><span class="cl"> { year <span class="m">2011 </span>}
</span></span><span class="line"><span class="cl"> { month <span class="m">7 </span>}
</span></span><span class="line"><span class="cl"> { day <span class="m">4 </span>}
</span></span><span class="line"><span class="cl"> { gmt-offset T{ duration { hour <span class="m">-7 </span>} } }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>We can see that today is the 185<sup>th</sup> day of the year:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2011 7 4 </span><date> day-of-year <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">185
</span></span></span></code></pre></div><p>Today is also the 27<sup>th</sup> week of the year (using weeks starting
on Sunday - there might be a different answer using
<a href="https://docs.factorcode.org/content/word-week-of-year-monday,formatting.private.html">week-of-year-monday</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2011 7 4 </span><date> week-of-year-sunday <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">27
</span></span></span></code></pre></div><p>This year, July 4<sup>th</sup> falls on a Monday, but its a different
day of the week each year. We can see which days of the week
Independence Day falls on over the last few years:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2011 2000 </span>[a..b] [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">7 4 </span><date> day-name <span class="s">"%s: %s\n"</span> printf
</span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl">2011: Monday
</span></span><span class="line"><span class="cl">2010: Sunday
</span></span><span class="line"><span class="cl">2009: Saturday
</span></span><span class="line"><span class="cl">2008: Friday
</span></span><span class="line"><span class="cl">2007: Wednesday
</span></span><span class="line"><span class="cl">2006: Tuesday
</span></span><span class="line"><span class="cl">2005: Monday
</span></span><span class="line"><span class="cl">2004: Sunday
</span></span><span class="line"><span class="cl">2003: Friday
</span></span><span class="line"><span class="cl">2002: Thursday
</span></span><span class="line"><span class="cl">2001: Wednesday
</span></span><span class="line"><span class="cl">2000: Tuesday
</span></span></code></pre></div><p>And, finally, the most recent version of Factor (0.94) was
<a href="https://factor-language.blogspot.com/2010/09/factor-094-now-available.html">released</a>
on September 18, 2010, which was 289 days ago.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">2011 7 4 </span><date> <span class="m">2010 9 18 </span><date>
</span></span><span class="line"><span class="cl"> time- duration>days <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">289
</span></span></span></code></pre></div>TF-IDF
https://re.factorcode.org/2011/06/tf-idf.html
Wed, 29 Jun 2011 15:38:00 -0700https://re.factorcode.org/2011/06/tf-idf.html<p>A few days ago, someone implemented a concise
<a href="https://en.wikipedia.org/wiki/Tf%E2%80%93idf">TF-IDF</a> (“term frequency -
inverse document frequency”) search engine in
<a href="https://github.com/felipehummel/TinySearchEngine/blob/master/scala/tinySearch.scala">Scala</a>.
That was followed by a similarly concise version in
<a href="https://thecomputersarewinning.com/post/simple-tfidf-in-clojure">Clojure</a>.
I thought to contribute a simple (hopefully also concise) implementation
using <a href="https://www.factorcode.org">Factor</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">arrays</span> <span class="nn">assocs</span> <span class="nn">combinators.short-circuit</span> <span class="nn">fry</span>
</span></span><span class="line"><span class="cl"><span class="nn">io.encodings.utf8</span> <span class="nn">io.files</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">math.functions</span>
</span></span><span class="line"><span class="cl"><span class="nn">math.statistics</span> <span class="nn">memoize</span> <span class="nn">sequences</span> <span class="nn">sets</span> <span class="nn">sorting</span> <span class="nn">splitting</span>
</span></span><span class="line"><span class="cl"><span class="nn">unicode.case</span> <span class="nn">unicode.categories</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">tf-idf</span>
</span></span></code></pre></div><h3 id="tokenize">Tokenize</h3>
<p>Since our inputs are going to be text files, our program will need to:</p>
<ol>
<li>Read the file into a string.</li>
<li>Split the string into words.</li>
<li>Eliminate common (or “stop”) words.</li>
</ol>
<p>Fist, let’s build a word to split our text on any non-letter or
non-number characters.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">split-words</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">words</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ { [ Letter? ] [ digit? ] } 1|| <span class="nb">not </span>] split-when <span class="nb">harvest </span><span class="k">;
</span></span></span></code></pre></div><p>We load a list of “stop words” that are to be ignored. These are
typically common occurring words such as “<em>and, in, or, of, the, is</em>”.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MEMO:</span> <span class="nf">stopwords</span> <span class="nf">( -- </span><span class="nv">words</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"vocab:tf-idf/stopwords.txt"</span> utf8 file-lines fast-set <span class="k">;
</span></span></span></code></pre></div><p>We can then tokenize a piece of text, removing all of the “stop words”.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">tokenize</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">words</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> >lower split-words [ stopwords in? <span class="nb">not </span>] <span class="nb">filter </span><span class="k">;
</span></span></span></code></pre></div><p>And, finally, we can create a word to tokenize a series of files into an
<code>assoc</code> mapping <code>path</code> to <code>words</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">tokenize-files</span> <span class="nf">( </span><span class="nv">paths</span> <span class="nf">-- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>utf8 file-contents tokenize ] H{ } <span class="nb">map>assoc </span><span class="k">;
</span></span></span></code></pre></div><h3 id="index">Index</h3>
<p>To implement our search engine, we need to build an index that maps each
<code>word</code> to list of <code>(path, count)</code> pairs.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">index1</span> <span class="nf">( </span><span class="nv">path</span> <span class="nv">words</span> <span class="nf">-- </span><span class="nv">path</span> <span class="nv">index</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> histogram [ <span class="nb">pick swap 2array </span>] <span class="nb">assoc-map </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">index-all</span> <span class="nf">( </span><span class="nv">assoc</span> <span class="nf">-- </span><span class="nv">index</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ index1 ] <span class="nb">assoc-map values </span>assoc-merge <span class="k">;
</span></span></span></code></pre></div><p>The tokenized files and our index will form the basis for a “database”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">db</span> <span class="nv">docs</span> <span class="nv">index</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><db></span> <span class="nf">( </span><span class="nv">paths</span> <span class="nf">-- </span><span class="nv">db</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> tokenize-files <span class="nb">dup </span>index-all db <span class="nb">boa </span><span class="k">;
</span></span></span></code></pre></div><h3 id="tf-idf">TF-IDF</h3>
<p>The “inverse document frequency” is calculated by the
<a href="https://docs.factorcode.org/content/word-log,math.functions.html">log</a>
of the total number of documents divided by number of documents where a
particular word appears.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">idf</span> <span class="nf">( </span><span class="nv">term</span> <span class="nv">db</span> <span class="nf">-- </span><span class="nv">idf</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">nip </span>docs>> ] [ index>> <span class="nb">at </span>] <span class="nb">2bi </span>[ <span class="nb">assoc-size </span>] <span class="nb">bi@ / </span>log <span class="k">;
</span></span></span></code></pre></div><p>Using this, we can create a “TF-IDF” scores by multiplying the number of
times a term appears in each document by the previously calculated
“inverse document frequency”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">tf-idf</span> <span class="nf">( </span><span class="nv">term</span> <span class="nv">db</span> <span class="nf">-- </span><span class="nv">scores</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ index>> <span class="nb">at </span>] [ idf ] <span class="nb">2bi </span>'[ _ <span class="nb">* </span>] <span class="nb">assoc-map </span><span class="k">;
</span></span></span></code></pre></div><h3 id="search">Search</h3>
<p>Our search engine is now just a matter of:</p>
<ul>
<li>Splitting an input query into terms.</li>
<li>Calculating the “TF-IDF” score for each term.</li>
<li>Normalizing the scores across documents.</li>
<li>Sorting the scores from highest to lowest.</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">scores</span> <span class="nf">( </span><span class="nv">query</span> <span class="nv">db</span> <span class="nf">-- </span><span class="nv">scores</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ split-words ] <span class="nb">dip </span>'[ _ tf-idf ] <span class="nb">map </span>assoc-merge <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(normalize)</span> <span class="nf">( </span><span class="nv">path</span> <span class="nv">db</span> <span class="nf">-- </span><span class="nv">value</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ docs>> <span class="nb">at </span>] <span class="nb">keep </span>'[ _ idf <span class="m">2 </span>^ ] <span class="nb">map-sum </span>sqrt <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">normalize</span> <span class="nf">( </span><span class="nv">scores</span> <span class="nv">db</span> <span class="nf">-- </span><span class="nv">scores'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> '[ <span class="nb">sum over </span>_ (normalize) <span class="nb">/ </span>] <span class="nb">assoc-map </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">search</span> <span class="nf">( </span><span class="nv">query</span> <span class="nv">db</span> <span class="nf">-- </span><span class="nv">scores</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ scores ] <span class="nb">keep </span>normalize sort-values <span class="nb">reverse </span><span class="k">;
</span></span></span></code></pre></div><h3 id="notes">Notes</h3>
<p><em>The implementation above uses an <code>assoc-merge</code> utility word that
performs a “union”, for each key collecting a list of all values.</em></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(assoc-merge)</span> <span class="nf">( </span><span class="nv">assoc1</span> <span class="nv">assoc2</span> <span class="nf">-- </span><span class="nv">assoc1</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">over </span>[ <span class="nb">push-at </span>] with-assoc <span class="nb">assoc-each </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">assoc-merge</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">merge</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> H{ } <span class="nb">clone </span>[ (assoc-merge) ] <span class="nb">reduce </span><span class="k">;
</span></span></span></code></pre></div><p><em>Also, searching for words that don’t exist produces a “divide by zero”
error. Making it more robust requires some changes to either catch and
ignore that error or to “add 1” to the denominator of the “IDF”
formula.</em></p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/tf-idf/tf-idf.factor">GitHub</a>.</p>
Yahoo! Finance
https://re.factorcode.org/2011/06/yahoo-finance.html
Fri, 10 Jun 2011 21:23:00 -0700https://re.factorcode.org/2011/06/yahoo-finance.html<p>Many programmers work for publicly traded companies, and probably spend
more time than they realize watching their (or their friends) company’s
stock prices. To make that easier, I wanted to implement a simple
wrapper to retrieve prices from <a href="https://finance.yahoo.com">Yahoo!
Finance</a>.</p>
<h2 id="quotes">Quotes</h2>
<p>You can use a “quotes.csv” interface to retrieve current price
information. The way it works is to perform an HTTP request to a
specially formatted URL that looks like:</p>
<pre tabindex="0"><code>https://finance.yahoo.com/d/quotes.csv?s=SYMBOLS&f=FIELDS
</code></pre><p>In the URL, <code>SYMBOLS</code> is a list of symbols separated by “<code>+</code>” and
<code>FIELDS</code> is a list of letters and numbers (from the following table)
representing fields to be requested.</p>
<table>
<tbody>
<tr class="odd">
<td><strong>a</strong></td>
<td>Ask</td>
<td><strong>a2</strong></td>
<td>Average Daily Volume</td>
<td><strong>a5</strong></td>
<td>Ask Size</td>
</tr>
<tr class="even">
<td><strong>b</strong></td>
<td>Bid</td>
<td><strong>b2</strong></td>
<td>Ask (Real-time)</td>
<td><strong>b3</strong></td>
<td>Bid (Real-time)</td>
</tr>
<tr class="odd">
<td><strong>b4</strong></td>
<td>Book Value</td>
<td><strong>b6</strong></td>
<td>Bid Size</td>
<td><strong>c</strong></td>
<td>Change & Percent Change</td>
</tr>
<tr class="even">
<td><strong>c1</strong></td>
<td>Change</td>
<td><strong>c3</strong></td>
<td>Commission</td>
<td><strong>c6</strong></td>
<td>Change (Real-time)</td>
</tr>
<tr class="odd">
<td><strong>c8</strong></td>
<td>After Hours Change (Real-time)</td>
<td><strong>d</strong></td>
<td>Dividend/Share</td>
<td><strong>d1</strong></td>
<td>Last Trade Date</td>
</tr>
<tr class="even">
<td><strong>d2</strong></td>
<td>Trade Date</td>
<td><strong>e</strong></td>
<td>Earnings/Share</td>
<td><strong>e1</strong></td>
<td>Error Indication (returned for symbol changed / invalid)</td>
</tr>
<tr class="odd">
<td><strong>e7</strong></td>
<td>EPS Estimate Current Year</td>
<td><strong>e8</strong></td>
<td>EPS Estimate Next Year</td>
<td><strong>e9</strong></td>
<td>EPS Estimate Next Quarter</td>
</tr>
<tr class="even">
<td><strong>f6</strong></td>
<td>Float Shares</td>
<td><strong>g</strong></td>
<td>Day’s Low</td>
<td><strong>h</strong></td>
<td>Day’s High</td>
</tr>
<tr class="odd">
<td><strong>j</strong></td>
<td>52-week Low</td>
<td><strong>k</strong></td>
<td>52-week High</td>
<td><strong>g1</strong></td>
<td>Holdings Gain Percent</td>
</tr>
<tr class="even">
<td><strong>g3</strong></td>
<td>Annualized Gain</td>
<td><strong>g4</strong></td>
<td>Holdings Gain</td>
<td><strong>g5</strong></td>
<td>Holdings Gain Percent (Real-time)</td>
</tr>
<tr class="odd">
<td><strong>g6</strong></td>
<td>Holdings Gain (Real-time)</td>
<td><strong>i</strong></td>
<td>More Info</td>
<td><strong>i5</strong></td>
<td>Order Book (Real-time)</td>
</tr>
<tr class="even">
<td><strong>j1</strong></td>
<td>Market Capitalization</td>
<td><strong>j3</strong></td>
<td>Market Cap (Real-time)</td>
<td><strong>j4</strong></td>
<td>EBITDA</td>
</tr>
<tr class="odd">
<td><strong>j5</strong></td>
<td>Change From 52-week Low</td>
<td><strong>j6</strong></td>
<td>Percent Change From 52-week Low</td>
<td><strong>k1</strong></td>
<td>Last Trade (Real-time) With Time</td>
</tr>
<tr class="even">
<td><strong>k2</strong></td>
<td>Change Percent (Real-time)</td>
<td><strong>k3</strong></td>
<td>Last Trade Size</td>
<td><strong>k4</strong></td>
<td>Change From 52-week High</td>
</tr>
<tr class="odd">
<td><strong>k5</strong></td>
<td>Percent Change From 52-week High</td>
<td><strong>l</strong></td>
<td>Last Trade (With Time)</td>
<td><strong>l1</strong></td>
<td>Last Trade (Price Only)</td>
</tr>
<tr class="even">
<td><strong>l2</strong></td>
<td>High Limit</td>
<td><strong>l3</strong></td>
<td>Low Limit</td>
<td><strong>m</strong></td>
<td>Day’s Range</td>
</tr>
<tr class="odd">
<td><strong>m2</strong></td>
<td>Day’s Range (Real-time)</td>
<td><strong>m3</strong></td>
<td>50-day Moving Average</td>
<td><strong>m4</strong></td>
<td>200-day Moving Average</td>
</tr>
<tr class="even">
<td><strong>m5</strong></td>
<td>Change From 200-day Moving Average</td>
<td><strong>m6</strong></td>
<td>Percent Change From 200-day Moving Average</td>
<td><strong>m7</strong></td>
<td>Change From 50-day Moving Average</td>
</tr>
<tr class="odd">
<td><strong>m8</strong></td>
<td>Percent Change From 50-day Moving Average</td>
<td><strong>n</strong></td>
<td>Name</td>
<td><strong>n4</strong></td>
<td>Notes</td>
</tr>
<tr class="even">
<td><strong>o</strong></td>
<td>Open</td>
<td><strong>p</strong></td>
<td>Previous Close</td>
<td><strong>p1</strong></td>
<td>Price Paid</td>
</tr>
<tr class="odd">
<td><strong>p2</strong></td>
<td>Change in Percent</td>
<td><strong>p5</strong></td>
<td>Price/Sales</td>
<td><strong>p6</strong></td>
<td>Price/Book</td>
</tr>
<tr class="even">
<td><strong>q</strong></td>
<td>Ex-Dividend Date</td>
<td><strong>r</strong></td>
<td>P/E Ratio</td>
<td><strong>r1</strong></td>
<td>Dividend Pay Date</td>
</tr>
<tr class="odd">
<td><strong>r2</strong></td>
<td>P/E Ratio (Real-time)</td>
<td><strong>r5</strong></td>
<td>PEG Ratio</td>
<td><strong>r6</strong></td>
<td>Price/EPS Estimate Current Year</td>
</tr>
<tr class="even">
<td><strong>r7</strong></td>
<td>Price/EPS Estimate Next Year</td>
<td><strong>s</strong></td>
<td>Symbol</td>
<td><strong>s1</strong></td>
<td>Shares Owned</td>
</tr>
<tr class="odd">
<td><strong>s7</strong></td>
<td>Short Ratio</td>
<td><strong>t1</strong></td>
<td>Last Trade Time</td>
<td><strong>t6</strong></td>
<td>Trade Links</td>
</tr>
<tr class="even">
<td><strong>t7</strong></td>
<td>Ticker Trend</td>
<td><strong>t8</strong></td>
<td>1 yr Target Price</td>
<td><strong>v</strong></td>
<td>Volume</td>
</tr>
<tr class="odd">
<td><strong>v1</strong></td>
<td>Holdings Value</td>
<td><strong>v7</strong></td>
<td>Holdings Value (Real-time)</td>
<td><strong>w</strong></td>
<td>52-week Range</td>
</tr>
<tr class="even">
<td><strong>w1</strong></td>
<td>Day’s Value Change</td>
<td><strong>w4</strong></td>
<td>Day’s Value Change (Real-time)</td>
<td><strong>x</strong></td>
<td>Stock Exchange</td>
</tr>
<tr class="odd">
<td><strong>y</strong></td>
<td>Dividend Yield</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<p>Using this URL format, we can build a word that retrieves current quotes
for a list of symbols:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">quotes</span> <span class="nf">( </span><span class="nv">symbols</span> <span class="nf">-- </span><span class="nv">csv</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://finance.yahoo.com/d/quotes.csv"</span> >url
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span><span class="s">"+"</span> <span class="nb">join </span><span class="s">"s"</span> set-query-param
</span></span><span class="line"><span class="cl"> <span class="s">"sbal1v"</span> <span class="s">"f"</span> set-query-param
</span></span><span class="line"><span class="cl"> http-get <span class="nb">nip >string </span>string>csv
</span></span><span class="line"><span class="cl"> { <span class="s">"Symbol"</span> <span class="s">"Bid"</span> <span class="s">"Ask"</span> <span class="s">"Last"</span> <span class="s">"Volume"</span> } <span class="nb">prefix </span><span class="k">;
</span></span></span></code></pre></div><p>With the
<a href="https://docs.factorcode.org/content/vocab-strings.tables.html">strings.table</a>
vocabulary, we can format the response as a table:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="s">"MSFT"</span> <span class="s">"GOOG"</span> <span class="s">"AAPL"</span> } quotes
</span></span><span class="line"><span class="cl"> format-table [ <span class="nb">print </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl">Symbol Bid Ask Last Volume
</span></span><span class="line"><span class="cl">MSFT <span class="m">23.64 </span> <span class="m">23.69 </span> <span class="m">23.705 </span> <span class="m">49327104
</span></span></span><span class="line"><span class="cl">GOOG <span class="m">505.00 509.05 509.505 2440475
</span></span></span><span class="line"><span class="cl">AAPL N/A <span class="m">334.80 325.90 </span> <span class="m">15504200
</span></span></span></code></pre></div><h2 id="historical-prices">Historical Prices</h2>
<p>You can also retrieve historical prices using a “table.csv” interface.
Similar to retrieving quotes, you make an HTTP request to a special URL:</p>
<pre tabindex="0"><code>https://ichart.finance.yahoo.com/table.csv?s=SYMBOL
</code></pre><p>In the URL, <code>SYMBOL</code> is the symbol that you are requesting prices for,
and you can further limit the response using additional parameters:</p>
<p>Start date for historical prices:</p>
<ul>
<li><strong>a</strong> - Month number, starting with 0 for January.</li>
<li><strong>b</strong> - Day number, eg, 1 for the first of the month.</li>
<li><strong>c</strong> - Year.</li>
</ul>
<p>End date for historical prices (default is the most current available
closing price):</p>
<ul>
<li><strong>d</strong> - Month number, starting with 0 for January.</li>
<li><strong>e</strong> - Day number, eg, 1 for the first of the month.</li>
<li><strong>f</strong> - Year.</li>
</ul>
<p>And finally, the frequency of historical prices:</p>
<ul>
<li><strong>g</strong> - Possible values are ’d’ for daily (the default), ‘w’ for
weekly, and ’m’ for monthly.</li>
</ul>
<p>With this knowledge, we can build a word to retrieve historical prices
from January 1, 2009 until the current day.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">historical-prices</span> <span class="nf">( </span><span class="nv">symbol</span> <span class="nf">-- </span><span class="nv">csv</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://ichart.finance.yahoo.com/table.csv"</span> >url
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span><span class="s">"s"</span> set-query-param
</span></span><span class="line"><span class="cl"> <span class="s">"0"</span> <span class="s">"a"</span> set-query-param
</span></span><span class="line"><span class="cl"> <span class="s">"1"</span> <span class="s">"b"</span> set-query-param
</span></span><span class="line"><span class="cl"> <span class="s">"2009"</span> <span class="s">"c"</span> set-query-param
</span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>string>csv <span class="k">;
</span></span></span></code></pre></div><p>To use it, and demonstrate the impact that
<a href="https://finance.yahoo.com/q?s=AAPL&ql=1">AAPL</a> has had over the last few
years, we can
<a href="https://re.factorcode.org/2011/03/google-charts.html">chart</a> the
daily closing prices (remembering to reverse the order of the prices,
oldest to newest):</p>
<p>
<img src="https://re.factorcode.org/images/2011-06-10-yahoo-finance-historical-prices.png" alt="" width="442" height="271" />
</p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/yahoo/finance/finance.factor">GitHub</a>.</p>
Fibonacci Wars
https://re.factorcode.org/2011/05/fibonacci-wars.html
Tue, 17 May 2011 11:53:00 -0700https://re.factorcode.org/2011/05/fibonacci-wars.html<p>Inspired by a recent
<a href="https://bosker.wordpress.com/2011/04/29/the-worst-algorithm-in-the-world/">article</a>
about the “worst algorithm in the world” for calculating <a href="https://en.wikipedia.org/wiki/Fibonacci_number">Fibonacci
numbers</a> using
<a href="https://www.python.org">Python</a>, I wanted to show a few implementations
in <a href="https://www.factorcode.org">Factor</a>.</p>
<h3 id="recursive">Recursive</h3>
<p>Most people should be familiar with the simple definition for
calculating the Fibonacci numbers:</p>
<p>
<img src="https://re.factorcode.org/images/2011-05-17-fibonacci-wars-slow-fib.png" alt="" width="218" height="56" />
</p>
<p>Some recursive implementations of this algorithm suffer from very slow
performance due to the nature of hugely recursive functions, making them
unsuitable for larger Fibonacci numbers. Thankfully, it is easy to
implement caching using the
<a href="https://docs.factorcode.org/content/article-memoize.html">memoize</a>
vocabulary:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MEMO:</span> <span class="nf">slow-fib</span> <span class="nf">( </span><span class="nv">m</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb">>= </span>[ <span class="nb">throw </span>] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">2 </span><span class="nb">>= </span>[
</span></span><span class="line"><span class="cl"> [ <span class="m">2 </span><span class="nb">- </span>slow-fib ] [ <span class="m">1 </span><span class="nb">- </span>slow-fib ] <span class="nb">bi +
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><h3 id="iterative">Iterative</h3>
<p>A better way would be to implement an iterative solution, that simply
keeps the current and previous Fibonacci number on the stack, adding
them the specified number of times:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">okay-fib</span> <span class="nf">( </span><span class="nv">m</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb">>= </span>[ <span class="nb">throw </span>] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> [ <span class="m">0 1 </span>] <span class="nb">dip </span>[ [ <span class="nb">+ </span>] [ <span class="nb">drop </span>] <span class="nb">2bi </span>] <span class="nb">times drop </span><span class="k">;
</span></span></span></code></pre></div><h3 id="magical">Magical</h3>
<p>A much better way (introduced by the original article) suggests taking
advantage of a matrix expression for the Fibonacci numbers:</p>
<p>
<img src="https://re.factorcode.org/images/2011-05-17-fibonacci-wars-fast-fib.png" alt="" width="281" height="41" />
</p>
<p>Using this, we can implement a faster version in Factor (using
<a href="https://docs.factorcode.org/content/article-locals.html">locals</a> to make
it a bit more readable):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">fast-fib</span> <span class="nf">( </span><span class="nv">m</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> m <span class="m">0 </span><span class="nb">>= </span>[ m <span class="nb">throw </span>] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> m <span class="m">2 </span>>base [ <span class="sc">CHAR: 1 </span><span class="nb">= </span>] { } <span class="nb">map-as </span>:> bits
</span></span><span class="line"><span class="cl"> <span class="m">1 </span>:> a! <span class="m">0 </span>:> b! <span class="m">1 </span>:> c!
</span></span><span class="line"><span class="cl"> bits [
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> a c <span class="nb">+ </span>b <span class="nb">*
</span></span></span><span class="line"><span class="cl"> b <span class="nb">sq </span>c <span class="nb">sq +
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> a <span class="nb">sq </span>b <span class="nb">sq +
</span></span></span><span class="line"><span class="cl"> a c <span class="nb">+ </span>b <span class="nb">*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span>b! a! a b <span class="nb">+ </span>c!
</span></span><span class="line"><span class="cl"> ] <span class="nb">each </span>b <span class="k">;
</span></span></span></code></pre></div><h3 id="performance">Performance</h3>
<p>Some simple performance numbers show how much faster the “magical”
version is, particularly for calculating large Fibonacci numbers (such
as the one-millionth number in the sequence).</p>
<table>
<thead>
<tr class="header">
<th></th>
<th style="text-align: right;">10,000</th>
<th style="text-align: center;">100,000</th>
<th style="text-align: center;">1,000,000</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>slow-fib</code></td>
<td style="text-align: right;">0.0097</td>
<td style="text-align: center;">--</td>
<td style="text-align: center;">--</td>
</tr>
<tr class="even">
<td><code>okay-fib</code></td>
<td style="text-align: right;">0.0053</td>
<td style="text-align: right;">0.2560</td>
<td style="text-align: right;">25.9608</td>
</tr>
<tr class="odd">
<td><code>fast-fib</code></td>
<td style="text-align: right;">0.0001</td>
<td style="text-align: right;">0.0076</td>
<td style="text-align: right;">0.4851</td>
</tr>
</tbody>
</table>
<p>It’s worth pointing out that Factor is about as fast as Python 2.6 for
the “magical” calculation. In Python 2.7 and later, certain improvements
were made to their large number support, resulting in Python being about
3x faster.</p>
<p>However, if you want real performance, you can use C and the <a href="https://gmplib.org/">GNU
Multiple Precision Arithmetic Library</a> to implement
the iterative algorithm (about <a href="https://github.com/mrjbq7/re-factor/blob/master/fast-fib/fib1.c">3 times
faster</a>
than Factor), the magical algorithm (about <a href="https://github.com/mrjbq7/re-factor/blob/master/fast-fib/fib3.c">28 times
faster</a>
than Factor), or use the builtin
<a href="https://gmplib.org/manual/Number-Theoretic-Functions.html#index-mpz_005ffib_005fui-401">mpz_fib_ui</a>
function (about <a href="https://github.com/mrjbq7/re-factor/blob/master/fast-fib/fib2.c">44 times
faster</a>
than Factor).</p>
<p>The code for this (as well as several version in C) are on my
<a href="https://github.com/mrjbq7/re-factor/tree/master/fast-fib">GitHub</a>.</p>
Google Search
https://re.factorcode.org/2011/05/google-search.html
Sun, 15 May 2011 10:15:00 -0700https://re.factorcode.org/2011/05/google-search.html<p>A couple months ago, I implemented wrappers for the <a href="https://re.factorcode.org/2011/03/google-charts.html">Google
Charts</a> and
<a href="https://re.factorcode.org/2011/03/google-translate.html">Google
Translate</a>
API’s. Today, I’d like to implement a wrapper for Google Search.</p>
<h3 id="search">Search</h3>
<p>First, we should build a word that, given a query, returns a URL to
retrieve Google Search results (formatted as
<a href="https://www.json.org/">JSON</a>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">search-url</span> <span class="nf">( </span><span class="nv">query</span> <span class="nf">-- </span><span class="nv">url</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">URL" https://ajax.googleapis.com/ajax/services/search/web"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"1.0"</span> <span class="s">"v"</span> set-query-param
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span><span class="s">"q"</span> set-query-param
</span></span><span class="line"><span class="cl"> <span class="s">"8"</span> <span class="s">"rsz"</span> set-query-param
</span></span><span class="line"><span class="cl"> <span class="s">"0"</span> <span class="s">"start"</span> set-query-param <span class="k">;
</span></span></span></code></pre></div><p>We can define a <a href="https://docs.factorcode.org/content/article-tuples.html">tuple
class</a> to hold
the attributes every search result should have:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">search-result</span> <span class="nv">cacheUrl</span> <span class="nv">GsearchResultClass</span> <span class="nv">visibleUrl</span>
</span></span><span class="line"><span class="cl"><span class="nv">title</span> <span class="nv">content</span> <span class="nv">unescapedUrl</span> <span class="nv">url</span> <span class="nv">titleNoFormatting</span> <span class="k">;
</span></span></span></code></pre></div><p>Using some code to <a href="https://re.factorcode.org/2011/01/setting-attributes.html">set attributes
dynamically</a>,
we can perform a Google Search, and parse the results into a sequence of
<code>search-result</code> objects.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">http-search</span> <span class="nf">( </span><span class="nv">query</span> <span class="nf">-- </span><span class="nv">results</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> search-url http-get <span class="nb">nip </span>json
</span></span><span class="line"><span class="cl"> { <span class="s">"responseData"</span> <span class="s">"results"</span> } [ <span class="nb">swap at </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> [ <span class="no">\ search-result</span> from-slots ] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><h3 id="display">Display</h3>
<p>We can build some simple words that can be used to format the output of
a search:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-heading</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> H{
</span></span><span class="line"><span class="cl"> { font-size <span class="m">14 </span>}
</span></span><span class="line"><span class="cl"> { background COLOR: light-gray }
</span></span><span class="line"><span class="cl"> } format <span class="nb">nl </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-title</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> H{
</span></span><span class="line"><span class="cl"> { foreground COLOR: blue }
</span></span><span class="line"><span class="cl"> } format <span class="nb">nl </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-content</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="m">60 </span>wrap-string <span class="nb">print </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-url</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>>url H{
</span></span><span class="line"><span class="cl"> { font-name <span class="s">"monospace"</span> }
</span></span><span class="line"><span class="cl"> { foreground COLOR: dark-green }
</span></span><span class="line"><span class="cl"> } [ write-object ] with-style <span class="nb">nl </span><span class="k">;
</span></span></span></code></pre></div><p>And then create a word to perform a search and display the results. If
you are using my
<a href="https://github.com/mrjbq7/re-factor/blob/master/webbrowser/webbrowser.factor">webbrowser</a>
vocabulary, you can open the URL’s in a webbrowser directly from Factor.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">http-search.</span> <span class="nf">( </span><span class="nv">query</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"Search results for '%s'"</span> sprintf write-heading <span class="nb">nl </span>]
</span></span><span class="line"><span class="cl"> [ http-search ] <span class="nb">bi </span>[
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ titleNoFormatting>> write-title ]
</span></span><span class="line"><span class="cl"> [ content>> write-content ]
</span></span><span class="line"><span class="cl"> [ unescapedUrl>> write-url ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave nl
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><h3 id="try-it">Try It</h3>
<p>If everything works correctly, you should be able to perform a search in
the Listener (e.g., searching for “factor”):</p>
<p>
<img src="https://re.factorcode.org/images/2011-05-15-google-search-http-search.png" alt="" width="512" height="323" />
</p>
<p>Some things we might do to improve this:</p>
<ul>
<li>add paging, to the next or previous page of search results</li>
<li>highlight in bold the searched words in the content</li>
<li>unescape the HTML entities in the content</li>
<li>build a lightweight GUI to render the results</li>
</ul>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/google/search/search.factor">GitHub</a>.</p>
Open URL (in Listener)
https://re.factorcode.org/2011/05/open-url-in-listener.html
Thu, 05 May 2011 21:38:00 -0700https://re.factorcode.org/2011/05/open-url-in-listener.html<p>In January, I implemented a cross-platform
<a href="https://re.factorcode.org/2011/01/open-url.html">open-url</a> feature
to allow opening URLs from Factor. One major problem was that I couldn’t
figure out how to make URLs “clickable” in the Listener.</p>
<p>Then, I learned that the
<a href="https://docs.factorcode.org/content/vocab-ui.operations.html">ui.operations</a>
vocabulary allows us to add “capabilities” that get triggered when
certain types are rendered in the Listener. It turns out to be pretty
easy, actually:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">[ url? ] <span class="no">\ open-url</span> H{ } define-operation
</span></span></code></pre></div><p>Using that, you can right-click on URL objects, and choose to open them
in your web browser:</p>
<p>
<img src="https://re.factorcode.org/images/2011-05-05-open-url-in-listener-open-url.png" alt="" width="307" height="149" />
</p>
<p>I also made a <a href="https://github.com/factor/factor/commit/927f66f901d181e8900b33321e22b1c9dad7c2c2#basis/help/markup/markup.factor">small
patch</a>
to add this support to URLs that are rendered in the <a href="https://docs.factorcode.org/content/article-help.html">help
system</a> or in the
<a href="https://docs.factorcode.org/content/vocab-slides.html">slides</a>
vocabulary.</p>
<p><em>Note: if you want this operation to be the “primary operation”
triggered when the URL is left-clicked, you can add <code>{ +primary+ t }</code> to
the hashtable used in the operation’s definition.</em></p>
Reddit Stats
https://re.factorcode.org/2011/05/reddit-stats.html
Sun, 01 May 2011 19:49:00 -0700https://re.factorcode.org/2011/05/reddit-stats.html<p>I thought it would be fun to track the development of the
<a href="https://factorcode.org">Factor</a> programming language using Reddit. Of
course, to do this, I wanted to use Factor.</p>
<p>A few months ago, I implemented a <a href="https://re.factorcode.org/2011/01/reddit-top.html">Reddit
“Top”</a> program.
We can use the code for that as a basis for showing, using Reddit
scores, how the activity (or popularity?) of a couple Factor blogs have
changed over the last few years:</p>
<p>
<img src="https://re.factorcode.org/images/2011-05-01-reddit-stats-blog-scores.png" alt="" width="373" height="252" />
</p>
<h3 id="stories">Stories</h3>
<p>We start by creating a <code>story</code> tuple that will hold all of the
properties that Reddit returns for each posted link (including the score
– up votes minus down votes – which we will be using).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">story</span> <span class="nv">author</span> <span class="nv">clicked</span> <span class="nv">created</span> <span class="nv">created_utc</span> <span class="nv">domain</span> <span class="nv">downs</span>
</span></span><span class="line"><span class="cl"><span class="nv">hidden</span> <span class="nv">id</span> <span class="nv">is_self</span> <span class="nv">levenshtein</span> <span class="nv">likes</span> <span class="nv">media</span> <span class="nv">media_embed</span> <span class="nv">name</span>
</span></span><span class="line"><span class="cl"><span class="nv">num_comments</span> <span class="nv">over_18</span> <span class="nv">permalink</span> <span class="nv">saved</span> <span class="nv">score</span> <span class="nv">selftext</span>
</span></span><span class="line"><span class="cl"><span class="nv">selftext_html</span> <span class="nv">subreddit</span> <span class="nv">subreddit_id</span> <span class="nv">thumbnail</span> <span class="nv">title</span> <span class="nv">ups</span> <span class="nv">url</span> <span class="k">;
</span></span></span></code></pre></div><p>And a word to parse a “data” result into a story object:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-story</span> <span class="nf">( </span><span class="nv">assoc</span> <span class="nf">-- </span><span class="nv">obj</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"data"</span> <span class="nb">swap at </span><span class="no">\ story</span> from-slots <span class="k">;
</span></span></span></code></pre></div><h3 id="paging">Paging</h3>
<p>The <a href="https://code.reddit.com/wiki/API">Reddit API</a> provides results as a
series of “pages” (until all results are exhausted). This is pretty
similar to how the website works, so it should be fairly easy to
understand the mechanics. We will define a <code>page</code> object that holds the
current URL, the results from the current page, and links to the pages
before and after the current page:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">page</span> <span class="nv">url</span> <span class="nv">results</span> <span class="nv">before</span> <span class="nv">after</span> <span class="k">;
</span></span></span></code></pre></div><p>We can then build a word to fetch the page (as a JSON response), parse
it, and construct a <code>page</code> object:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">json-page</span> <span class="nf">( </span><span class="nv">url</span> <span class="nf">-- </span><span class="nv">page</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> >url <span class="nb">dup </span>http-get <span class="nb">nip </span>json> <span class="s">"data"</span> <span class="nb">swap at </span>{
</span></span><span class="line"><span class="cl"> [ <span class="s">"children"</span> <span class="nb">swap at </span>[ parse-story ] <span class="nb">map </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"before"</span> <span class="nb">swap at </span>[ <span class="no">f </span>] when-json-null ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"after"</span> <span class="nb">swap at </span>[ <span class="no">f </span>] when-json-null ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span><span class="no">\ page</span> <span class="nb">boa </span><span class="k">;
</span></span></span></code></pre></div><p>An easy “paging” mechanism that, given a page of results, can return the
next page:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-page</span> <span class="nf">( </span><span class="nv">page</span> <span class="nf">-- </span><span class="nv">page'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ url>> ] [ after>> <span class="s">"after"</span> set-query-param ] <span class="nb">bi </span>json-page <span class="k">;
</span></span></span></code></pre></div><p>And, using the
<a href="https://docs.factorcode.org/content/article-namespaces-make.html">make</a>
vocabulary, construct all the results into a sequence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-pages</span> <span class="nf">( </span><span class="nv">page</span> <span class="nf">-- </span><span class="nv">results</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ [ results>> , ] [ <span class="nb">dup </span>after>> ] <span class="nb">bi </span>]
</span></span><span class="line"><span class="cl"> [ next-page ] <span class="nb">while drop
</span></span></span><span class="line"><span class="cl"> ] { } make <span class="nb">concat </span><span class="k">;
</span></span></span></code></pre></div><h3 id="domain-stats">Domain Stats</h3>
<p>We can retrieve all the results and produces a map of years that links
were posted to a sum of the scores for each year’s links:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">domain-stats</span> <span class="nf">( </span><span class="nv">domain</span> <span class="nf">-- </span><span class="nv">stats</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://api.reddit.com/domain/%s"</span> sprintf json-page all-pages
</span></span><span class="line"><span class="cl"> [ created>> <span class="m">1000 </span><span class="nb">* </span>millis>timestamp year>> ] group-by
</span></span><span class="line"><span class="cl"> [ [ score>> ] <span class="nb">map-sum </span>] <span class="nb">assoc-map </span><span class="k">;
</span></span></span></code></pre></div><p>Finally, a word to display a chart of the scores across a given list of
domains:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">domains.</span> <span class="nf">( </span><span class="nv">domains</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> H{ } <span class="nb">clone </span>[
</span></span><span class="line"><span class="cl"> '[ domain-stats [ <span class="nb">swap </span>_ <span class="nb">at+ </span>] <span class="nb">assoc-each </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">keep >alist </span>sort-keys <bar> <span class="m">160 </span>>>height chart. <span class="k">;
</span></span></span></code></pre></div><p>The code for this is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/reddit/reddit.factor">GitHub</a>.</p>
iPhone Backups
https://re.factorcode.org/2011/04/iphone-backups.html
Wed, 20 Apr 2011 09:22:00 -0700https://re.factorcode.org/2011/04/iphone-backups.html<p>A few weeks ago, I noticed a
<a href="https://github.com/timjuravich/relation-text">project</a> on GitHub that
extracts text messages from your iPhone and prints them out. More
specifically, it accesses the backups that iTunes makes and queries a
<a href="https://sqlite.org/">SQLite</a> database to retrieve the messages. We are
going to build something similar using <a href="https://factorcode.org/">Factor</a>.</p>
<p>First, some vocabularies we will be using:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">db</span> <span class="nn">db.sqlite</span> <span class="nn">db.tuples</span> <span class="nn">db.types</span> <span class="nn">io.directories</span>
</span></span><span class="line"><span class="cl"><span class="nn">io.files.info</span> <span class="nn">io.files.unique</span> <span class="nn">io.pathnames</span> <span class="nn">kernel</span> <span class="nn">sequences</span>
</span></span><span class="line"><span class="cl"><span class="nn">sorting</span> <span class="k">;
</span></span></span></code></pre></div><h3 id="database">Database</h3>
<p>We will need a word to choose the most recently modified file in a
directory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">last-modified</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">path'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ file-info modified>> ] sort-with <span class="nb">last
</span></span></span><span class="line"><span class="cl"> ] with-directory-files <span class="k">;
</span></span></span></code></pre></div><p>The iPhone backups are stored (on Mac OS) in the
<code>~/Library/Application Support/MobileSync/Backup</code> directory. We can
choose the most recent backup:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">last-backup</span> <span class="nf">( -- </span><span class="nv">path</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> home <span class="s">"Library/Application Support/MobileSync/Backup"</span>
</span></span><span class="line"><span class="cl"> append-path <span class="nb">dup </span>last-modified append-path <span class="k">;
</span></span></span></code></pre></div><p>The messages are stored in a SQLite database file with the name
<code>3d0d7e5fb2ce288813306e4d4636395e047a3d28</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">last-messages</span> <span class="nf">( -- </span><span class="nv">path</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> last-backup <span class="s">"3d0d7e5fb2ce288813306e4d4636395e047a3d28"</span>
</span></span><span class="line"><span class="cl"> append-path <span class="k">;
</span></span></span></code></pre></div><p>Since we will be using a backup file (that might be needed later), we
will make make a helper word that copies the SQLite database to a
temporary file (so we can’t mess up the original) and then opens it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><copy-sqlite-db></span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">sqlite-db</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> temporary-file [ copy-file ] [ <sqlite-db> ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>Using this, we can make another helper word that allows us to run
arbitrary queries on a copy of the most recent messages database:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">with-messages-db</span> <span class="nf">( </span><span class="nv">quot</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ last-messages <copy-sqlite-db> ] <span class="nb">dip </span>with-db <span class="k">; inline
</span></span></span></code></pre></div><h3 id="schema">Schema</h3>
<p>The SQLite database has a <code>message</code> table that contains all the text
messages sent or received by your iPhone. Using <code>.schema</code>, we can see
what it looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">message</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">ROWID</span><span class="w"> </span><span class="nb">INTEGER</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="k">KEY</span><span class="w"> </span><span class="n">AUTOINCREMENT</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">address</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">date</span><span class="w"> </span><span class="nb">INTEGER</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">flags</span><span class="w"> </span><span class="nb">INTEGER</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">replace</span><span class="w"> </span><span class="nb">INTEGER</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">svc_center</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">group_id</span><span class="w"> </span><span class="nb">INTEGER</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">association_id</span><span class="w"> </span><span class="nb">INTEGER</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">height</span><span class="w"> </span><span class="nb">INTEGER</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">UIFlags</span><span class="w"> </span><span class="nb">INTEGER</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">version</span><span class="w"> </span><span class="nb">INTEGER</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">subject</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">country</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">headers</span><span class="w"> </span><span class="nb">BLOB</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">recipients</span><span class="w"> </span><span class="nb">BLOB</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">read</span><span class="w"> </span><span class="nb">INTEGER</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><h3 id="queries">Queries</h3>
<p>We could use raw SQL queries to retrieve all the messages:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"select * from message"</span> sql-query ] with-messages-db
</span></span></code></pre></div><p>Or, we can build a word that performs a SQL query to retrieve just the
number of messages, converting the response into a number.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">count-messages</span> <span class="nf">( -- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"select count(*) from message"</span> sql-query
</span></span><span class="line"><span class="cl"> <span class="nb">first first </span>string>number <span class="k">;
</span></span></span></code></pre></div><p>Using it is pretty straightforward:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ count-messages ] with-messages-db
</span></span></code></pre></div><p>Unfortunately, raw SQL queries are not able to automatically convert the
columns from the result set based on the type of data stored. It would
be nicer if numbers stored in the database could be retrieved as numbers
from Factor. We can instead model the database tables, and then use
high-level constructs to query and interact with the database. You can
read a nice
<a href="https://docs.factorcode.org/content/article-db-tuples-tutorial.html">tutorial</a>
to get more information on how <code>db.tuples</code> works.</p>
<p>First, we create a tuple that matches the <code>message</code> table:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">message</span> <span class="nv">rowid</span> <span class="nv">address</span> <span class="nv">date</span> <span class="nv">text</span> <span class="nv">flags</span> <span class="nv">replace</span> <span class="nv">svc-center</span>
</span></span><span class="line"><span class="cl"><span class="nv">group-id</span> <span class="nv">association-id</span> <span class="nv">height</span> <span class="nv">ui-flags</span> <span class="nv">version</span> <span class="nv">subject</span> <span class="nv">country</span>
</span></span><span class="line"><span class="cl"><span class="nv">headers</span> <span class="nv">recipients</span> <span class="nv">read?</span> <span class="k">;
</span></span></span></code></pre></div><p>Then, we define a persistence relationship between the <code>message</code> tuple
and the <code>message</code> table:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">message <span class="s">"message"</span> {
</span></span><span class="line"><span class="cl"> { <span class="s">"rowid"</span> <span class="s">"ROWID"</span> +db-assigned-id+ }
</span></span><span class="line"><span class="cl"> { <span class="s">"address"</span> <span class="s">"address"</span> TEXT }
</span></span><span class="line"><span class="cl"> { <span class="s">"date"</span> <span class="s">"date"</span> INTEGER }
</span></span><span class="line"><span class="cl"> { <span class="s">"text"</span> <span class="s">"text"</span> TEXT }
</span></span><span class="line"><span class="cl"> { <span class="s">"flags"</span> <span class="s">"flags"</span> INTEGER }
</span></span><span class="line"><span class="cl"> { <span class="s">"replace"</span> <span class="s">"replace"</span> INTEGER }
</span></span><span class="line"><span class="cl"> { <span class="s">"svc-center"</span> <span class="s">"svc_center"</span> TEXT }
</span></span><span class="line"><span class="cl"> { <span class="s">"group-id"</span> <span class="s">"group_id"</span> INTEGER }
</span></span><span class="line"><span class="cl"> { <span class="s">"association-id"</span> <span class="s">"association_id"</span> INTEGER }
</span></span><span class="line"><span class="cl"> { <span class="s">"height"</span> <span class="s">"height"</span> INTEGER }
</span></span><span class="line"><span class="cl"> { <span class="s">"ui-flags"</span> <span class="s">"UIFlags"</span> INTEGER }
</span></span><span class="line"><span class="cl"> { <span class="s">"version"</span> <span class="s">"version"</span> INTEGER }
</span></span><span class="line"><span class="cl"> { <span class="s">"subject"</span> <span class="s">"subject"</span> TEXT }
</span></span><span class="line"><span class="cl"> { <span class="s">"country"</span> <span class="s">"country"</span> TEXT }
</span></span><span class="line"><span class="cl"> { <span class="s">"headers"</span> <span class="s">"headers"</span> BLOB }
</span></span><span class="line"><span class="cl"> { <span class="s">"recipients"</span> <span class="s">"recipients"</span> BLOB }
</span></span><span class="line"><span class="cl"> { <span class="s">"read?"</span> <span class="s">"read"</span> INTEGER }
</span></span><span class="line"><span class="cl">} define-persistent
</span></span></code></pre></div><p>Using this, we can count the number of messages very simply:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">count-messages</span> <span class="nf">( -- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> T{ message } count-tuples <span class="k">;
</span></span></span></code></pre></div><p>Or, retrieve all the messages:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-messages</span> <span class="nf">( -- </span><span class="nv">messages</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> T{ message } select-tuples <span class="k">;
</span></span></span></code></pre></div><p>Or, retrieve all the messages we’ve sent:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">sent-messages</span> <span class="nf">( -- </span><span class="nv">messages</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> T{ message { flags <span class="m">3 </span>} } select-tuples <span class="k">;
</span></span></span></code></pre></div><p>Or, retrieve only the unread messages we’ve received:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">unread-messages</span> <span class="nf">( -- </span><span class="nv">messages</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> T{ message { flags <span class="m">2 </span>} { read? <span class="m">0 </span>} } select-tuples <span class="k">;
</span></span></span></code></pre></div><p>Or, retrieve the messages sent from a particular phone number:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">messages-from</span> <span class="nf">( </span><span class="nv">addr</span> <span class="nf">-- </span><span class="nv">messages</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> message <span class="nb">new swap </span>>>address select-tuples <span class="k">;
</span></span></span></code></pre></div><p>Or, using
<a href="https://re.factorcode.org/2011/04/group-by.html">group-by</a>,
retrieve your messages as groups of conversations:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-conversations</span> <span class="nf">( -- </span><span class="nv">conversations</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> all-messages [ group-id>> ] group-by <span class="k">;
</span></span></span></code></pre></div><p>Much more useful, and much cleaner, than using the raw SQL queries on
the database. The results are returned as a sequence of <code>message</code>
objects that can be manipulated directly within Factor.</p>
<h3 id="try-it">Try It</h3>
<p>You can use your new query tools, for example, to
<a href="https://re.factorcode.org/2011/03/google-charts.html">chart</a> the
number of messages you send or receive per day of the week:</p>
<p>
<img src="https://re.factorcode.org/images/2011-04-20-iphone-backups-messages.png" alt="" width="451" height="312" />
</p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/iphone-backup/iphone-backup.factor">GitHub</a>.</p>
Mail with GUI
https://re.factorcode.org/2011/04/mail-with-gui.html
Sun, 17 Apr 2011 11:01:00 -0700https://re.factorcode.org/2011/04/mail-with-gui.html<p>One of the examples that is used to demonstrate
<a href="https://factorcode.org">Factor</a> to new users is sending an e-mail. Using
the <a href="https://docs.factorcode.org/content/article-smtp.html">smtp</a>
vocabulary, all you need to send an email is:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <email>
</span></span><span class="line"><span class="cl"> { <span class="s">"[email protected]"</span> } >>to
</span></span><span class="line"><span class="cl"> <span class="s">"Up for lunch?"</span> >>subject
</span></span><span class="line"><span class="cl"> <span class="s">"At Tracy's."</span> >>body
</span></span><span class="line"><span class="cl"> send-email
</span></span></code></pre></div><blockquote>
<p><em>Note: you might need to <a href="https://docs.factorcode.org/content/article-smtp-gmail.html">configure your SMTP
settings</a>
before this works.</em></p>
</blockquote>
<h3 id="with-ui">with-ui</h3>
<p>That’s nice, but wouldn’t it be neat if we could use the
<a href="https://docs.factorcode.org/content/article-ui.html">UI</a> to build a
compose window that can send emails in a more graphical manner? Perhaps
something like this:</p>
<p>
<img src="https://re.factorcode.org/images/2011-04-17-mail-with-gui-compose.png" alt="" width="509" height="283" />
</p>
<p>We’re going to use words from several vocabularies:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">arrays</span> <span class="nn">colors.constants</span> <span class="nn">kernel</span> <span class="nn">smtp</span> <span class="nn">ui</span>
</span></span><span class="line"><span class="cl"><span class="nn">ui.commands</span> <span class="nn">ui.gadgets</span> <span class="nn">ui.gadgets.borders</span> <span class="nn">ui.gadgets.buttons</span>
</span></span><span class="line"><span class="cl"><span class="nn">ui.gadgets.editors</span> <span class="nn">ui.gadgets.labels</span> <span class="nn">ui.gadgets.scrollers</span>
</span></span><span class="line"><span class="cl"><span class="nn">ui.gadgets.tracks</span> <span class="nn">ui.pens.solid</span> <span class="k">;
</span></span></span></code></pre></div><p>We define a type of <a href="https://docs.factorcode.org/content/article-ui-track-layout.html">track
layout</a>
that holds <a href="https://docs.factorcode.org/content/article-ui.gadgets.editors.html">editor
gadgets</a>
for each of the fields we need to receive input for. We will set the
“To:” field to have focus first when the window is displayed.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">mail-gadget</span> < <span class="nc">track</span> <span class="nv">to</span> <span class="nv">subject</span> <span class="nv">body</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">mail-gadget</span> <span class="nf">focusable-child*</span> to>> <span class="k">;
</span></span></span></code></pre></div><p>We build the UI using words that layout each of the main features:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><to></span> <span class="nf">( </span><span class="nv">mail</span> <span class="nf">-- </span><span class="nv">gadget</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> to>> <span class="s">"To:"</span> label-on-left <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><subject></span> <span class="nf">( </span><span class="nv">mail</span> <span class="nf">-- </span><span class="nv">gadget</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> subject>> <span class="s">"Subject:"</span> label-on-left <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><body></span> <span class="nf">( </span><span class="nv">mail</span> <span class="nf">-- </span><span class="nv">gadget</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> body>> <scroller> COLOR: gray <solid> >>boundary <span class="k">;
</span></span></span></code></pre></div><p>Using the <a href="https://docs.factorcode.org/content/article-ui-commands.html">command
framework</a>,
we can create commands for “Send” and “Cancel” and configure it so a
<a href="https://docs.factorcode.org/content/word-__lt__toolbar__gt__%2Cui.gadgets.buttons.html">toolbar</a>
could be created automatically.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">com-send</span> <span class="nf">( </span><span class="nv">mail</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <email>
</span></span><span class="line"><span class="cl"> <span class="nb">over </span>to>> editor-string <span class="nb">1array </span>>>to
</span></span><span class="line"><span class="cl"> <span class="nb">over </span>subject>> editor-string >>subject
</span></span><span class="line"><span class="cl"> <span class="nb">over </span>body>> editor-string >>body
</span></span><span class="line"><span class="cl"> send-email close-window <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">com-cancel</span> <span class="nf">( </span><span class="nv">mail</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> close-window <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">mail-gadget <span class="s">"toolbar"</span> <span class="no">f </span>{
</span></span><span class="line"><span class="cl"> { <span class="no">f </span>com-send }
</span></span><span class="line"><span class="cl"> { <span class="no">f </span>com-cancel }
</span></span><span class="line"><span class="cl">} define-command-map
</span></span></code></pre></div><p>Finally, we make a word that creates our mail gadget:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><mail-gadget></span> <span class="nf">( -- </span><span class="nv">gadget</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> vertical mail-gadget new-track
</span></span><span class="line"><span class="cl"> <span class="m">1 </span>>>fill
</span></span><span class="line"><span class="cl"> { <span class="m">10 10 </span>} >>gap
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <editor> >>to
</span></span><span class="line"><span class="cl"> <editor> >>subject
</span></span><span class="line"><span class="cl"> <multiline-editor>
</span></span><span class="line"><span class="cl"> <span class="m">10 </span>>>min-rows
</span></span><span class="line"><span class="cl"> <span class="m">60 </span>>>min-cols
</span></span><span class="line"><span class="cl"> >>body
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><to <span class="no">f </span>track-add
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><subject> <span class="no">f </span>track-add
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><body> <span class="m">1 </span>track-add
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><toolbar> <span class="no">f </span>track-add <span class="k">;
</span></span></span></code></pre></div><p>And a simple word to open the gadget in a new “compose” window (with a
5-pixel border for aesthetic reasons):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">open-compose-window</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <mail-gadget>
</span></span><span class="line"><span class="cl"> { <span class="m">5 5 </span>} <border> { <span class="m">1 1 </span>} >>fill
</span></span><span class="line"><span class="cl"> <span class="s">"Compose"</span> open-window <span class="k">;
</span></span></span></code></pre></div><h3 id="bonus">Bonus</h3>
<p>You can even print the mail gadget out in the Listener to see how it
looks. <em>Note: it’s fully functional, so be careful clicking those
buttons!</em></p>
<p>
<img src="https://re.factorcode.org/images/2011-04-17-mail-with-gui-compose-in-listener.png" alt="" width="512" height="371" />
</p>
<p>Some things we could do to improve this simple example:</p>
<ul>
<li><a href="https://github.com/mrjbq7/re-factor/commit/c56a0f1ec072e2171336016b0be224e8278e883a">Implement TAB support to move easily between fields.</a></li>
<li>Popup error dialog if not all fields are filled out properly.</li>
<li>Better align the “To:” and “Subject:” text fields.</li>
<li><a href="https://github.com/mrjbq7/re-factor/commit/1c6136acef6fe76f63923935d43665a3d1004315">Support multiple “To:” addresses.</a></li>
<li>Prompt “Are you sure?” before closing the window when clicking “Cancel”.</li>
<li><a href="https://github.com/mrjbq7/re-factor/commit/2e7ea44dcbe9d2357993f0adb1892777d4ace845">Don’t close the Listener when clicking buttons (with “<code>gadget.</code>”).</a></li>
</ul>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/mail-ui/mail-ui.factor">GitHub</a>.</p>
XKCD
https://re.factorcode.org/2011/04/xkcd.html
Thu, 14 Apr 2011 09:41:00 -0700https://re.factorcode.org/2011/04/xkcd.html<p>Pretty much everyone loves Randall Munroe’s <a href="https://xkcd.com">XKCD</a>
comic. But, wouldn’t it be better if you could read it from your
<a href="https://factorcode.org">Factor</a> listener? I thought so too!</p>
<p>We are going to build something that lets you do this:</p>
<p>
<img src="https://re.factorcode.org/images/2011-04-14-xkcd-xkcd.png" alt="" width="512" height="182" />
</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">formatting</span> <span class="nn">http.client</span> <span class="nn">images.http</span> <span class="nn">images.viewer</span> <span class="nn">kernel</span>
</span></span><span class="line"><span class="cl"><span class="nn">regexp</span> <span class="nn">strings</span> <span class="k">;
</span></span></span></code></pre></div><p>We need a word that loads an XKCD comic webpage, extracts out of it the
URL to the comic, and then loads it into Factor as an image object (to
be
<a href="https://docs.factorcode.org/content/article-images.viewer.html">displayed</a>).
In this case, we build a <a href="https://docs.factorcode.org/content/article-regexp.html">regular
expression</a> to
look for the first URL that matches where he hosts his comic images.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">load-comic</span> <span class="nf">( </span><span class="nv">url</span> <span class="nf">-- </span><span class="nv">image</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> http-get <span class="nb">nip
</span></span></span><span class="line"><span class="cl"> <span class="s">R" https://imgs\.xkcd\.com/comics/[^\.]+\.(png|jpg)"</span>
</span></span><span class="line"><span class="cl"> first-match <span class="nb">>string </span>load-http-image <span class="k">;
</span></span></span></code></pre></div><p>Using this, we can display his latest comic strip:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">latest-comic.</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://xkcd.com"</span> load-comic image. <span class="k">;
</span></span></span></code></pre></div><p>Or, knowing that each comic is numbered, a specific comic from the
archive:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">comic.</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://xkcd.com/%s/"</span> sprintf load-comic image. <span class="k">;
</span></span></span></code></pre></div><p>Or, like the screenshot above, a random comic from the archives:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-comic.</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://dynamic.xkcd.com/random/comic/"</span> load-comic image. <span class="k">;
</span></span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/xkcd/xkcd.factor">GitHub</a>.</p>
Wikipedia over DNS
https://re.factorcode.org/2011/04/wikipedia-over-dns.html
Tue, 12 Apr 2011 08:47:00 -0700https://re.factorcode.org/2011/04/wikipedia-over-dns.html<p>A few days ago, I noticed a <a href="https://dgl.cx/wikipedia-dns">project</a> that
allowed querying Wikipedia over DNS. The way this works is to use the
sub-domain <code>query.wp.dg.cx</code> and parse the DNS “text record”, TXT, as
defined in <a href="https://tools.ietf.org/html/rfc1035#page-12">RFC 1035</a>.</p>
<p>For example, to query for the term “computer”:</p>
<pre tabindex="0"><code>$ host -t txt computer.wp.dg.cx
computer.wp.dg.cx descriptive text "A computer is a machine that mani
pulates data according to a list of instructions. The first devices t
hat resemble modern computers date to the mid-20th century (1940\226\
128\1471945), although the computer concept and various machines simi
lar to computers existed" " earlier. Early electronic computers were
the size of a large room, consuming as much power as several hundred
modern personal... https://a.vu/w:Computer"
</code></pre><p>Several months ago, Doug Coleman wrote the <code>dns</code> vocabulary to make DNS
requests from <a href="https://factorcode.org">Factor</a>. Using it, you can lookup
information for a hostname using a “pure Factor” version of
<a href="https://linux.die.net/man/1/host">host</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">tools.dns</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"microsoft.com"</span> host
</span></span><span class="line"><span class="cl">microsoft.com has address 207.46.197.32
</span></span><span class="line"><span class="cl">microsoft.com has address 207.46.232.182
</span></span><span class="line"><span class="cl">microsoft.com mail is handled by <span class="m">10 </span>mail.messaging.microsoft.com
</span></span></code></pre></div><p>Yesterday, I spoke to Doug about using the <code>dns</code> vocabulary with the
“Wikipedia over DNS” service. He discovered it required a few minor
changes to support DNS TXT queries:</p>
<ul>
<li><a href="https://github.com/factor/factor/commit/46fa62ce257be3876ad8bbc8ef7ccd7df0743093">Add support for parsing DNS TXT
queries.</a></li>
<li><a href="https://github.com/factor/factor/commit/8851c187762c18992feba240561ba7d639ca38c5">Decode TXT strings as utf8, use write instead of
print.</a></li>
<li><a href="https://github.com/factor/factor/commit/10260e528666871df91b35e1076dc1e987261685">Print a newline after each TXT message in
DNS.</a></li>
</ul>
<p>With a newer version of Factor that includes those changes, you can now
make these queries:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">dns</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"computer.wp.dg.cx"</span> TXT.
</span></span><span class="line"><span class="cl">A computer is a machine that manipulates data according to a list <span class="nb">of </span>
</span></span><span class="line"><span class="cl">instructions. The <span class="nb">first </span>devices that resemble modern computers date
</span></span><span class="line"><span class="cl">to the mid-20th century (1940–1945), although the computer concept a
</span></span><span class="line"><span class="cl">nd various machines similar to computers existed earlier. Early elec
</span></span><span class="line"><span class="cl">tronic computers were the size <span class="nb">of </span>a large room, consuming as much po
</span></span><span class="line"><span class="cl">wer as several hundred modern personal... https://a.vu/w:Computer
</span></span></code></pre></div>Group By
https://re.factorcode.org/2011/04/group-by.html
Fri, 08 Apr 2011 08:23:00 -0700https://re.factorcode.org/2011/04/group-by.html<p>When dealing with sequences, it can be useful to “group” them based on
some criteria into smaller sequences. I couldn’t find a word that quite
solved my problem in <a href="https://factorcode.org">Factor</a>, so I wrote
<code>group-by</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">assocs</span> <span class="nn">kernel</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">group-by</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">elt</span> <span class="nf">-- </span><span class="nv">key</span> <span class="nf">) -- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> H{ } <span class="nb">clone </span>[
</span></span><span class="line"><span class="cl"> [ <span class="nb">push-at </span>] <span class="nb">curry compose </span>[ <span class="nb">dup </span>] <span class="nb">prepose each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">keep </span><span class="k">; inline
</span></span></span></code></pre></div><h3 id="examples">Examples</h3>
<p>For example, we could use it to split the first 20 numbers into two
groups based on whether they are prime or not:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">math.primes</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">20 </span><iota> [ prime? ] group-by <span class="m">.
</span></span></span><span class="line"><span class="cl">H{
</span></span><span class="line"><span class="cl"> { <span class="no">f </span>V{ <span class="m">0 1 4 6 8 9 10 12 14 15 16 18 </span>} }
</span></span><span class="line"><span class="cl"> { <span class="no">t </span>V{ <span class="m">2 3 5 7 11 13 17 19 </span>} }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Or, we could group the subsets of a string by their length:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">math.combinatorics</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"abc"</span> all-subsets [ <span class="nb">length </span>] group-by <span class="m">.
</span></span></span><span class="line"><span class="cl">H{
</span></span><span class="line"><span class="cl"> { <span class="m">0 </span>V{ <span class="s">""</span> } }
</span></span><span class="line"><span class="cl"> { <span class="m">1 </span>V{ <span class="s">"a"</span> <span class="s">"b"</span> <span class="s">"c"</span> } }
</span></span><span class="line"><span class="cl"> { <span class="m">2 </span>V{ <span class="s">"ab"</span> <span class="s">"ac"</span> <span class="s">"bc"</span> } }
</span></span><span class="line"><span class="cl"> { <span class="m">3 </span>V{ <span class="s">"abc"</span> } }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Or, group some random numbers by their
<a href="https://docs.factorcode.org/content/word-bit-count,math.bitwise.html">bit-count</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">math.bitwise</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">20 </span>[ <span class="m">100 </span>random ] <span class="nb">replicate
</span></span></span><span class="line"><span class="cl"> [ bit-count ] group-by <span class="m">.
</span></span></span><span class="line"><span class="cl">H{
</span></span><span class="line"><span class="cl"> { <span class="m">1 </span>V{ <span class="m">32 1 32 4 </span>} }
</span></span><span class="line"><span class="cl"> { <span class="m">2 </span>V{ <span class="m">12 </span>} }
</span></span><span class="line"><span class="cl"> { <span class="m">3 </span>V{ <span class="m">13 74 56 98 44 </span>} }
</span></span><span class="line"><span class="cl"> { <span class="m">4 </span>V{ <span class="m">83 30 45 46 75 77 </span>} }
</span></span><span class="line"><span class="cl"> { <span class="m">5 </span>V{ <span class="m">61 59 61 </span>} }
</span></span><span class="line"><span class="cl"> { <span class="m">6 </span>V{ <span class="m">63 </span>} }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><h3 id="how-it-works">How it works</h3>
<p>The Factor code is roughly equivalent to the following
<a href="https://python.org">Python</a> code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">defaultdict</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">group_by</span><span class="p">(</span><span class="n">seq</span><span class="p">,</span> <span class="n">f</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">d</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">seq</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">key</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">d</span><span class="p">[</span><span class="n">key</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">d</span>
</span></span></code></pre></div><p>Let’s take it step-by-step. First, start defining a word (function)
called <code>group-by</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">group-by</span>
</span></span></code></pre></div><p>Next, define it to take two arguments, a
<a href="https://docs.factorcode.org/content/article-sequences.html">sequence</a>
(list or array) of values and a
<a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a>
(anonymous function or lambda) that computes a key for each element, and
outputs an
<a href="https://docs.factorcode.org/content/article-assocs.html">assoc</a>
(association, map, or dict). Names here are used only for documentation,
it could take a <code>foo</code> and <code>bar</code> and return a <code>baz</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="nf">( </span><span class="nv">seq</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">elt</span> <span class="nf">-- </span><span class="nv">key</span> <span class="nf">) -- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span></code></pre></div><p>The code inside the word is everything until the “<code>;</code>”. We want to
output a
<a href="https://docs.factorcode.org/content/article-hashtables.html">hashtable</a>,
so we first create one by
<a href="https://docs.factorcode.org/content/word-clone,kernel.html">cloning</a> an
empty hashtable (<code>H{ }</code>).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">H{ } <span class="nb">clone </span>
</span></span></code></pre></div><p>We will
<a href="https://docs.factorcode.org/content/word-compose,kernel.html">compose</a> a
word that
<a href="https://docs.factorcode.org/content/word-dup,kernel.html">duplicates</a>
each element to compute a key that is used to
<a href="https://docs.factorcode.org/content/word-push-at,assocs.html">push</a> each
element into an appropriate bucket (a
<a href="https://docs.factorcode.org/content/article-vectors.html">vector</a>) in
the hashtable. The <code>push-at</code> word has the signature
<code>( value key assoc -- )</code>. For example, if grouping by the length of a
string, we want something that looks a bit like
<code>[ dup length H{ } push-at ]</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">[ <span class="nb">push-at </span>] <span class="nb">curry compose </span>[ <span class="nb">dup </span>] <span class="nb">prepose
</span></span></span></code></pre></div><p>We then, apply this quotation to each element in the sequence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="nb">each
</span></span></span></code></pre></div><p>And, finally, we want to make sure that the hashtable that we created
isn’t “consumed”, but
<a href="https://docs.factorcode.org/content/word-keep,kernel.html">kept</a> on the
stack as a return value.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">[ ... ] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div>Verbosity
https://re.factorcode.org/2011/04/verbosity.html
Tue, 05 Apr 2011 15:40:00 -0700https://re.factorcode.org/2011/04/verbosity.html<p>One of my favorite features in the <a href="https://factorcode.org">Factor</a>
programming language is its general concise-ness. Certain problems can
be expressed in a very modest amount of code. Mind you, we’re not
talking about the extremes that some languages go through to be good at
<a href="https://codegolf.com/">Code Golf</a>, but minimized to some extent.</p>
<p>A
<a href="https://www.dreamincode.net/forums/topic/225686-generate-random-string-of-letters/">tutorial</a>
was posted a couple days ago that demonstrated how to write a program to
generate a “variable length string full of random characters”. I wanted
to contrast the solution in the tutorial with a simple version written
in Factor.</p>
<p><em>Note: this is not meant as a critique of the tutorial, nor a critique
of the <a href="https://en.wikipedia.org/wiki/C_Sharp_(programming_language)">C#</a>
programming language.</em></p>
<p>The finished “random string” class (with tutorial comments removed):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System.Text</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nn">RandomStringTutorial</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">public</span> <span class="k">class</span> <span class="nc">RandomString</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kt">char</span> <span class="n">c</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kt">int</span> <span class="n">n</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="n">Random</span> <span class="n">r</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Random</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="n">StringBuilder</span> <span class="n">sb</span> <span class="p">=</span> <span class="k">new</span> <span class="n">StringBuilder</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">public</span> <span class="kt">string</span> <span class="n">GenerateString</span><span class="p">(</span><span class="kt">int</span> <span class="n">Length</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p"><</span> <span class="n">Length</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">sb</span><span class="p">.</span><span class="n">Append</span><span class="p">(</span><span class="n">RandomChar</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">sb</span><span class="p">.</span><span class="n">ToString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kt">char</span> <span class="n">RandomChar</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">n</span> <span class="p">=</span> <span class="n">r</span><span class="p">.</span><span class="n">Next</span><span class="p">(</span><span class="m">65</span><span class="p">,</span> <span class="m">122</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="p">></span> <span class="m">65</span> <span class="p">&&</span> <span class="n">n</span> <span class="p"><</span> <span class="m">90</span> <span class="p">||</span> <span class="n">n</span> <span class="p">></span> <span class="m">97</span> <span class="p">&&</span> <span class="n">n</span> <span class="p"><</span> <span class="m">123</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">c</span> <span class="p">=</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToChar</span><span class="p">(</span><span class="n">n</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">RandomChar</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">c</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>And then a “main” method to run this as a program:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nn">RandomStringTutorial</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">class</span> <span class="nc">Program</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">static</span> <span class="k">void</span> <span class="n">Main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">RandomString</span> <span class="n">rs</span> <span class="p">=</span> <span class="k">new</span> <span class="n">RandomString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="n">Console</span><span class="p">.</span><span class="n">Write</span><span class="p">(</span><span class="n">rs</span><span class="p">.</span><span class="n">GenerateString</span><span class="p">(</span><span class="m">8</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"> <span class="n">Console</span><span class="p">.</span><span class="n">ReadLine</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Doing something similar in Factor, including a main word so that the
vocabulary could be deployed as a binary, or run from the command-line:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">io</span> <span class="nn">kernel</span> <span class="nn">literals</span> <span class="nn">ranges</span> <span class="nn">random</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">random-string</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">valid-chars</span> $[
</span></span><span class="line"><span class="cl"> <span class="sc">CHAR: A CHAR: Z </span>[a..b] <span class="sc">CHAR: a CHAR: z </span>[a..b] <span class="nb">append
</span></span></span><span class="line"><span class="cl">]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-string</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">string</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ valid-chars random ] <span class="s">""</span> <span class="nb">replicate-as </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">run-random-string</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="m">8 </span>random-string <span class="nb">print readln drop </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">run-random-string</span>
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/random-string/random-string.factor">GitHub</a>.</p>
Powers of 2
https://re.factorcode.org/2011/04/powers-of-2.html
Sat, 02 Apr 2011 08:21:00 -0700https://re.factorcode.org/2011/04/powers-of-2.html<p>I came across a <a href="https://www.skorks.com/2010/10/write-a-function-to-determine-if-a-number-is-a-power-of-2/">blog
post</a>
discussing an interview question for developers:</p>
<blockquote>
<p><em>“Write a function to determine if a number is a power of 2.”</em></p>
</blockquote>
<p>Subsequently, I noticed a <a href="https://stackoverflow.com/questions/600293/how-to-check-if-a-number-is-a-power-of-2">great
discussion</a>
on StackOverflow discussing methods of solving this problem, and another
<a href="https://www.exploringbinary.com/ten-ways-to-check-if-an-integer-is-a-power-of-two-in-c/">blog
post</a>
describing ten ways to do this in C. I’ve translated a few
implementations into Factor to contrast the various approaches. The
signature of the words we will create looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">power-of-2?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span></code></pre></div><p>And some basic test cases used to verify that it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{ <span class="no">t </span>} [ { <span class="m">1 2 4 1024 </span>} [ <span class="nb">power-of-2? </span>] <span class="nb">all? </span>] unit-test
</span></span><span class="line"><span class="cl">{ <span class="no">f </span>} [ { <span class="m">-1 0 3 1025 </span>} [ <span class="nb">power-of-2? </span>] <span class="nb">any? </span>] unit-test
</span></span></code></pre></div><h3 id="implementations">Implementations</h3>
<p>We can shift the number to the right, checking to see that the first odd
value observed is 1:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">shift-right/power-of-2?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb"><= </span>[ <span class="nb">drop </span><span class="no">f </span>] [ [ <span class="nb">dup even? </span>] [ <span class="nb">2/ </span>] <span class="nb">while </span><span class="m">1 </span><span class="nb">= </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Or, we can use a <a href="https://docs.factorcode.org/content/vocab-math.bits.html">virtual sequence of
bits</a> and count
the number of “on” bits (should be only 1):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bits/power-of-2?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb"><= </span>[ <span class="nb">drop </span><span class="no">f </span>] [ make-bits [ t? ] <span class="nb">count </span><span class="m">1 </span><span class="nb">= </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Or, we can compute the integer
<a href="https://docs.factorcode.org/content/word-log2,math.html">log2</a> raised to
the second power, and compare:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">log2/power-of-2?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb"><= </span>[ <span class="nb">drop </span><span class="no">f </span>] [ <span class="nb">dup log2 2^ = </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Or, we can calculate the
<a href="https://docs.factorcode.org/content/word-next-power-of-2,math.html">next-power-of-2</a>,
and compare:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-power/power-of-2?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">1 </span><span class="nb">= </span>[ <span class="nb">drop </span><span class="no">t </span>] [ <span class="nb">dup next-power-of-2 = </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Or, we can compare the number with its <a href="https://en.wikipedia.org/wiki/Two%27s_complement">two’s
complement</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">complement/power-of-2?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb"><= </span>[ <span class="nb">drop </span><span class="no">f </span>] [ <span class="nb">dup dup neg bitand = </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Or, we can decrement the number and compare it with the original:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">decrement/power-of-2?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb"><= </span>[ <span class="nb">drop </span><span class="no">f </span>] [ <span class="nb">dup </span><span class="m">1 </span><span class="nb">- bitand zero? </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Or, we can define a lookup table (using the
<a href="https://docs.factorcode.org/content/vocab-literals.html">literals</a>
vocabulary to define the table at compile time) holding all possible
64-bit powers of 2 (restricting the range of valid inputs to 64-bits):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">POWERS-OF-2</span> $[ <span class="m">64 </span><iota> [ <span class="nb">2^ </span>] <span class="nb">map </span>]
</span></span></code></pre></div><p>Using this, we can check a given number against all the values in the
lookup table:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">check-all/power-of-2?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> POWERS-OF-2 <span class="nb">member? </span><span class="k">;
</span></span></span></code></pre></div><p>Or, we can do a linear search, stopping when we see numbers too large:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">linear-search/power-of-2?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> POWERS-OF-2 <span class="nb">over </span>[ <span class="nb">>= </span>] <span class="nb">curry find nip = </span><span class="k">;
</span></span></span></code></pre></div><p>Or, knowing that the lookup table is sorted, we can do a binary search:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">binary-search/power-of-2?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> POWERS-OF-2 sorted-member? <span class="k">;
</span></span></span></code></pre></div><p>Or, we can compute a
<a href="https://docs.factorcode.org/content/article-hash-sets.html">hash-set</a>
(at compile time), and check for membership:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">hash-search/power-of-2?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> $[ POWERS-OF-2 fast-set ] in? <span class="k">;
</span></span></span></code></pre></div><p>Or, we can use the integer
<a href="https://docs.factorcode.org/content/word-log2,math.html">log2</a> as an
index into the lookup table.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">log-search/power-of-2?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb"><= </span>[ <span class="nb">drop </span><span class="no">f </span>] [ <span class="nb">dup log2 </span>POWERS-OF-2 <span class="nb">nth = </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><h3 id="testing">Testing</h3>
<p>We can make a list of all our implementations:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">IMPLEMENTATIONS</span> {
</span></span><span class="line"><span class="cl"> shift-right/power-of-2?
</span></span><span class="line"><span class="cl"> bits/power-of-2?
</span></span><span class="line"><span class="cl"> log2/power-of-2?
</span></span><span class="line"><span class="cl"> next-power/power-of-2?
</span></span><span class="line"><span class="cl"> complement/power-of-2?
</span></span><span class="line"><span class="cl"> decrement/power-of-2?
</span></span><span class="line"><span class="cl"> check-all/power-of-2?
</span></span><span class="line"><span class="cl"> linear-search/power-of-2?
</span></span><span class="line"><span class="cl"> binary-search/power-of-2?
</span></span><span class="line"><span class="cl"> hash-search/power-of-2?
</span></span><span class="line"><span class="cl"> log-search/power-of-2?
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>And then test their functionality:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">test-power-of-2</span> <span class="nf">( -- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> IMPLEMENTATIONS [
</span></span><span class="line"><span class="cl"> 1quotation [ call( n -- <span class="nb">? </span>) ] <span class="nb">curry
</span></span></span><span class="line"><span class="cl"> [ { <span class="m">1 2 4 1024 </span>} <span class="nb">swap all? </span>]
</span></span><span class="line"><span class="cl"> [ { <span class="m">-1 0 3 1025 </span>} <span class="nb">swap any? not </span>] <span class="nb">bi and
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">all? </span><span class="k">;
</span></span></span></code></pre></div><p>Sure enough, they seem to work:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> test-power-of-2 <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">t
</span></span></span></code></pre></div><h3 id="performance">Performance</h3>
<p>We can benchmark the performance of the various implementations
operating on 1,000,000 random 32-bit numbers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">bench-power-of-2</span> <span class="nf">( -- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> IMPLEMENTATIONS randomize <span class="m">20 </span><span class="nb">2^ </span>[ random-32 ] <span class="nb">replicate </span>'[
</span></span><span class="line"><span class="cl"> [ name>> <span class="s">"/"</span> split1 <span class="nb">drop </span>] [
</span></span><span class="line"><span class="cl"> 1quotation [ <span class="nb">drop </span>] <span class="nb">compose
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">each </span>] <span class="nb">curry </span>[ _ ] <span class="nb">prepose
</span></span></span><span class="line"><span class="cl"> nano-count [ call( -- ) nano-count ] <span class="nb">dip -
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] { } <span class="nb">map>assoc </span><span class="k">;
</span></span></span></code></pre></div><p>Running the benchmark, we see that <code>log2/power-of-2?</code> is the (slightly)
fastest version:</p>
<p>
<img src="https://re.factorcode.org/images/2011-04-02-powers-of-2-power-of-2.png" alt="" width="512" height="326" />
</p>
<blockquote>
<p><em>The raw numbers from one of my benchmark runs:</em></p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> bench-power-of-2 sort-values <span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> { <span class="s">"log2"</span> <span class="m">118107290 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"complement"</span> <span class="m">119691428 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"decrement"</span> <span class="m">121455742 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"log-search"</span> <span class="m">122799186 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"next-power"</span> <span class="m">127366447 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"shift-right"</span> <span class="m">137695485 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"binary-search"</span> <span class="m">204224141 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"check-all"</span> <span class="m">267042396 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"hash-search"</span> <span class="m">269629705 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"linear-search"</span> <span class="m">280441186 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"bits"</span> <span class="m">1112186059 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><h3 id="improvement">Improvement</h3>
<p>But, can we do better? We have already created a faster implementation
than the <code>math</code> vocabulary, which defines
<a href="https://docs.factorcode.org/content/word-power-of-2__que__,math.html">power-of-2?</a>
using “decrement”. Focusing on that implementation, perhaps we can still
introduce some improvements.</p>
<p>We can do less work, by exiting early using a <a href="https://docs.factorcode.org/content/article-combinators.short-circuit.html">short-circuit
combinator</a>
if the first test fails:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">decrement+short/power-of-2?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">1 </span><span class="nb">- bitand zero? </span>] [ <span class="m">0 </span><span class="nb">> </span>] } 1&& <span class="k">;
</span></span></span></code></pre></div><p>Or, we can add <a href="https://docs.factorcode.org/content/article-typed.html">type
information</a>,
assuming only fixnum values (restricting our possible input values to a
60-bit number between -576,460,752,303,423,488 and
576,460,752,303,423,487):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TYPED:</span> <span class="nf">decrement+typed/power-of-2?</span> <span class="nf">( </span><span class="nv">n:</span> <span class="nv">fixnum</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb"><= </span>[ <span class="nb">drop </span><span class="no">f </span>] [ <span class="nb">dup </span><span class="m">1 </span><span class="nb">- bitand zero? </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Or, if we are okay with restricting the input values, we can try writing
it in C:</p>
<p>1. Build a simple C function in <code>power-of-2.c</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf"><stdint.h></span><span class="cp">
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int64_t</span> <span class="nf">isPowerOfTwo</span> <span class="p">(</span><span class="kt">int64_t</span> <span class="n">x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">((</span><span class="n">x</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span> <span class="o">&&</span> <span class="p">((</span><span class="n">x</span> <span class="o">&</span> <span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span> <span class="o">==</span> <span class="mi">0</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>2. Build a C library we can use :</p>
<pre tabindex="0"><code>$ cc -fno-common -c power-of-2.c
$ cc -dynamiclib -install_name power-of-2.dylib \
-o power-of-2.dylib power-of-2.o
$ sudo mv power-of-2.dylib /usr/local/lib
</code></pre><p>3. Wrap the C library from Factor (using the
<a href="https://docs.factorcode.org/content/article-alien.html">alien</a>
vocabulary):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">alien</span> <span class="nn">alien.c-types</span> <span class="nn">alien.syntax</span> <span class="nn">alien.libraries</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="s">"libpowerof2"</span> <span class="s">"power-of-2.dylib"</span> cdecl add-library
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">LIBRARY: libpowerof2
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> int <span class="nf">isPowerOfTwo</span> ( int x )
</span></span></code></pre></div><p>4. And, finally, build a Factor word that uses it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">decrement+alien/power-of-2?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> isPowerOfTwo <span class="m">1 </span><span class="nb">= </span><span class="k">;
</span></span></span></code></pre></div><p>Running the benchmarks shows the typed version only slightly beating the
short-circuit version, with a roughly 10% improvement:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> { <span class="s">"decrement+typed"</span> <span class="m">111711456 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"decrement+short"</span> <span class="m">112070520 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"decrement+alien"</span> <span class="m">113014058 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"decrement"</span> <span class="m">123256748 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Given that we want some ability to generalize our function to all
integer inputs, I’d be happy declaring <code>decrement+short/power-of-2?</code> the
“winner”. Can you do better?</p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/power-of-2/power-of-2.factor">GitHub</a>.</p>
Unique Hash
https://re.factorcode.org/2011/03/unique-hash.html
Mon, 28 Mar 2011 18:37:00 -0700https://re.factorcode.org/2011/03/unique-hash.html<p>Recently, I stumbled onto a <a href="https://blog.kevburnsjr.com/php-unique-hash">blog
post</a> from 2009 which
discussed a way to generate random-looking strings from a series of
unique numeric id’s, using PHP. The author uses a base-62 encoding
(<code>[0-9][A-Z][a-z]</code>) to convert a number to a unique string using a
series of prime numbers to reduce the potential of collisions. Below, I
show how it might look in <a href="https://www.factorcode.org">Factor</a>.</p>
<p>First, we implement a simple base-62 encoder:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">CHARS</span>
</span></span><span class="line"><span class="cl"><span class="s">"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">base62</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">string</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span><span class="m">0 </span><span class="nb">> </span>] [ <span class="m">62 </span><span class="nb">/mod </span>CHARS <span class="nb">nth </span>] <span class="s">""</span> <span class="nb">produce-as reverse nip </span><span class="k">;
</span></span></span></code></pre></div><p>Next, we define a series of prime numbers (which should be kept “secret”
to make the algorithm hard to predict) chosen to be close to the <em>next
highest prime from the golden mean of the number of possible
permutations for each string length</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">PRIMES</span>
</span></span><span class="line"><span class="cl">{ <span class="m">1 41 2377 147299 9132313 566201239 35104476161 2176477521929 </span>}
</span></span></code></pre></div><p>Finally, we can implement the “unique hash” function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">udihash</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">chars</span> <span class="nf">-- </span><span class="nv">string</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> chars PRIMES <span class="nb">nth </span>n <span class="nb">* </span><span class="m">62 </span>chars ^ <span class="nb">mod </span>base62
</span></span><span class="line"><span class="cl"> chars <span class="sc">CHAR: 0 </span><span class="nb">pad-head </span><span class="k">;
</span></span></span></code></pre></div><p>Try it out and see that it produces the same random-looking results as
the original author. For example, a 5-character hash of the numbers 1
through 10:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 </span>[1..b] [ <span class="m">5 </span>udihash <span class="nb">print </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl">cJio3
</span></span><span class="line"><span class="cl">EdRc6
</span></span><span class="line"><span class="cl">qxAQ9
</span></span><span class="line"><span class="cl">TGtEC
</span></span><span class="line"><span class="cl">5ac2F
</span></span><span class="line"><span class="cl">huKqI
</span></span><span class="line"><span class="cl">KE3eL
</span></span><span class="line"><span class="cl">wXmSO
</span></span><span class="line"><span class="cl">YrVGR
</span></span><span class="line"><span class="cl">BBE4U
</span></span></code></pre></div><p>You can find a
<a href="https://stackoverflow.com/questions/959957/php-short-hash">discussion</a>
on StackOverflow, a similar approach used in the <a href="https://redmine.polemic.net.nz/projects/url-shortener/repository/revisions/151/annotate/trunk/code/SSURLShortenerObject.php">SilverStripe
CMS</a>
to shorten URLs, and lots of <a href="https://www.google.com/search?as_q=php-unique-hash">search
results</a> probably
inspiring (or inspired by) the original blog post.</p>
<p>If you’re curious how to select your prime numbers, you can use
<a href="https://docs.factorcode.org/content/vocab-math.primes.html">math.primes</a>
to create your own list:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="k">CONSTANT:</span> <span class="nf">golden-ratio</span> <span class="m">1.618033988749894848
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">5 </span>[1..b] [
</span></span><span class="line"><span class="cl"> <span class="m">62 </span><span class="nb">swap </span>^ golden-ratio <span class="nb">/i </span>next-prime
</span></span><span class="line"><span class="cl"> ] <span class="nb">map </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">41 2377 147299 9132313 566201239 </span>}
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/pseudo-crypt/pseudo-crypt.factor">GitHub</a>.</p>
sum
https://re.factorcode.org/2011/03/sum.html
Fri, 25 Mar 2011 12:27:00 -0700https://re.factorcode.org/2011/03/sum.html<p>Today’s <a href="https://programmingpraxis.com/2011/03/25/sum/">programming
challenge</a> is to implement
the “old Unix Sys V R4” <code>sum</code> command:</p>
<blockquote>
<p><em>“The original sum calculated a checksum as the sum of the bytes in
the file, modulo 2<sup>16</sup>−1, as well as the number of 512-byte
blocks the file occupied on disk. Called with no arguments, sum read
standard input and wrote the checksum and file blocks to standard
output; called with one or more filename arguments, sum read each file
and wrote for each a line containing the checksum, file blocks, and
filename.”</em></p>
</blockquote>
<p>First, some imports:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">command-line</span> <span class="nn">formatting</span> <span class="nn">io</span> <span class="nn">io.encodings.binary</span> <span class="nn">io.files</span>
</span></span><span class="line"><span class="cl"><span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">math.functions</span> <span class="nn">namespaces</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span></code></pre></div><h3 id="short-version">Short Version</h3>
<p>A quick file-based version might look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">sum-file.</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> binary file-contents
</span></span><span class="line"><span class="cl"> [ <span class="nb">sum </span><span class="m">65535 </span><span class="nb">mod </span>] [ <span class="nb">length </span><span class="m">512 </span><span class="nb">/ </span>ceiling ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] [ <span class="s">"%d %d %s\n"</span> printf ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>You can try it out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/usr/share/dict/words"</span> sum-file.
</span></span><span class="line"><span class="cl"><span class="m">19278 4858 </span>/usr/share/dict/words
</span></span></code></pre></div><p>The main drawbacks to this version are: loading the entire file into
memory (which might be a problem for big files), not printing an error
if the file is not found, and not supporting standard input.</p>
<h3 id="full-version">Full Version</h3>
<p>A more complete version might begin by implementing a function that
reads from a stream, computing the checksum and the number of 512-byte
blocks:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">sum-stream</span> <span class="nf">( -- </span><span class="nv">checksum</span> <span class="nv">blocks</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 0 </span>[ <span class="m">65536 </span><span class="nb">read-partial dup </span>] [
</span></span><span class="line"><span class="cl"> [ <span class="nb">sum nip + </span>] [ <span class="nb">length + nip </span>] <span class="nb">3bi
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while drop </span>[ <span class="m">65535 </span><span class="nb">mod </span>] [ <span class="m">512 </span><span class="nb">/ </span>ceiling ] <span class="nb">bi* </span><span class="k">;
</span></span></span></code></pre></div><p>The output should look like <code>CHECKSUM BLOCKS FILENAME</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">sum-stream.</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ sum-stream ] <span class="nb">dip </span><span class="s">"%d %d %s\n"</span> printf <span class="k">;
</span></span></span></code></pre></div><p>We can generate output for a particular file (printing
<code>FILENAME: not found</code> if the file does not exist):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">sum-file.</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>exists? [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>binary [ sum-stream. ] with-file-reader
</span></span><span class="line"><span class="cl"> ] [ <span class="s">"%s: not found\n"</span> printf ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>And, to prepare a version of <code>sum</code> that we can deploy as a binary and
run from the command line, we build a simple MAIN: word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">run-sum</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> command-line <span class="nb">get </span>[ <span class="s">""</span> sum-stream. ] [
</span></span><span class="line"><span class="cl"> [ sum-file. ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-empty </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">run-sum</span>
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/sum/sum.factor">GitHub</a>.</p>
Look and Say
https://re.factorcode.org/2011/03/look-and-say.html
Wed, 23 Mar 2011 10:55:00 -0700https://re.factorcode.org/2011/03/look-and-say.html<p>There was a <a href="https://programmingpraxis.com/2011/03/15/look-and-say/">programming
challenge</a> a week
ago that asked for solutions that create the <a href="https://mathworld.wolfram.com/LookandSaySequence.html">Look and
Say</a> sequence.
Below is an implementation in <a href="https://factorcode.org">Factor</a>.</p>
<blockquote>
<p><em>The “Look and Say” sequence, Sloane number A005150, begins 1, 11, 21,
1211, 111221, 312211, 13112221, 1113213211, …. Each term is
constructed from its predecessor by stating the frequency and number
of each group of like digits. For instance, the term after 1211 is
“one 1, one 2, and two 1s”, or 111221.</em></p>
</blockquote>
<p>We can use the
<a href="https://docs.factorcode.org/content/vocab-splitting.monotonic.html">splitting.monotonic</a>
vocabulary to split a number, recombine it based on the length of
intermediate sequences of digits, and produce the next value in a “Look
and Say” sequence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">formatting</span> <span class="nn">kernel</span> <span class="nn">math.parser</span> <span class="nn">sequences</span> <span class="nn">splitting.monotonic</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">look-and-say</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">n'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> number>string [ <span class="nb">= </span>] monotonic-split [
</span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>] [ <span class="nb">first </span>] <span class="nb">bi </span><span class="s">"%d%c"</span> sprintf
</span></span><span class="line"><span class="cl"> ] <span class="nb">map concat </span>string>number <span class="k">;
</span></span></span></code></pre></div><p>You can try it out in the Listener to see the first 10 numbers in the
sequence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1 10 </span>[ <span class="nb">dup </span><span class="m">. </span>look-and-say ] <span class="nb">times
</span></span></span><span class="line"><span class="cl"><span class="m">1
</span></span></span><span class="line"><span class="cl"><span class="m">11
</span></span></span><span class="line"><span class="cl"><span class="m">21
</span></span></span><span class="line"><span class="cl"><span class="m">1211
</span></span></span><span class="line"><span class="cl"><span class="m">111221
</span></span></span><span class="line"><span class="cl"><span class="m">312211
</span></span></span><span class="line"><span class="cl"><span class="m">13112221
</span></span></span><span class="line"><span class="cl"><span class="m">1113213211
</span></span></span><span class="line"><span class="cl"><span class="m">31131211131221
</span></span></span><span class="line"><span class="cl"><span class="m">13211311123113112211
</span></span></span></code></pre></div>Names of Gaddafi
https://re.factorcode.org/2011/03/names-of-gaddafi.html
Mon, 21 Mar 2011 11:12:00 -0700https://re.factorcode.org/2011/03/names-of-gaddafi.html<p>A
<a href="https://stackoverflow.com/questions/5365283/regular-expression-to-search-for-gadaffi">question</a>
was posted yesterday on StackOverflow asking how to create a regular
expression to search for the <a href="https://www.express.be/joker/nl/platdujour/gaddafi-khadaffi-el-qadafi-kadhafy/141157.htm">many
names</a>
of Libyan leader, Muammar al-Gaddafi. I thought it would be fun to
explore this problem using <a href="https://factorcode.org">Factor</a>.</p>
<p>Someone helpfully posted an
<a href="https://upload.wikimedia.org/math/6/1/f/61f34aa25871e9546b6a11243e1bed31.png">image</a>
that demonstrates some of the variations of his full name:</p>
<p>
<img src="https://re.factorcode.org/images/2011-03-21-names-of-gaddafi-61f34aa25871e9546b6a11243e1bed31.png" alt="" width="689" height="227" />
</p>
<h3 id="list-of-names">List of Names</h3>
<p>One approach would be to list out all his possible names, and then check
to see if a given string is in the list:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">names</span> {
</span></span><span class="line"><span class="cl"> <span class="s">"Gadaffi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Gadafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Gadafy"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Gaddafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Gaddafy"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Gaddhafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Gadhafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Gathafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Ghadaffi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Ghadafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Ghaddafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Ghaddafy"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Gheddafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Kadaffi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Kadafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Kad'afi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Kaddafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Kadhafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Kazzafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Khadaffy"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Khadafy"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Khaddafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Qadafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Qaddafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Qadhafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Qadhaafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Qadhdhafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Qadthafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Qathafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Quathafi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Qudhafi"</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">gaddafi?</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> names <span class="nb">member? </span><span class="k">;
</span></span></span></code></pre></div><h3 id="regular-expressions">Regular Expressions</h3>
<p>If we wanted to speed it up, we could build a <a href="https://docs.factorcode.org/content/article-regexp.html">regular
expression</a> from
all of the names (using
<a href="https://docs.factorcode.org/content/vocab-literals.html">literals</a> to
build the regular expression at compile time):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USE:</span> <span class="nn">regexp</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">gaddafi?</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> $[ names <span class="s">"|"</span> <span class="nb">join </span><regexp> ] matches? <span class="k">;
</span></span></span></code></pre></div><blockquote>
<p><em>Note: the first time you call this method it needs to compile the
regular expression and is a bit slow. Subsequent calls are much
faster.</em></p>
</blockquote>
<p>One problem with that, is that it doesn’t take into account that
sometimes he is called “Al Qaddafi” or “el-Gaddafi”. We could create our
own case-insensitive pattern that attempts to capture all the possible
variations of his name:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">names-pattern</span>
</span></span><span class="line"><span class="cl">R/ ((al|el)[-\s]?)?(Kh?|Gh?|Qu?)[aeu](d['dt]?|t|zz|dhd)h?aa?ff?[iy]/i
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">gaddafi?</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> names-pattern matches? <span class="k">;
</span></span></span></code></pre></div><p>An advantage of using regular expressions is that it is easy to take a
piece of text and normalize all the mentions of his name:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">normalize-gaddafi</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">string'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> names-pattern <span class="s">"Gaddafi"</span> re-replace <span class="k">;
</span></span></span></code></pre></div><h3 id="soundex">Soundex</h3>
<p>An interesting idea was proposed on the <a href="https://news.ycombinator.com/item?id=2349389">Hacker News
discussion</a> to use
<a href="https://en.wikipedia.org/wiki/Soundex">Soundex</a> to match names. That
might look something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USE:</span> <span class="nn">soundex</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">gaddafi?</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> soundex { <span class="s">"G310"</span> <span class="s">"K310"</span> <span class="s">"Q310"</span> <span class="s">"Q331"</span> } <span class="nb">member? </span><span class="k">;
</span></span></span></code></pre></div><p>Some disadvantages of this is that it doesn’t capture names with prefix
“Al” or “El”, and misses some names (e.g., “Kazzafi” has a soundex value
of “K210”, but that would produce a false match against a name like
“KOSOFF”).</p>
<p>This problem is made even harder if you want to include all the possible
variations of his first name (e.g., Muammar, Moamar, Mo’ammar, Mu’amar,
Moamma, etc.), and include text that is “<code>FIRSTNAME LASTNAME</code>” or
“<code>LASTNAME, FIRSTNAME</code>”.</p>
Typed Netstrings
https://re.factorcode.org/2011/03/typed-netstrings.html
Sun, 20 Mar 2011 14:24:00 -0700https://re.factorcode.org/2011/03/typed-netstrings.html<p>A few hours ago, Zed Shaw
<a href="https://twitter.com/#!/zedshaw/status/49539537390669825">tweeted</a> about
an experiment creating typed netstrings:</p>
<p>An experiment in tagging netstrings with the types of their data as an
alternative to JSON: <a href="https://codepad.org/xct0E5ac"
rel="nofollow"><a href="https://codepad.org/xct0E5ac">https://codepad.org/xct0E5ac</a></a><span
class="timestamp"><a href="https://twitter.com/#!/zedshaw/status/49539537390669825" title="Sun Mar 20 18:35:20 +0000 2011">less than a minute
ago</a>
via web</span><span class="metadata"><span
class="author"><a href="https://twitter.com/zedshaw"></a><strong><a href="https://twitter.com/zedshaw">zedshaw</a></strong><br>
zedshaw</span></span></p>
<p>I thought that an implementation in <a href="https://www.factorcode.org">Factor</a>
would contrast nicely with Zed’s <a href="https://codepad.org/xct0E5ac">Python</a>
version. The basic idea is to support four kinds of data types:</p>
<ul>
<li>Text</li>
<li>Numbers</li>
<li>Lists</li>
<li>Dictionaries (e.g., maps or associations)</li>
</ul>
<p>First, some imports:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">arrays</span> <span class="nn">combinators</span> <span class="nn">formatting</span> <span class="nn">hashtables</span> <span class="nn">kernel</span>
</span></span><span class="line"><span class="cl"><span class="nn">math.parser</span> <span class="nn">sequences</span> <span class="nn">splitting</span> <span class="k">;
</span></span></span></code></pre></div><p>An encoded payload value looks something like
“<code>{LENGTH}:{PAYLOAD}{TYPE}</code>”. We can write a simple word to parse a
string that looks like that into its parts:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-payload</span> <span class="nf">( </span><span class="nv">data</span> <span class="nf">-- </span><span class="nv">remain</span> <span class="nv">payload</span> <span class="nv">payload-type</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">":"</span> split1 <span class="nb">swap </span>string>number <span class="nb">cut unclip swapd </span><span class="k">;
</span></span></span></code></pre></div><p>We can build a simple parser for these “typed netstrings” (deferring for
the moment, how we parse lists and dictionaries):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">DEFER:</span> <span class="nf">parse-dict</span>
</span></span><span class="line"><span class="cl"><span class="kn">DEFER:</span> <span class="nf">parse-list</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-tnetstring</span> <span class="nf">( </span><span class="nv">data</span> <span class="nf">-- </span><span class="nv">remain</span> <span class="nv">value</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> parse-payload {
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: # </span>[ string>number ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: " </span>[ ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: } </span>[ parse-dict ] }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: ] </span>[ parse-list ] }
</span></span><span class="line"><span class="cl"> [ <span class="s">"Invalid payload type: %c"</span> sprintf <span class="nb">throw </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div><p>Parsing lists is just repeatedly parsing values until the remainder is
exhausted:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-list</span> <span class="nf">( </span><span class="nv">data</span> <span class="nf">-- </span><span class="nv">value</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ { } ] [
</span></span><span class="line"><span class="cl"> [ <span class="nb">dup empty? not </span>] [ parse-tnetstring ] <span class="nb">produce nip
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if-empty </span><span class="k">;
</span></span></span></code></pre></div><p>Parsing dictionaries is only a little more involved. We need a way to
parse successive key/value pairs, checking some simple error conditions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-pair</span> <span class="nf">( </span><span class="nv">data</span> <span class="nf">-- </span><span class="nv">extra</span> <span class="nv">value</span> <span class="nv">key</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> parse-tnetstring [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ <span class="s">"Unbalanced dictionary store"</span> <span class="nb">throw </span>] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> parse-tnetstring
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ <span class="s">"Invalid value, null not allowed"</span> <span class="nb">throw </span>] <span class="nb">unless
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">dip </span><span class="k">;
</span></span></span></code></pre></div><p>Then we can build the dictionary, repeatedly parsing key/value pairs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-dict</span> <span class="nf">( </span><span class="nv">data</span> <span class="nf">-- </span><span class="nv">value</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ H{ } ] [
</span></span><span class="line"><span class="cl"> [ <span class="nb">dup empty? not </span>] [ parse-pair <span class="nb">swap 2array </span>] <span class="nb">produce
</span></span></span><span class="line"><span class="cl"> <span class="nb">nip </span>>hashtable
</span></span><span class="line"><span class="cl"> ] <span class="nb">if-empty </span><span class="k">;
</span></span></span></code></pre></div><p>And, to make the interface easy to use, we wrap our <code>parse-tnetstring</code>
word, checking that there was no un-parsed remainder:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">tnetstring</span> <span class="nf">( </span><span class="nv">data</span> <span class="nf">-- </span><span class="nv">value</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> parse-tnetstring <span class="nb">swap </span>[
</span></span><span class="line"><span class="cl"> <span class="s">"Had trailing junk: %s"</span> sprintf <span class="nb">throw
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">unless-empty </span><span class="k">;
</span></span></span></code></pre></div><p>We can show that it works on one of Zed’s more complex examples:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"34:5:hello\"22:11:12345678901#4:this\"]}"</span> tnetstring <span class="m">.
</span></span></span><span class="line"><span class="cl">H{ { <span class="s">"hello"</span> { <span class="m">12345678901 </span><span class="s">"this"</span> } } }
</span></span></code></pre></div><p>The code (and some tests) for this is on
<a href="https://github.com/mrjbq7/re-factor/blob/master/tnetstrings/tnetstrings.factor">GitHub</a>.</p>
<p><em>Update: I added support for booleans and implemented writer words to
match the reader words above. Everything is on my GitHub.</em></p>
Square?
https://re.factorcode.org/2011/03/square.html
Fri, 18 Mar 2011 09:24:00 -0700https://re.factorcode.org/2011/03/square.html<p>A recent <a href="https://codegolf.stackexchange.com/questions/1487/determine-if-4-points-form-a-square">code
golf</a>
requested a way to determine if 4 points form a square.
<a href="https://factorcode.org">Factor</a> may not be the shortest answer, but I
thought I would contribute it anyway.</p>
<p>We require four unique points to be provided. We then use the <a href="https://www.purplemath.com/modules/distform.htm">distance
formula</a> to compute the
distances between all pairs of points. For a square, there should be
just two lengths (the side and the diagonal) not counting zero (the
distance from a point to itself).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">math.combinatorics</span> <span class="nn">math.vectors</span> <span class="nn">sequences</span> <span class="nn">sets</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">square?</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> members [ <span class="nb">length </span><span class="m">4 </span><span class="nb">= </span>] [
</span></span><span class="line"><span class="cl"> <span class="m">2 </span>[ <span class="nb">first2 </span>v- [ <span class="nb">sq </span>] <span class="nb">map-sum </span>] map-combinations
</span></span><span class="line"><span class="cl"> { <span class="m">0 </span>} diff <span class="nb">length </span><span class="m">2 </span><span class="nb">=
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">bi and </span><span class="k">;
</span></span></span></code></pre></div><p>We can write some unit tests to make sure it works.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USE:</span> <span class="nn">tools.test</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">[ <span class="no">t </span>] [
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { { <span class="m">0 0 </span>} { <span class="m">0 1 </span>} { <span class="m">1 1 </span>} { <span class="m">1 0 </span>} } <span class="c">! standard square</span>
</span></span><span class="line"><span class="cl"> { { <span class="m">0 0 </span>} { <span class="m">2 1 </span>} { <span class="m">3 -1 </span>} { <span class="m">1 -2 </span>} } <span class="c">! non-axis-aligned square</span>
</span></span><span class="line"><span class="cl"> { { <span class="m">0 0 </span>} { <span class="m">1 1 </span>} { <span class="m">0 1 </span>} { <span class="m">1 0 </span>} } <span class="c">! different order</span>
</span></span><span class="line"><span class="cl"> { { <span class="m">0 0 </span>} { <span class="m">0 4 </span>} { <span class="m">2 2 </span>} { <span class="m">-2 2 </span>} } <span class="c">! rotated square</span>
</span></span><span class="line"><span class="cl"> } [ square? ] <span class="nb">all?
</span></span></span><span class="line"><span class="cl">] unit-test
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">[ <span class="no">f </span>] [
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { { <span class="m">0 0 </span>} { <span class="m">0 2 </span>} { <span class="m">3 2 </span>} { <span class="m">3 0 </span>} } <span class="c">! rectangle</span>
</span></span><span class="line"><span class="cl"> { { <span class="m">0 0 </span>} { <span class="m">3 4 </span>} { <span class="m">8 4 </span>} { <span class="m">5 0 </span>} } <span class="c">! rhombus</span>
</span></span><span class="line"><span class="cl"> { { <span class="m">0 0 </span>} { <span class="m">0 0 </span>} { <span class="m">1 1 </span>} { <span class="m">0 0 </span>} } <span class="c">! only 2 distinct points</span>
</span></span><span class="line"><span class="cl"> { { <span class="m">0 0 </span>} { <span class="m">0 0 </span>} { <span class="m">1 0 </span>} { <span class="m">0 1 </span>} } <span class="c">! only 3 distinct points</span>
</span></span><span class="line"><span class="cl"> } [ square? ] <span class="nb">any?
</span></span></span><span class="line"><span class="cl">] unit-test
</span></span></code></pre></div><p>Since it’s code golf (fewest characters possible), how might you make it
shorter?</p>
Google Translate
https://re.factorcode.org/2011/03/google-translate.html
Wed, 16 Mar 2011 08:36:00 -0700https://re.factorcode.org/2011/03/google-translate.html<p>After having some fun building a <a href="https://www.factorcode.org">Factor</a>
wrapper for the <a href="https://code.google.com/apis/chart/">Google Charts API</a>,
I decided to implement the <a href="https://code.google.com/apis/language/translate/overview.html">Google Translate
API</a>. We
will be using version 2, which returns JSON over HTTP.</p>
<p>The Google Translate API requires the use of an API key, which you can
get from the <a href="https://code.google.com/apis/console/?api=translate">Google APIs
console</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">assocs</span> <span class="nn">google</span> <span class="nn">http.client</span> <span class="nn">io</span> <span class="nn">json.reader</span> <span class="nn">kernel</span> <span class="nn">locals</span>
</span></span><span class="line"><span class="cl"><span class="nn">namespaces</span> <span class="nn">sequences</span> <span class="nn">urls</span> <span class="nn">urls.secure</span> <span class="k">;
</span></span></span></code></pre></div><h3 id="translate">Translate</h3>
<p>First, we define a global symbol to store the API key:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">google-api-key</span>
</span></span></code></pre></div><p>Next, we make a simple word to create translation URLs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">translate-url</span> <span class="nf">( </span><span class="nv">text</span> <span class="nv">source</span> <span class="nv">target</span> <span class="nf">-- </span><span class="nv">url</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">URL" https://www.googleapis.com/language/translate/v2"</span>
</span></span><span class="line"><span class="cl"> google-api-key <span class="nb">get-global </span><span class="s">"key"</span> set-query-param
</span></span><span class="line"><span class="cl"> source <span class="s">"source"</span> set-query-param
</span></span><span class="line"><span class="cl"> target <span class="s">"target"</span> set-query-param
</span></span><span class="line"><span class="cl"> text <span class="s">"q"</span> set-query-param <span class="k">;
</span></span></span></code></pre></div><p>We can translate a string of text from a source language into a target
language:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">translate</span> <span class="nf">( </span><span class="nv">text</span> <span class="nv">source</span> <span class="nv">target</span> <span class="nf">-- </span><span class="nv">text'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> translate-url http-get <span class="nb">nip </span>json
</span></span><span class="line"><span class="cl"> { <span class="s">"data"</span> <span class="s">"translations"</span> } [ <span class="nb">swap at </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> <span class="nb">first </span><span class="s">"translatedText"</span> <span class="nb">swap at </span><span class="k">;
</span></span></span></code></pre></div><p>Once you’ve set your API key, you can try it out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Hello world!"</span> <span class="s">"en"</span> <span class="s">"es"</span> translate <span class="nb">print
</span></span></span><span class="line"><span class="cl">¡Hola, mundo!
</span></span></code></pre></div><p>Google Translate supports <a href="https://code.google.com/apis/language/translate/v2/using_rest.html#language-params">many
languages</a>,
which makes it pretty useful.</p>
<h3 id="translation-party">Translation Party</h3>
<p>If you haven’t seen the <a href="https://translationparty.com/">Translation
Party</a> website, you should check it out.
Basically, it translates a phrase from English into Japanese and back
again until it reaches a stable equilibrium. Some of the results are
pretty funny. We are going to build this in Factor, but supporting any
source and target language.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">translation-party</span> <span class="nf">( </span><span class="nv">text</span> <span class="nv">source</span> <span class="nv">target</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> text <span class="nb">dup print </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>source target translate <span class="nb">dup print
</span></span></span><span class="line"><span class="cl"> target source translate <span class="nb">dup print
</span></span></span><span class="line"><span class="cl"> <span class="nb">swap dupd = not
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">loop drop </span><span class="k">;
</span></span></span></code></pre></div><p>For example, we can translate the phrase <a href="https://translationparty.com/#8713983">“Type anything here and you’ll
get funny”</a> and you get:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Type anything here and you'll get funny"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"en"</span> <span class="s">"ja"</span> translation-party
</span></span><span class="line"><span class="cl">Type anything here <span class="nb">and </span>you'll <span class="nb">get </span>funny
</span></span><span class="line"><span class="cl">ここに何も入力すると、面白い取得します
</span></span><span class="line"><span class="cl">Here you enter anything gets interesting
</span></span><span class="line"><span class="cl">ここでは、何が面白いの入力
</span></span><span class="line"><span class="cl">Here is something interesting input
</span></span><span class="line"><span class="cl">ここで何か面白いものが入力される
</span></span><span class="line"><span class="cl">Something interesting is entered here
</span></span><span class="line"><span class="cl">何かが興味深いここで入力されている
</span></span><span class="line"><span class="cl">Are entered here is something interesting
</span></span><span class="line"><span class="cl">何かが興味深いここで入力されていますが
</span></span><span class="line"><span class="cl">Has been entered here is something interesting
</span></span><span class="line"><span class="cl">ここで入力されている何か面白いです
</span></span><span class="line"><span class="cl">What is interesting is entered here
</span></span><span class="line"><span class="cl">興味深いのは、ここに入力されている
</span></span><span class="line"><span class="cl">Interestingly, that is entered here
</span></span><span class="line"><span class="cl">興味深いことに、ここに入力されている
</span></span><span class="line"><span class="cl">Interestingly, that is entered here
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/google/translate/translate.factor">GitHub</a>.</p>
Google Charts
https://re.factorcode.org/2011/03/google-charts.html
Fri, 11 Mar 2011 20:37:00 -0800https://re.factorcode.org/2011/03/google-charts.html<p>Recently, I implemented a wrapper for the <a href="https://code.google.com/apis/chart/">Google Chart
API</a> in
<a href="https://www.factorcode.org">Factor</a>. The <a href="https://re.factorcode.org/2010/09/visual-repl.html">Visual
REPL</a> provides a
really great way to do exploratory programming, such as my previous
example of integrating
<a href="https://re.factorcode.org/2011/01/wolframalpha-using-factor.html">Wolfram|Alpha</a>
with Factor.</p>
<p>Below are some various examples of using my <code>google.charts</code> vocabulary:</p>
<h3 id="pie-charts">Pie Charts</h3>
<p>At the end of 2008, Slava Pestov posted an
<a href="https://factor-language.blogspot.com/2008/12/prevalence-of-shuffle-words-and.html">article</a>
analyzing the usage of shuffle words in the core library. He created a
<code>usage-histogram</code> word that counts the number of usages of a sequence of
words:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">usage-histogram</span> <span class="nf">( </span><span class="nv">words</span> <span class="nf">-- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ name>> ] [ usage <span class="nb">length </span>] <span class="nb">bi </span>] { } <span class="nb">map>assoc </span><span class="k">;
</span></span></span></code></pre></div><p>We can then produce a pie chart showing the relative usage of various
shuffle words:</p>
<p>
<img src="https://re.factorcode.org/images/2011-03-11-google-charts-chart-pie.png" alt="" width="465" height="273" />
</p>
<h3 id="bar-charts">Bar Charts</h3>
<p>It might be fun to use a dictionary to count the number of words that
start with each letter. We can use the dictionary available on most
Unix-like systems to make a histogram and display the results as a bar
chart:</p>
<p>
<img src="https://re.factorcode.org/images/2011-03-11-google-charts-chart-bar.png" alt="" width="399" height="347" />
</p>
<h3 id="sparklines">Sparklines</h3>
<p>Edward Tufte coined the term
<a href="https://en.wikipedia.org/wiki/Sparkline">sparkline</a> and described them
as “data-intense, design-simple, word-sized graphics”. They are often
useful for quick visualizations of large data sets, for example a random
sequence of numbers:</p>
<p>
<img src="https://re.factorcode.org/images/2011-03-11-google-charts-chart-sparkline.png" alt="" width="402" height="142" />
</p>
<h3 id="qr-codes">QR Codes</h3>
<p>Recently, Fred Alger posted some <a href="https://weblog.fredalger.net/2011/01/qr-codes-in-factor.html">source
code</a> which
uses QR Codes and the
<a href="https://www.nttdocomo.co.jp/english/service/imode/make/content/barcode/function/application/addressbook/index.html">MECARD</a>
format to share address book information with mobile users. We can
convert arbitrary text into a QR Code for display:</p>
<p>
<img src="https://re.factorcode.org/images/2011-03-11-google-charts-chart-qr-code.png" alt="" width="347" height="258" />
</p>
<h3 id="formulas">Formulas</h3>
<p>We can use <a href="https://en.wikipedia.org/wiki/TeX">Tex</a> to plot formulas:</p>
<p>
<img src="https://re.factorcode.org/images/2011-03-11-google-charts-chart-formula.png" alt="" width="423" height="107" />
</p>
<h3 id="scatter">Scatter</h3>
<p>We can make scatter plots, for example 100 random <code>x,y</code> points:</p>
<p>
<img src="https://re.factorcode.org/images/2011-03-11-google-charts-chart-scatter.png" alt="" width="333" height="287" />
</p>
<p>The code is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/google/charts/charts.factor">GitHub</a>.</p>
Fast Now
https://re.factorcode.org/2011/03/fast-now.html
Sun, 06 Mar 2011 09:45:00 -0800https://re.factorcode.org/2011/03/fast-now.html<p>Sometimes profiling an application will show hotspots in the oddest
places. For some types of network applications that process huge volumes
of events, calls to
<a href="https://linux.die.net/man/2/gettimeofday"><code>gettimeofday()</code></a> can become a
bottleneck. If each event needs to have a timestamp generated, this
could mean thousands of <a href="https://en.wikipedia.org/wiki/System_call">system
calls</a> in a very short time,
all producing essentially the same value. If the “actual” time is not so
important, performance could be gained by relaxing the requirement that
all events received in a <a href="https://linux.die.net/man/2/select"><code>select()</code></a>
loop have the same timestamp.</p>
<p>One way to do this is to cache the timestamp after waking up, and use
that timestamp for all events received within the I/O loop.
Unfortunately, the I/O loop can take a long time to process - resulting
in timestamps that diverge from “actual” time by small or large (and
unpredictable) amounts. Perhaps a better way would be to cache the
result of <code>gettimeofday()</code> with a resolution of one millisecond.</p>
<h3 id="fast-now">fast-now</h3>
<p>In <a href="https://www.factorcode.org">Factor</a>, <code>gettimeofday()</code> is called by
the <code>now</code> word from the <code>calendar</code> vocabulary. Let’s try and build a
<code>fast-now</code> word that adds caching:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">calendar</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">namespaces</span> <span class="nn">system</span> <span class="k">;
</span></span></span></code></pre></div><p>Our cache resolution is one millisecond (or 1,000,000 nanoseconds):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">cache-duration</span> <span class="m">1000000
</span></span></span></code></pre></div><p>We will be keeping the cached value and the expiration time:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">cache-value</span>
</span></span><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">cache-until</span>
</span></span></code></pre></div><p>Given the current time in nanoseconds, we can check to see if the cached
value has expired:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">cache-expired?</span> <span class="nf">( </span><span class="nv">nanos</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> cache-until <span class="nb">get-global </span><span class="m">0 </span><span class="nb">or > </span><span class="k">;
</span></span></span></code></pre></div><p>If it has, we can reset the cache expiration:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">reset-cache</span> <span class="nf">( </span><span class="nv">nanos</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> cache-duration <span class="nb">+ </span>cache-until <span class="nb">set-global </span><span class="k">;
</span></span></span></code></pre></div><p>And update the cached value (the result of calling <code>now</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">update-cache</span> <span class="nf">( </span><span class="nv">nanos</span> <span class="nf">-- </span><span class="nv">timestamp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> reset-cache now [ cache-value <span class="nb">set-global </span>] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div><p>Building the <code>fast-now</code> word is as easy as:</p>
<ol>
<li>Get the current monotonically increasing <code>nano-count</code>.</li>
<li>Check if the cache has expired.</li>
<li>Update the cache if expired, otherwise return the cached value.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fast-now</span> <span class="nf">( -- </span><span class="nv">timestamp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> nano-count <span class="nb">dup </span>cache-expired? [ update-cache ] [
</span></span><span class="line"><span class="cl"> <span class="nb">drop </span>cache-value <span class="nb">get-global
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p><em>Note: we use a <a href="https://code-factor.blogspot.com/2009/11/monotonic-timers.html">monotonic
timer</a>
because it is a much faster operation than calling <code>gettimeofday()</code>.
Another fast way would be to use the <a href="https://docs.factorcode.org/content/word-instruction-count,cpu.x86.features.html">instruction
counter</a>,
however we would need to <a href="https://re.factorcode.org/2010/11/estimating-cpu-speed.html">estimate cpu
speed</a>
to know how many instructions the cached value should survive for.</em></p>
<h3 id="try-it">Try It</h3>
<p>We can try this out, and see how much faster it is:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">1000 </span>[ now <span class="nb">drop </span>] <span class="nb">times </span>] benchmark <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">19706313
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">1000 </span>[ fast-now <span class="nb">drop </span>] <span class="nb">times </span>] benchmark <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">356574
</span></span></span></code></pre></div><p>So, 1,000 calls to <code>now</code> took 19.706 milliseconds, and only 0.356
milliseconds for <code>fast-now</code> (for a speedup of <strong>55x</strong>)! In the case of a
single call, both <code>now</code> and <code>fast-now</code> take roughly the same time. Not a
bad improvement, right?</p>
<p>The code (and some tests) for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/fast-now/fast-now.factor">GitHub</a>.</p>
Gestalt
https://re.factorcode.org/2011/02/gestalt.html
Mon, 21 Feb 2011 17:09:00 -0800https://re.factorcode.org/2011/02/gestalt.html<p>Sometimes it is useful to lookup the operating system version that your
code is running on. On the Mac OS, this information can be retrieved
in <a href="https://www.cocoadev.com/index.pl?DeterminingOSVersion">several</a>
ways. We will be adding <a href="https://www.factorcode.org">Factor</a> support for
using the <code>Gestalt</code> function (available as part of the <a href="https://developer.apple.com/library/mac/#documentation/Carbon/Reference/Gestalt_Manager/Reference/reference.html">Gestalt
Manager</a>
in the <code>CoreServices.framework</code> since Mac OS 10.0).</p>
<p>First, we create a namespace and list of vocabularies we will be using:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">alien.data</span> <span class="nn">alien.syntax</span> <span class="nn">combinators</span> <span class="nn">core-foundation</span>
</span></span><span class="line"><span class="cl"><span class="nn">formatting</span> <span class="nn">io.binary</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="k">;
</span></span></span></code></pre></div><p>IN: gestalt</p>
<p>Using Factor’s <a href="https://docs.factorcode.org/content/article-alien.html">C library
interface</a>, we
can declare the function and its types:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">TYPEDEF:</span> <span class="nf">SInt16</span> <span class="nf">OSErr</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">TYPEDEF:</span> <span class="nf">UInt32</span> <span class="nf">OSType</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> OSErr <span class="nf">Gestalt</span> ( OSType selector, SInt32* response )
</span></span></code></pre></div><p>Using this, we can create the <code>gestalt</code> word, which calls the function,
checks for errors, and returns the result:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">gestalt</span> <span class="nf">( </span><span class="nv">selector</span> <span class="nf">-- </span><span class="nv">response</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { SInt32 } [ Gestalt <span class="m">0 </span><span class="nb">assert= </span>] with-out-parameters <span class="k">;
</span></span></span></code></pre></div><p>Looking at <code>Gestalt.h</code>, we can see several <code>enum</code> definitions that can
be used to retrieve system version information:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="nb">enum </span>{
</span></span><span class="line"><span class="cl"> gestaltSystemVersion <span class="nb">= </span>'sysv',
</span></span><span class="line"><span class="cl"> gestaltSystemVersionMajor <span class="nb">= </span>'sys1',
</span></span><span class="line"><span class="cl"> gestaltSystemVersionMinor <span class="nb">= </span>'sys2',
</span></span><span class="line"><span class="cl"> gestaltSystemVersionBugFix <span class="nb">= </span>'sys3'
</span></span><span class="line"><span class="cl">};
</span></span></code></pre></div><p>Knowing these, we can create words for accessing the various system
versions exposed by <code>Gestalt</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">system-version</span> <span class="nf">( -- </span><span class="nv">n</span> <span class="nf">) </span><span class="s">"sysv"</span> be> gestalt <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">system-version-major</span> <span class="nf">( -- </span><span class="nv">n</span> <span class="nf">) </span><span class="s">"sys1"</span> be> gestalt <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">system-version-minor</span> <span class="nf">( -- </span><span class="nv">n</span> <span class="nf">) </span><span class="s">"sys2"</span> be> gestalt <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">system-version-bugfix</span> <span class="nf">( -- </span><span class="nv">n</span> <span class="nf">) </span><span class="s">"sys3"</span> be> gestalt <span class="k">;
</span></span></span></code></pre></div><p>However, we see a comment in <code>Gestalt.h</code> which says:</p>
<pre tabindex="0"><code>If the values of the minor or bug fix revision are larger
than 9, then gestaltSystemVersion will substitute the value
9 for them. For example, Mac OS 10.3.15 will be returned
as 0x1039, and Mac OS 10.10.5 will return 0x1095.
A better way to get version information on Mac OS
would be to use the new gestaltSystemVersionMajor,
gestaltSystemVersionMinor, and gestaltSystemVersionBugFix
selectors, which don't have arbitrary limits on the values
returned.
</code></pre><p>With this limitation, we could still use the <code>system-version</code> to
retrieve Apple’s “code name” for the version of Mac OS you are running
(since it works for all of the <a href="https://en.wikipedia.org/wiki/Mac_OS_X#Versions">released Mac OS
versions</a>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">system-code-name</span> <span class="nf">( -- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> system-version <span class="m">0xFFF0 </span><span class="nb">bitand </span>{
</span></span><span class="line"><span class="cl"> { <span class="m">0x1070 </span>[ <span class="s">"Lion"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x1060 </span>[ <span class="s">"Snow Leopard"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x1050 </span>[ <span class="s">"Leopard"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x1040 </span>[ <span class="s">"Tiger"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x1030 </span>[ <span class="s">"Panther"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x1020 </span>[ <span class="s">"Jaguar"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x1010 </span>[ <span class="s">"Puma"</span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x1000 </span>[ <span class="s">"Cheetah"</span> ] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">"Unknown"</span> ]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div><p>And then, we can make a version string using each of the major, minor,
and bugfix values:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">system-version-string</span> <span class="nf">( -- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> system-version-major
</span></span><span class="line"><span class="cl"> system-version-minor
</span></span><span class="line"><span class="cl"> system-version-bugfix
</span></span><span class="line"><span class="cl"> <span class="s">"%s.%s.%s"</span> sprintf <span class="k">;
</span></span></span></code></pre></div><p>Using this, we can see which version of Mac OS is running on my
laptop:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> system-version-string system-code-name
</span></span><span class="line"><span class="cl"> <span class="s">"%s (%s)\n"</span> printf
</span></span><span class="line"><span class="cl">10.6.6 (Snow Leopard)
</span></span></code></pre></div><p>By the way, another comment in <code>Gestalt.h</code> says:</p>
<pre tabindex="0"><code>If you want to know the product build version string,
product name, or the user visible version string you should
read in the system version information from the file
/System/Library/CoreServices/SystemVersion.plist.
</code></pre><p>Using the <code>cocoa.plist</code> vocabulary, we can do just that:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">cocoa.plist</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/System/Library/CoreServices/SystemVersion.plist"</span>
</span></span><span class="line"><span class="cl"> read-plist <span class="m">.
</span></span></span><span class="line"><span class="cl">H{
</span></span><span class="line"><span class="cl"> { <span class="s">"ProductVersion"</span> <span class="s">"10.6.6"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"ProductName"</span> <span class="s">"Mac OS"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"ProductBuildVersion"</span> <span class="s">"10J567"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"ProductUserVisibleVersion"</span> <span class="s">"10.6.6"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"ProductCopyright"</span> <span class="s">"1983-2011 Apple Inc."</span> }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/gestalt/gestalt.factor">GitHub</a>.</p>
Port Knocking
https://re.factorcode.org/2011/02/port-knocking.html
Thu, 17 Feb 2011 06:36:00 -0800https://re.factorcode.org/2011/02/port-knocking.html<p>As a followup to my <a href="https://re.factorcode.org/2011/02/port-scanner.html">last
article</a>, I
wanted to show how to implement <a href="https://en.wikipedia.org/wiki/Port_knocking">port
knocking</a> functionality in
<a href="https://www.factorcode.org">Factor</a>. This was particularly interesting
to me since I ran across a <a href="https://www.chipx86.com/blog/2011/02/10/i-invented-port-knocking/">recent blog
post</a>
about the “inventor” of port knocking.</p>
<blockquote>
<p><em>"<strong>port knocking</strong> is a method of externally opening ports on a
firewall by generating a connection attempt on a set of prespecified
closed ports"</em></p>
</blockquote>
<p>There are several types of port knocking, with variations using any
combination of TCP, UDP, ICMP, or similar network protocols. For this
implementation, we will choose to use TCP. In particular, using the
<code>open-port?</code> word from our
<a href="https://github.com/mrjbq7/re-factor/blob/master/port-scan/port-scan.factor">port-scan</a>
vocabulary. As a reminder, it looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">continuations</span> <span class="nn">io.encodings.binary</span> <span class="nn">io.sockets</span> <span class="nn">kernel</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">open-port?</span> <span class="nf">( </span><span class="nv">host</span> <span class="nv">port</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <inet> [ binary [ <span class="no">t </span>] with-client ] [ <span class="nb">2drop </span><span class="no">f </span>] <span class="nb">recover </span><span class="k">;
</span></span></span></code></pre></div><p>The idea is to knock on a sequence of ports, in order. In its simplest
form, we attempt a connection to each port, ignoring whether it is open
or closed (not relevant).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">knock-ports</span> <span class="nf">( </span><span class="nv">host</span> <span class="nv">ports</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dupd </span>open-port? <span class="nb">drop </span>] <span class="nb">each drop </span><span class="k">;
</span></span></span></code></pre></div><p>You can imagine using it something like this (knocking on ports 10000,
10001, 10002 and then write a “Hello” message to port 12345):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"localhost"</span>
</span></span><span class="line"><span class="cl"> [ { <span class="m">10000 10001 10002 </span>} knock-ports ]
</span></span><span class="line"><span class="cl"> [ <span class="m">12345 </span><inet> ascii [ <span class="s">"Hello"</span> <span class="nb">print </span>] with-client ]
</span></span><span class="line"><span class="cl"> <span class="nb">bi
</span></span></span></code></pre></div><p>Some ways to improve this might be to allow “knock delay” between
successive knocks, support UDP or ICMP knocks, or perhaps to encompass
this functionality transparently into a reusable <code>with-knocking-client</code>
word.</p>
Port Scanner
https://re.factorcode.org/2011/02/port-scanner.html
Tue, 15 Feb 2011 11:20:00 -0800https://re.factorcode.org/2011/02/port-scanner.html<p>For fun, I thought I’d show how to build a simple <a href="https://en.wikipedia.org/wiki/Port_scanner">port
scanner</a> using
<a href="https://www.factorcode.org">Factor</a></p>
<p>We start by listing vocabularies that we will be using and a namespace
for our work:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">continuations</span> <span class="nn">formatting</span> <span class="nn">kernel</span> <span class="nn">ranges</span>
</span></span><span class="line"><span class="cl"><span class="nn">io.encodings.binary</span> <span class="nn">io.sockets</span> <span class="nn">make</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">port-scan</span>
</span></span></code></pre></div><p>We need to decide how to check for an open port. In this case, we are
going to attempt to establish a TCP connection. If it succeeds, then the
port is open, otherwise it will throw an error connecting and we will
assume the port was not open.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">open-port?</span> <span class="nf">( </span><span class="nv">host</span> <span class="nv">port</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <inet> [ binary [ <span class="no">t </span>] with-client ] [ <span class="nb">2drop </span><span class="no">f </span>] <span class="nb">recover </span><span class="k">;
</span></span></span></code></pre></div><blockquote>
<p><em>Note: we should be setting a connection timeout, so that we do not
let a connection attempt last forever. However, I’m not quite sure how
to do that – the documentation for <code>io.sockets</code> and <code>io.timeouts</code>
didn’t make it obvious.</em></p>
</blockquote>
<p>Next, we will make a word that returns an array of all open ports
(checking ports from 1 to 1024). We use the
<a href="https://docs.factorcode.org/content/article-namespaces-make.html">make</a>
vocabulary to build the sequence dynamically.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">open-ports</span> <span class="nf">( </span><span class="nv">host</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1024 </span>[1..b] [
</span></span><span class="line"><span class="cl"> [ <span class="nb">2dup </span>open-port? [ , ] [ <span class="nb">drop </span>] <span class="nb">if </span>] <span class="nb">each drop
</span></span></span><span class="line"><span class="cl"> ] { } make <span class="k">;
</span></span></span></code></pre></div><p>Finally, we can make a word that provides some visual output back to the
user:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">scan-ports</span> <span class="nf">( </span><span class="nv">host</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"Scanning %s...\n"</span> printf ]
</span></span><span class="line"><span class="cl"> [ open-ports [ <span class="s">"%d is open\n"</span> printf ] <span class="nb">each </span>]
</span></span><span class="line"><span class="cl"> <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>Using this on my laptop returns the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"127.0.0.1"</span> scan-ports
</span></span><span class="line"><span class="cl">Scanning 127.0.0.1...
</span></span><span class="line"><span class="cl"><span class="m">631 </span>is open
</span></span></code></pre></div><p>It is quite simple and functional as is. However, some obvious
improvements could be made:</p>
<ul>
<li>adding the connection timeout as mentioned above</li>
<li>providing the output of <code>scan-ports</code> to the user as open ports are
found</li>
<li>using the <a href="https://docs.factorcode.org/content/article-concurrency.combinators.html">concurrent
combinators</a>
to test ports in parallel</li>
<li>using a <a href="https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers">list of port
numbers</a>
to identify services that might be on open ports</li>
</ul>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/port-scan/port-scan.factor">GitHub</a>.</p>
Simple RPG
https://re.factorcode.org/2011/02/simple-rpg.html
Sat, 12 Feb 2011 18:14:00 -0800https://re.factorcode.org/2011/02/simple-rpg.html<p>There was a <a href="https://www.primaryobjects.com/CMS/Article126.aspx">post</a> a
few days ago that discussed “fluent interfaces” using a simple
role-playing game as an example. Since
<a href="https://www.factorcode.org">Factor</a>, as a <a href="https://concatenative.org/wiki/view/Concatenative%20language">concatenative
language</a>,
can be quite “fluent”, I decided to port the original <a href="https://github.com/primaryobjects/Fluent-Simple-RPG-Game">C#
version</a> into
Factor to see how it looks.</p>
<p>The basic “simple RPG” game starts with the creation of a hero, who
encounters and does battle with various randomly generated enemies. It
is text-based and looks something like this:</p>
<pre tabindex="0"><code>= Starting Battle =
Name: Valient
Class: fighter
HP: 20/20
Age: 22
Str 18 / Agi 14 / Int 12
Gold: 0
vs.
Name: Destroicous
Class: fighter
HP: 8/8
Age: 107
Str 7 / Agi 6 / Int 18
Gold: 42
An enemy approaches>
Valient (20/20) / Destroicous (8/8)
Valient poisons Destroicous for 6 damage!
Destroicous incinerates Valient for 3 damage!
</code></pre><p>Some vocabularies we will be using:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">combinators</span> <span class="nn">formatting</span> <span class="nn">io</span> <span class="nn">kernel</span> <span class="nn">locals</span> <span class="nn">math</span>
</span></span><span class="line"><span class="cl"><span class="nn">ranges</span> <span class="nn">random</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span></code></pre></div><h3 id="object-creation">Object Creation</h3>
<p>The original article focuses mainly on object creation, which I’ll only
mention in passing. The “fluent” version in C# looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="n">characterBuilder</span><span class="p">.</span><span class="n">Create</span><span class="p">(</span><span class="s">"King"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">As</span><span class="p">(</span><span class="n">ClassType</span><span class="p">.</span><span class="n">Fighter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">WithAge</span><span class="p">(</span><span class="m">49</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">HP</span><span class="p">(</span><span class="m">50</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">Strength</span><span class="p">(</span><span class="m">17</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">Agility</span><span class="p">(</span><span class="m">12</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">Intelligence</span><span class="p">(</span><span class="m">15</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">Gold</span><span class="p">(</span><span class="m">9999999</span><span class="p">);</span>
</span></span></code></pre></div><p>Translating that directly to Factor (using <a href="https://docs.factorcode.org/content/article-accessors.html">slot
accessors</a> to
construct objects), would look something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">character <span class="nb">new
</span></span></span><span class="line"><span class="cl"> <span class="s">"King"</span> >>name
</span></span><span class="line"><span class="cl"> <span class="s">"fighter"</span> >>class
</span></span><span class="line"><span class="cl"> <span class="m">49 </span>>>age
</span></span><span class="line"><span class="cl"> <span class="m">50 </span>>>hp
</span></span><span class="line"><span class="cl"> <span class="m">17 </span>>>strength
</span></span><span class="line"><span class="cl"> <span class="m">12 </span>>>agility
</span></span><span class="line"><span class="cl"> <span class="m">15 </span>>>intelligence
</span></span><span class="line"><span class="cl"> <span class="m">9999999 </span>>>gold
</span></span></code></pre></div><h3 id="the-character">The Character</h3>
<p>We define a character type with some basic fields (using short names
that are common among RPG programmers) to represent either our hero, or
his enemies:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">character</span> <span class="nv">name</span> <span class="nv">class</span> <span class="nv">age</span> <span class="nv">str</span> <span class="nv">agi</span> <span class="nv">int</span> <span class="nv">gold</span> <span class="nv">hp</span> <span class="nv">max-hp</span> <span class="k">;
</span></span></span></code></pre></div><p>We will list several possible character classes (although the main logic
will not take these into account):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">classes</span> {
</span></span><span class="line"><span class="cl"> <span class="s">"fighter"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"mage"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"cleric"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"rogue"</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>A word to check if a character is alive:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">alive?</span> <span class="nf">( </span><span class="nv">character</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">) </span>hp>> <span class="m">0 </span><span class="nb">> </span><span class="k">;
</span></span></span></code></pre></div><p>We can print out our character’s full stats:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">full-stats</span> <span class="nf">( </span><span class="nv">character</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ name>> <span class="s">"Name: %s\n"</span> printf ]
</span></span><span class="line"><span class="cl"> [ class>> <span class="s">"Class: %s\n"</span> printf ]
</span></span><span class="line"><span class="cl"> [ [ hp>> ] [ max-hp>> ] <span class="nb">bi </span><span class="s">"HP: %d/%d\n"</span> printf ]
</span></span><span class="line"><span class="cl"> [ age>> <span class="s">"Age: %d\n"</span> printf ]
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ str>> ] [ agi>> ] [ int>> ] <span class="nb">tri
</span></span></span><span class="line"><span class="cl"> <span class="s">"Str %d / Agi %d / Int %d\n"</span> printf
</span></span><span class="line"><span class="cl"> ]
</span></span><span class="line"><span class="cl"> [ gold>> <span class="s">"Gold: %d\n"</span> printf ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span><span class="k">;
</span></span></span></code></pre></div><p>Or print just a “quick” version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">quick-stats</span> <span class="nf">( </span><span class="nv">character</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ name>> ] [ hp>> ] [ max-hp>> ] <span class="nb">tri </span><span class="s">"%s (%d/%d)"</span> printf <span class="k">;
</span></span></span></code></pre></div><h3 id="the-battle">The Battle</h3>
<p><em>Note: Our battle logic is implemented with
<a href="https://docs.factorcode.org/content/article-locals.html">locals</a> to try
and match the C# version closely.</em></p>
<p>We support some random attack types:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">attack-verbs</span> {
</span></span><span class="line"><span class="cl"> <span class="s">"slashes"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"stabs"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"smashes"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"impales"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"poisons"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"shoots"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"incinerates"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"destroys"</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>The attack logic is very simple - a random amount of damage using a
random attack type:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">attack</span> <span class="nf">( </span><span class="nv">attacker</span> <span class="nv">defender</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="m">10 </span>random :> damage
</span></span><span class="line"><span class="cl"> attacker name>
</span></span><span class="line"><span class="cl"> attack-verbs random
</span></span><span class="line"><span class="cl"> defender [ damage <span class="nb">- </span>] change-hp name>
</span></span><span class="line"><span class="cl"> damage
</span></span><span class="line"><span class="cl"> <span class="s">"%s %s %s for %d damage!\n"</span> printf <span class="k">;
</span></span></span></code></pre></div><p>The main battle logic starts a battle, loops performing a “fight to the
death”, and then declares our hero as victor or victim:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">battle</span> <span class="nf">( </span><span class="nv">hero</span> <span class="nv">enemy</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"= Starting Battle ="</span> <span class="nb">print
</span></span></span><span class="line"><span class="cl"> hero full-stats <span class="nb">nl
</span></span></span><span class="line"><span class="cl"> <span class="s">"vs."</span> <span class="nb">print nl
</span></span></span><span class="line"><span class="cl"> enemy full-stats <span class="nb">nl
</span></span></span><span class="line"><span class="cl"> <span class="s">"An enemy approaches> "</span> <span class="nb">write read1 drop nl nl
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> [ hero alive? enemy alive? <span class="nb">and </span>] [
</span></span><span class="line"><span class="cl"> hero quick-stats <span class="s">" / "</span> <span class="nb">write </span>enemy quick-stats <span class="nb">nl
</span></span></span><span class="line"><span class="cl"> hero enemy attack
</span></span><span class="line"><span class="cl"> enemy hero attack
</span></span><span class="line"><span class="cl"> hero alive? [ <span class="s">"> "</span> <span class="nb">write read1 drop </span>] <span class="nb">when nl
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> hero alive? [
</span></span><span class="line"><span class="cl"> <span class="s">"Our hero survives to fight another battle! "</span> <span class="nb">write
</span></span></span><span class="line"><span class="cl"> enemy gold>> <span class="s">"Won %d gold!\n"</span> printf
</span></span><span class="line"><span class="cl"> hero [ enemy gold>> <span class="nb">+ </span>] change-gold <span class="nb">drop
</span></span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> hero gold>> <span class="s">"Our hero has fallen with %d gold! "</span> printf
</span></span><span class="line"><span class="cl"> <span class="s">"The world is covered in darkness once again."</span> <span class="nb">print
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if nl </span><span class="k">;
</span></span></span></code></pre></div><h3 id="the-game">The Game</h3>
<p>We create “Valient”, our hero:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><hero></span> <span class="nf">( -- </span><span class="nv">character</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> character <span class="nb">new
</span></span></span><span class="line"><span class="cl"> <span class="s">"Valient"</span> >>name
</span></span><span class="line"><span class="cl"> <span class="s">"fighter"</span> >>class
</span></span><span class="line"><span class="cl"> <span class="m">22 </span>>>age
</span></span><span class="line"><span class="cl"> <span class="m">20 </span>[ >>hp ] [ >>max-hp ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> <span class="m">18 </span>>>str
</span></span><span class="line"><span class="cl"> <span class="m">14 </span>>>agi
</span></span><span class="line"><span class="cl"> <span class="m">12 </span>>>int
</span></span><span class="line"><span class="cl"> <span class="m">0 </span>>>gold <span class="k">;
</span></span></span></code></pre></div><p>Some logic to create random enemy names:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">first-names</span> {
</span></span><span class="line"><span class="cl"> <span class="s">"Destro"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Victo"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Mozri"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Fang"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Ovi"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Hell"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Syth"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"End"</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">last-names</span> {
</span></span><span class="line"><span class="cl"> <span class="s">"math"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"rin"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"sith"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"icous"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"ravage"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"wrath"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"ryn"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"less"</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-name</span> <span class="nf">( -- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> first-names last-names [ random ] <span class="nb">bi@ append </span><span class="k">;
</span></span></span></code></pre></div><p>And finally, create a random enemy to fight:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><enemy></span> <span class="nf">( -- </span><span class="nv">character</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> character <span class="nb">new
</span></span></span><span class="line"><span class="cl"> random-name >>name
</span></span><span class="line"><span class="cl"> classes random >>class
</span></span><span class="line"><span class="cl"> <span class="m">12 200 </span>[a..b] random >>age
</span></span><span class="line"><span class="cl"> <span class="m">5 12 </span>[a..b] random [ >>hp ] [ >>max-hp ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> <span class="m">21 </span>[1..b) random >>str
</span></span><span class="line"><span class="cl"> <span class="m">21 </span>[1..b) random >>agi
</span></span><span class="line"><span class="cl"> <span class="m">21 </span>[1..b) random >>int
</span></span><span class="line"><span class="cl"> <span class="m">50 </span>random >>gold <span class="k">;
</span></span></span></code></pre></div><p>Using these words, and the battle logic created earlier, we can run the
entire game:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">run-battle</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <hero> [ <span class="nb">dup </span>alive? ] [ <span class="nb">dup </span><enemy> battle ] <span class="nb">while drop </span><span class="k">;
</span></span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/simple-rpg/simple-rpg.factor">GitHub</a>.</p>
Ping? Pong!
https://re.factorcode.org/2011/02/ping-pong.html
Thu, 10 Feb 2011 14:16:00 -0800https://re.factorcode.org/2011/02/ping-pong.html<p>A few months ago, I
<a href="https://github.com/factor/factor/commit/a58278d0a33ef1227ed796d8772df4a84496d5a7">implemented</a>
the “ping” utility in <a href="https://www.factorcode.org">Factor</a>. Doug Coleman
helped get it working on Windows. Below I’m going to describe how it
works.</p>
<p>First, we needed to add ICMP support in Factor (in the <code>io.sockets.icmp</code>
vocabulary). Some things this required:</p>
<ul>
<li>Previously, Factor had <code>inet4</code> and <code>inet6</code> types representing a
“host/port” tuple in IPv4 and IPv6, respectively. ICMP is similar in
that it requires a “host”, but the “port” is unnecessary. With
Slava’s help, we factored out <code>ipv4</code> and <code>ipv6</code> types from <code>inet4</code>
and <code>inet6</code> and used them to create the <code>icmp4</code> and <code>icmp6</code> address
types.</li>
<li>We needed a generic word <code>protocol</code> that can be used to specify the
correct protocol value to be used when creating a socket.</li>
<li>We also needed an implementation of the <a href="https://re.factorcode.org/2010/09/internet-checksum.html">internet
checksum</a>,
which I had previously provided (in the <code>checksums.internet</code>
vocabulary).</li>
</ul>
<p>The <code>ping</code> implementation starts with imports and a namespace:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">byte-arrays</span> <span class="nn">calendar</span> <span class="nn">checksums</span>
</span></span><span class="line"><span class="cl"><span class="nn">checksums.internet</span> <span class="nn">combinators</span> <span class="nn">combinators.smart</span> <span class="nn">continuations</span>
</span></span><span class="line"><span class="cl"><span class="nn">destructors</span> <span class="nn">io.sockets</span> <span class="nn">io.sockets.icmp</span> <span class="nn">io.timeouts</span> <span class="nn">kernel</span>
</span></span><span class="line"><span class="cl"><span class="nn">locals</span> <span class="nn">pack</span> <span class="nn">random</span> <span class="nn">sequences</span> <span class="nn">system</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">ping</span>
</span></span></code></pre></div><p>In <a href="https://www.faqs.org/rfcs/rfc792.html">RFC 792</a>, the ICMP “Echo” and
“Echo Reply” packets are described. Both have the same form (except that
“Echo” is type 8 and “Echo Reply” is type 0). We define an <code>echo</code> tuple
to represent both types. The <code><echo></code> constructor is then used to create
an “Echo” (type 8) with a random identifier (some ping implementations
use the process id instead).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">echo</span> <span class="nv">type</span> <span class="nv">identifier</span> <span class="nv">sequence</span> <span class="nv">data</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><echo></span> <span class="nf">( </span><span class="nv">sequence</span> <span class="nv">data</span> <span class="nf">-- </span><span class="nv">echo</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">8 16 </span>random-bits ] <span class="nb">2dip </span>echo <span class="nb">boa </span><span class="k">;
</span></span></span></code></pre></div><p>Since both echo types have the same form, so we can use the packet
description in the RFC to create words to convert between <code>echo</code>’s and
<code>byte-array</code>’s. We use the internet checksum when encoding and before
decoding to verify the response.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">echo>byte-array</span> <span class="nf">( </span><span class="nv">echo</span> <span class="nf">-- </span><span class="nv">byte-array</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ type>> <span class="m">0 0 </span>] <span class="c">! code checksum</span>
</span></span><span class="line"><span class="cl"> [ identifier>> ]
</span></span><span class="line"><span class="cl"> [ sequence>> ] <span class="nb">tri
</span></span></span><span class="line"><span class="cl"> ] output>array <span class="s">"CCSSS"</span> pack-be
</span></span><span class="line"><span class="cl"> ] [ data>> ] <span class="nb">bi append </span>[
</span></span><span class="line"><span class="cl"> internet checksum-bytes <span class="m">2 4
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">keep replace-slice </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">byte-array>echo</span> <span class="nf">( </span><span class="nv">byte-array</span> <span class="nf">-- </span><span class="nv">echo</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>internet checksum-bytes B{ <span class="m">0 0 </span>} <span class="nb">assert=
</span></span></span><span class="line"><span class="cl"> <span class="m">8 </span><span class="nb">cut </span>[
</span></span><span class="line"><span class="cl"> <span class="s">"CCSSS"</span> unpack-be { <span class="m">0 3 4 </span>} <span class="nb">swap nths first3
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">dip </span>echo <span class="nb">boa </span><span class="k">;
</span></span></span></code></pre></div><p>Sending a ping is just creating an <code>echo</code> request, encoding it into a
<code>byte-array</code> and sending it to the specified address.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">send-ping</span> <span class="nf">( </span><span class="nv">addr</span> <span class="nv">raw</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <span class="m">0 </span>{ } <echo> echo>byte-array ] <span class="nb">2dip </span>send <span class="k">;
</span></span></span></code></pre></div><blockquote>
<p><em>Note: We should be incrementing the sequence properly, instead of
always sending zero here – and then using it to verify the reply
packets.</em></p>
</blockquote>
<p>Receiving a ping is just reading packets until we have one from the
specified address.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">recv-ping</span> <span class="nf">( </span><span class="nv">addr</span> <span class="nv">raw</span> <span class="nf">-- </span><span class="nv">echo</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> raw receive addr <span class="nb">= </span>[
</span></span><span class="line"><span class="cl"> <span class="m">20 </span><span class="nb">tail </span>byte-array>echo
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="nb">drop </span>addr raw recv-ping
</span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><blockquote>
<p><em>Note: There’s a subtle bug where we set a “read timeout” on the
socket, but if we keep getting packets from the wrong IP, then we will
loop without timing out properly.</em></p>
</blockquote>
<p>Normally ICMP can only be used with “raw” sockets which require root
(administrative) privileges to create. This is often implemented by
setting the <a href="https://en.wikipedia.org/wiki/Setuid">setuid</a> flag on the
<code>ping</code> executable. However, on BSD systems (like Mac OS), it is a
little different. Running <a href="https://developer.apple.com/library/mac/#DOCUMENTATION/Darwin/Reference/ManPages/man4/icmp.4.html">“man 4
icmp”</a>
shows you something called “Non-privileged ICMP” which allows you to
create an ICMP socket using the “datagram” socket (e.g., <code>SOCK_DGRAM</code>).
These sockets only support a limited subset of ICMP, but it is
sufficient for sending echo requests and receiving echo replies.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">HOOK:</span> <span class="nf"><ping-port></span> <span class="nf">os</span> <span class="nf">( </span><span class="nv">inet</span> <span class="nf">-- </span><span class="nv">port</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">object</span> <span class="nf"><ping-port></span> <raw> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">macosx</span> <span class="nf"><ping-port></span> <datagram> <span class="k">;
</span></span></span></code></pre></div><p>Putting this all together, we can implement a <code>ping</code> word that looks up
an IPv4 address for the specified hostname, and then sends a ping, using
<a href="https://docs.factorcode.org/content/article-io.timeouts.html">io.timeouts</a>
to wait up to one second for the response.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">ping</span> <span class="nf">( </span><span class="nv">host</span> <span class="nf">-- </span><span class="nv">reply</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <icmp> resolve-host [ icmp4? ] <span class="nb">filter </span>random
</span></span><span class="line"><span class="cl"> <span class="no">f </span><icmp4> <ping-port>
</span></span><span class="line"><span class="cl"> <span class="m">1 </span>seconds <span class="nb">over </span>set-timeout
</span></span><span class="line"><span class="cl"> [ [ send-ping ] [ recv-ping ] <span class="nb">2bi </span>] with-disposal <span class="k">;
</span></span></span></code></pre></div><p>For convenience, we make a word to ping the IPv4 localhost address.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">local-ping</span> <span class="nf">( -- </span><span class="nv">reply</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"127.0.0.1"</span> ping <span class="k">;
</span></span></span></code></pre></div><p>And a word that just checks to see if a host is alive, returning <code>t</code>
(true) or <code>f</code> (false).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">alive?</span> <span class="nf">( </span><span class="nv">host</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ ping <span class="nb">drop </span><span class="no">t </span>] [ <span class="nb">2drop </span><span class="no">f </span>] <span class="nb">recover </span><span class="k">;
</span></span></span></code></pre></div><p>This code is in <code>extra/ping</code> in the Factor repository. Currently, it
supports only IPv4 addresses, but could be modified to support IPv6
(using <a href="https://www.faqs.org/rfcs/rfc2463.html">RFC 2463</a> which requires
some modifications to use a “pseudo-header” in the checksum
calculation).</p>
Maximum/Minimum
https://re.factorcode.org/2011/02/maximum-minimum.html
Fri, 04 Feb 2011 14:06:00 -0800https://re.factorcode.org/2011/02/maximum-minimum.html<p>For some <a href="https://www.factorcode.org">Factor</a> code I was writing
recently, I needed to find the “maximum” and “minimum” of some sequences
of values. The “obvious” way to do this is to use the
<a href="https://docs.factorcode.org/content/word-supremum,sequences.html">supremum</a>
and
<a href="https://docs.factorcode.org/content/word-infimum,sequences.html">infimum</a>
words from the <code>sequences</code> vocabulary. Those words were not quite what I
wanted, so I thought I would write some thoughts about it:</p>
<p>First, an example to show how they work:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1 2 3 4 </span>} <span class="nb">supremum </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">4
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1 2 3 4 </span>} <span class="nb">infimum </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">1
</span></span></span></code></pre></div><p>Let’s say we create a <code>person</code> tuple with a name and age.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">person</span> <span class="nv">name</span> <span class="nv">age</span> <span class="k">;
</span></span></span></code></pre></div><p>Now, let’s say we have a list of people (e.g., a sequence of <code>person</code>
objects):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">PEOPLE</span> {
</span></span><span class="line"><span class="cl"> T{ person <span class="no">f </span><span class="s">"Jim"</span> <span class="m">30 </span>}
</span></span><span class="line"><span class="cl"> T{ person <span class="no">f </span><span class="s">"Sally"</span> <span class="m">27 </span>}
</span></span><span class="line"><span class="cl"> T{ person <span class="no">f </span><span class="s">"Rebecca"</span> <span class="m">32 </span>}
</span></span><span class="line"><span class="cl"> T{ person <span class="no">f </span><span class="s">"James"</span> <span class="m">28 </span>}
</span></span><span class="line"><span class="cl"> T{ person <span class="no">f </span><span class="s">"Benjamin"</span> <span class="m">22 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>What if we’d like to find out who the oldest person is? We could try
using the <code>supremum</code> word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> PEOPLE <span class="nb">supremum
</span></span></span><span class="line"><span class="cl">Generic word <=> does <span class="nb">not </span>define a method for the person class.
</span></span><span class="line"><span class="cl">Dispatching <span class="nb">on </span>object: T{ person <span class="no">f </span><span class="s">"Sally"</span> <span class="m">27 </span>}
</span></span></code></pre></div><p>Whoops! The <code>supremum</code> word uses the <code>max</code> generic word, which in turn
defaults to using the <code>after?</code> generic word, which in turn defaults to
using the <code><=></code> generic word (from the <code>math.order</code> vocabulary) to
compare two values for <code>+lt+</code>, <code>+eq+</code>, or <code>+gt+</code>.</p>
<p>So, let’s define that word for the <code>person</code> types:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">person</span> <span class="nf"><=></span> [ age>> ] <span class="nb">bi@ </span><=> <span class="k">;
</span></span></span></code></pre></div><p>Okay, let’s try again:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> PEOPLE <span class="nb">supremum </span><span class="m">.
</span></span></span><span class="line"><span class="cl">T{ person <span class="no">f </span><span class="s">"Rebecca"</span> <span class="m">32 </span>}
</span></span></code></pre></div><p>Alright, now we also want to find the person with the shortest name.
Well, we could use <code>infimum</code>, but it, like <code>supremum</code>, delegates to
<code><=></code> which (from our previous definition) currently orders by a
person’s <code>age</code>. Let’s redefine <code><=></code> to compare based on length of a
person’s <code>name</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">person</span> <span class="nf"><=></span> [ name>> <span class="nb">length </span>] <span class="nb">bi@ </span><=> <span class="k">;
</span></span></span></code></pre></div><p>Okay, let’s try it now:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> PEOPLE <span class="nb">infimum </span><span class="m">.
</span></span></span><span class="line"><span class="cl">T{ person <span class="no">f </span><span class="s">"Jim"</span> <span class="m">30 </span>}
</span></span></code></pre></div><p>It worked! But, this is quite awkward to change the definition of <code><=></code>
for each purpose. The problem here is that <code>supremum</code> and <code>infimum</code> (and
the <code><=></code> word they use) expect to be used for the natural ordering of
objects, not for arbitrary orderings.</p>
<p>Instead, we can define two new words, <code>max-by</code> and <code>min-by</code>, that use a
provided <code>quotation</code> to obtain a value that is then used to compare two
objects (e.g., in our first example, <code>age</code>, and in our second, the
length of <code>name</code>).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">max-by</span> <span class="nf">( </span><span class="nv">obj1</span> <span class="nv">obj2</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">obj</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">) -- </span><span class="nv">obj1/obj2</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">bi@ </span>[ max ] <span class="nb">keep eq? not </span>] <span class="nb">curry most </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">min-by</span> <span class="nf">( </span><span class="nv">obj1</span> <span class="nv">obj2</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">obj</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">) -- </span><span class="nv">obj1/obj2</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">bi@ </span>[ min ] <span class="nb">keep eq? not </span>] <span class="nb">curry most </span><span class="k">; inline
</span></span></span></code></pre></div><p>With these, we can define the words, <code>maximum</code> and <code>minimum</code>, which use
these to retrieve the object with “maximum” or “minimum” characteristics
(using the specified quotation).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">maximum</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">elt</span> <span class="nf">-- </span><span class="nv">...</span> <span class="nv">n</span> <span class="nf">) -- </span><span class="nv">elt</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">keep 2array </span>] <span class="nb">curry
</span></span></span><span class="line"><span class="cl"> [ [ <span class="nb">first </span>] max-by ] <span class="nb">map-reduce second </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">minimum</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">elt</span> <span class="nf">-- </span><span class="nv">...</span> <span class="nv">n</span> <span class="nf">) -- </span><span class="nv">elt</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">keep 2array </span>] <span class="nb">curry
</span></span></span><span class="line"><span class="cl"> [ [ <span class="nb">first </span>] min-by ] <span class="nb">map-reduce second </span><span class="k">; inline
</span></span></span></code></pre></div><p>Using these, we can easily find our answers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> PEOPLE [ age>> ] maximum <span class="m">.
</span></span></span><span class="line"><span class="cl">T{ person <span class="no">f </span><span class="s">"Rebecca"</span> <span class="m">32 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> PEOPLE [ name>> <span class="nb">length </span>] minimum <span class="m">.
</span></span></span><span class="line"><span class="cl">T{ person <span class="no">f </span><span class="s">"Jim"</span> <span class="m">30 </span>}
</span></span></code></pre></div><p>It’s worth noting that we could define both <code>supremum</code> and <code>infimum</code> in
terms of our new words:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">supremum</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">elt</span> <span class="nf">) </span>[ ] maximum <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">infimum</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">elt</span> <span class="nf">) </span>[ ] minimum <span class="k">;
</span></span></span></code></pre></div><p>But, because of the wrapping and unwrapping that we do inside of
<code>maximum</code> and <code>minimum</code>, it’s a little less efficient than the current
implementation.</p>
Wolfram|Alpha using Factor
https://re.factorcode.org/2011/01/wolfram-alpha-using-factor.html
Thu, 27 Jan 2011 10:00:00 -0800https://re.factorcode.org/2011/01/wolfram-alpha-using-factor.html<p><a href="https://www.wolframalpha.com">Wolfram|Alpha</a> is a fun and useful
“calculation engine” from the creators of
<a href="https://www.wolfram.com/mathematica/">Mathematica</a>. Launched in
mid-2009, it is available on the web, mobile devices, and from search
engines. Last week, Wolfram
<a href="https://blog.wolframalpha.com/2011/01/20/knowledge-based-computing-and-version-20-of-the-wolframalpha-api/">announced</a>
version 2.0 of their API. As part of that announcement, they made it
“free” for up to 2,000 non-commercial requests per month.</p>
<p>Since <a href="https://www.factorcode.org">Factor</a> has a <a href="https://re.factorcode.org/2010/09/visual-repl.html">visual
REPL</a>, I thought
it would make a perfect client for Wolfram|Alpha. Starting today, you
can do this:</p>
<p>
<img src="https://re.factorcode.org/images/2011-01-27-wolfram-alpha-using-factor-wolfram.png" alt="" width="533" height="716" />
</p>
<p>The Wolfram|Alpha <a href="https://products.wolframalpha.com/api/">API website</a>
has both an <a href="https://products.wolframalpha.com/docs/WolframAlpha-API-Reference.pdf">API
reference</a>
and an <a href="https://products.wolframalpha.com/api/explorer.html">API
explorer</a> that can
help you learn how it works. To get started, you need to <a href="https://developer.wolframalpha.com/portal/apisignup.html">sign
up</a> to receive
an API ID.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">formatting</span> <span class="nn">http</span> <span class="nn">http.client</span> <span class="nn">images.gif</span>
</span></span><span class="line"><span class="cl"><span class="nn">images.http</span> <span class="nn">io</span> <span class="nn">kernel</span> <span class="nn">namespaces</span> <span class="nn">sequences</span> <span class="nn">splitting</span>
</span></span><span class="line"><span class="cl"><span class="nn">urls.encoding</span> <span class="nn">xml</span> <span class="nn">xml.data</span> <span class="nn">xml.traversal</span> <span class="k">;
</span></span></span></code></pre></div><p>We will store the API ID in a symbol, that you can set:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">wolfram-api-id</span>
</span></span></code></pre></div><p>The API is queried by creating URLs (from the query input and API ID)
and parsing the response as XML.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">query</span> <span class="nf">( </span><span class="nv">query</span> <span class="nf">-- </span><span class="nv">xml</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> url-encode wolfram-api-id <span class="nb">get-global
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://api.wolframalpha.com/v2/query?input=%s&appid=%s"</span>
</span></span><span class="line"><span class="cl"> sprintf http-get <span class="nb">nip </span>string>xml <span class="k">;
</span></span></span></code></pre></div><p>Inside the XML response are “pods” corresponding to the different types
of information returned by Wolfram|Alpha. You can choose to receive the
“pods” as plaintext, images, sounds, HTML, or Mathematica values. By
default, the API returns plaintext and images.</p>
<p>We can create a <code>wolfram-image.</code> word that queries the API, extracts the
“pods”, outputs the title and images contained in each “pod”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wolfram-image.</span> <span class="nf">( </span><span class="nv">query</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> query <span class="s">"pod"</span> tags-named [
</span></span><span class="line"><span class="cl"> [ <span class="s">"title"</span> attr <span class="nb">print </span>]
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="s">"img"</span> deep-tags-named
</span></span><span class="line"><span class="cl"> [ <span class="s">"src"</span> attr http-image. ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>Thats how to integrate with Wolfram|Alpha. Before using, remember to set
your API ID:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"XXXXXX-XXXXXXXXXX"</span> wolfram-api-id <span class="nb">set-global
</span></span></span></code></pre></div><p>In addition to this, I made some minor style improvements (the gray
titles and indentation in the screenshot), created a <code>wolfram-text.</code>
word that prints the response as plaintext, and a <code>wolfram.</code> word that
detects if you are running Factor on the command-line or the graphic
interface and outputs text or images appropriately.</p>
<p>You can find the code on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/wolfram-alpha/wolfram-alpha.factor">GitHub</a>.</p>
<p><em>Note: I also needed to make a few minor fixes to Factor’s libraries to
get this to work. They are not yet merged into the main Factor
repository, but should be soon:</em></p>
<ul>
<li><a href="https://github.com/mrjbq7/factor/commit/fee16b2739ae25a66430c96d3062b79be235662d">compression.lzw: fix for gif89a decoding - maximum code size is 12
bits.</a></li>
<li><a href="https://github.com/mrjbq7/factor/commit/5abc730da49a7f29864809ace1c8d8b50f93c201">images.gif: the Graphics Control Block is OPTIONAL in the GIF
spec.</a></li>
<li><a href="https://github.com/mrjbq7/factor/commit/2dc1a52cc76c71344e1ac4f01b5bd493eb3a81f8">images.https: use the content type from the http response if
provided.</a></li>
</ul>
GitHub Vanity
https://re.factorcode.org/2011/01/github-vanity.html
Mon, 24 Jan 2011 18:01:00 -0800https://re.factorcode.org/2011/01/github-vanity.html<p>There was a <a href="https://thechangelog.com/post/2903183012/vain-check-github-watchers-forks-from-the-command-line">blog
post</a>
on <a href="https://thechangelog.com/">The Changelog</a> yesterday describing a
project that can give at-a-glance statistics about a GitHub user. The
project is called <code>vain</code> and is <a href="https://github.com/seejohnrun/vain">open
source</a>. Since implementing APIs is
fun (at least the first few times), I thought I would show how to
implement the <code>vain</code> utility using <a href="https://www.factorcode.org">Factor</a>
and the <a href="https://develop.github.com/">GitHub API</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">assocs</span> <span class="nn">combinators</span> <span class="nn">formatting</span> <span class="nn">http.client</span>
</span></span><span class="line"><span class="cl"><span class="nn">json.reader</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">sequences</span> <span class="nn">sorting</span> <span class="nn">utils</span> <span class="k">;
</span></span></span></code></pre></div><h3 id="experiment">Experiment</h3>
<p>We can experiment with the GitHub API in the Factor Listener, looking at
<a href="https://github.com/seejohnrun">seejohnrun</a>, the creator of <code>vain</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"https://github.com/api/v2/json/user/show/seejohnrun"</span>
</span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>json> <span class="m">.
</span></span></span><span class="line"><span class="cl">H{
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> <span class="s">"user"</span>
</span></span><span class="line"><span class="cl"> H{
</span></span><span class="line"><span class="cl"> { <span class="s">"followers_count"</span> <span class="m">18 </span>}
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> <span class="s">"gravatar_id"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"3a0541ed3d5324bb54b9f07990be20ae"</span>
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> { <span class="s">"login"</span> <span class="s">"seejohnrun"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"public_gist_count"</span> <span class="m">0 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"public_repo_count"</span> <span class="m">23 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"location"</span> <span class="s">"Verona, NJ"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"created_at"</span> <span class="s">"2009/03/19 10:29:18 -0700"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"type"</span> <span class="s">"User"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"id"</span> <span class="m">64965 </span>}
</span></span><span class="line"><span class="cl"> { <span class="s">"blog"</span> <span class="s">"https://johncrepezzi.com"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"email"</span> <span class="s">"[email protected]"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"name"</span> <span class="s">"John Crepezzi"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"company"</span> <span class="s">"Patch"</span> }
</span></span><span class="line"><span class="cl"> { <span class="s">"permission"</span> json-null }
</span></span><span class="line"><span class="cl"> { <span class="s">"following_count"</span> <span class="m">11 </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><h3 id="implement">Implement</h3>
<p>We can use the
<a href="https://re.factorcode.org/2011/01/setting-attributes.html"><code>set-slots</code></a>
word (similar to how we implemented <a href="https://re.factorcode.org/2011/01/reddit-top.html">Reddit
“Top”</a>) to get
the user details:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">user</span> <span class="nv">blog</span> <span class="nv">company</span> <span class="nv">created_at</span> <span class="nv">email</span> <span class="nv">followers_count</span>
</span></span><span class="line"><span class="cl"><span class="nv">following_count</span> <span class="nv">gravatar_id</span> <span class="nv">id</span> <span class="nv">location</span> <span class="nv">login</span> <span class="nv">name</span> <span class="nv">permission</span>
</span></span><span class="line"><span class="cl"><span class="nv">public_gist_count</span> <span class="nv">public_repo_count</span> <span class="nv">type</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">user-info</span> <span class="nf">( </span><span class="nv">login</span> <span class="nf">-- </span><span class="nv">user</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://github.com/api/v2/json/user/show/%s"</span> sprintf
</span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>json> <span class="s">"user"</span> <span class="nb">swap at
</span></span></span><span class="line"><span class="cl"> user <span class="nb">new </span>[ set-slots ] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div><p>Similarly, we can access a list of public repositories for a specific
user:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">repository</span> <span class="nv">created_at</span> <span class="nv">description</span> <span class="nv">fork</span> <span class="nv">forks</span>
</span></span><span class="line"><span class="cl"><span class="nv">has_downloads</span> <span class="nv">has_issues</span> <span class="nv">has_wiki</span> <span class="nv">homepage</span> <span class="nv">language</span> <span class="nv">name</span>
</span></span><span class="line"><span class="cl"><span class="nv">open_issues</span> <span class="nv">organization</span> <span class="nv">owner</span> <span class="nv">private</span> <span class="nv">pushed_at</span> <span class="nv">size</span> <span class="nv">url</span>
</span></span><span class="line"><span class="cl"><span class="nv">watchers</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">repositories</span> <span class="nf">( </span><span class="nv">login</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://github.com/api/v2/json/repos/show/%s"</span> sprintf
</span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>json> <span class="s">"repositories"</span> <span class="nb">swap at
</span></span></span><span class="line"><span class="cl"> [ repository <span class="nb">new </span>[ set-slots ] <span class="nb">keep </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>Using this, we have everything we need to implement <code>vain</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">vain</span> <span class="nf">( </span><span class="nv">login</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> user-info {
</span></span><span class="line"><span class="cl"> [ login>> ]
</span></span><span class="line"><span class="cl"> [ followers_count>> ]
</span></span><span class="line"><span class="cl"> [ public_repo_count>> ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave
</span></span></span><span class="line"><span class="cl"> <span class="s">"%s - %s followers - %s public repositories\n"</span> printf
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> repositories [ watchers>> ] inv-sort-with [
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ name>> ]
</span></span><span class="line"><span class="cl"> [ watchers>> <span class="s">"%s watchers"</span> sprintf ]
</span></span><span class="line"><span class="cl"> [ forks>> <span class="s">"%s forks"</span> sprintf ]
</span></span><span class="line"><span class="cl"> [ fork>> <span class="s">"(FORK)"</span> <span class="s">""</span> <span class="nb">? </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span><span class="s">"%-25s %12s %12s %s\n"</span> printf
</span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><h3 id="try-it">Try It</h3>
<p>You can see the output for <a href="https://github.com/seejohnrun">seejohnrun</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"seejohnrun"</span> vain
</span></span><span class="line"><span class="cl">seejohnrun <span class="nb">- </span><span class="m">18 </span>followers <span class="nb">- </span><span class="m">23 </span>public repositories
</span></span><span class="line"><span class="cl">ice_cube <span class="m">255 </span>watchers <span class="m">15 </span>forks
</span></span><span class="line"><span class="cl">database_validation <span class="m">60 </span>watchers <span class="m">4 </span>forks
</span></span><span class="line"><span class="cl">track_history <span class="m">57 </span>watchers <span class="m">4 </span>forks
</span></span><span class="line"><span class="cl">easy_translate <span class="m">39 </span>watchers <span class="m">2 </span>forks
</span></span><span class="line"><span class="cl">vain <span class="m">23 </span>watchers <span class="m">2 </span>forks
</span></span><span class="line"><span class="cl">console_tweet <span class="m">15 </span>watchers <span class="m">1 </span>forks
</span></span><span class="line"><span class="cl">tweetStream4J <span class="m">6 </span>watchers <span class="m">3 </span>forks
</span></span><span class="line"><span class="cl">my_tunes <span class="m">3 </span>watchers <span class="m">1 </span>forks
</span></span><span class="line"><span class="cl">locale_base <span class="m">2 </span>watchers <span class="m">1 </span>forks
</span></span><span class="line"><span class="cl">Open-Stanza <span class="m">2 </span>watchers <span class="m">1 </span>forks
</span></span><span class="line"><span class="cl">Pretty-Damn-Fancy <span class="m">1 </span>watchers <span class="m">1 </span>forks
</span></span><span class="line"><span class="cl">rstack <span class="m">1 </span>watchers <span class="m">1 </span>forks
</span></span><span class="line"><span class="cl">dependable <span class="m">1 </span>watchers <span class="m">1 </span>forks
</span></span><span class="line"><span class="cl">quick_short <span class="m">1 </span>watchers <span class="m">1 </span>forks
</span></span><span class="line"><span class="cl">html_namespacing <span class="m">1 </span>watchers <span class="m">0 </span>forks (FORK)
</span></span><span class="line"><span class="cl">usps <span class="m">1 </span>watchers <span class="m">0 </span>forks (FORK)
</span></span><span class="line"><span class="cl">dotfiles <span class="m">1 </span>watchers <span class="m">1 </span>forks
</span></span><span class="line"><span class="cl">datejs <span class="m">1 </span>watchers <span class="m">0 </span>forks (FORK)
</span></span><span class="line"><span class="cl">redis-repeater <span class="m">1 </span>watchers <span class="m">1 </span>forks
</span></span><span class="line"><span class="cl">roflscale <span class="m">1 </span>watchers <span class="m">1 </span>forks
</span></span><span class="line"><span class="cl">weatherbug <span class="m">1 </span>watchers <span class="m">1 </span>forks
</span></span><span class="line"><span class="cl">gravatar_helper <span class="m">1 </span>watchers <span class="m">1 </span>forks
</span></span><span class="line"><span class="cl">columnizer <span class="m">1 </span>watchers <span class="m">1 </span>forks
</span></span></code></pre></div><h3 id="bonus">Bonus</h3>
<p>Some fun with <a href="https://www.gravatar.com">Gravatar</a> pictures:</p>
<p>
<img src="https://re.factorcode.org/images/2011-01-24-github-vanity-gravatar.png" alt="" width="508" height="215" />
</p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/github/github.factor">GitHub</a>.</p>
Todo Lists
https://re.factorcode.org/2011/01/todo-lists.html
Sat, 22 Jan 2011 15:06:00 -0800https://re.factorcode.org/2011/01/todo-lists.html<p>It seems the thing to do these days is to write “a better todo list”.
Probably there is at least one (maybe dozens) implemented in each
programming language in existence. <a href="https://www.factorcode.org">Factor</a>
even has its own <a href="https://docs.factorcode.org/content/vocab-webapps.todo.html">todo web
application</a>.</p>
<p>When it comes to development, most developers keep lists of changes that
need to be made or features that need to be implemented. Factor has its
own <a href="https://concatenative.org/wiki/view/Factor/To%20do">todo list</a> on
the <a href="https://concatenative.org">concatenative.org</a> wiki. I know you are
thinking what I’m thinking: <em>wouldn’t it be great if we could keep the
todo list alongside the code?</em> In any event, it would make a nice
demonstration of the vocabulary and help browser system.</p>
<h3 id="metadata">metadata</h3>
<p>First, some background. Every vocabulary supports various
<a href="https://docs.factorcode.org/content/article-vocabs.metadata.html">metadata</a>
associated with the code, including:</p>
<table cellspacing="5">
<tbody>
<tr class="odd">
<td><code>summary.txt</code></td>
<td>a single line description of the vocabulary</td>
</tr>
<tr class="even">
<td><code>authors.txt</code></td>
<td>a list of <a href="https://docs.factorcode.org/content/article-vocab-authors.html">vocabulary
authors</a></td>
</tr>
<tr class="odd">
<td><code>resources.txt</code></td>
<td>a list of files to include when deploying</td>
</tr>
<tr class="even">
<td><code>tags.txt</code></td>
<td>a list of <a href="https://docs.factorcode.org/content/article-vocab-tags.html">vocabulary
tags</a> used for organization</td>
</tr>
<tr class="odd">
<td><code>platforms.txt</code></td>
<td>a list of supported platforms if not cross-platform</td>
</tr>
</tbody>
</table>
<h3 id="todotxt">todo.txt</h3>
<p>We are going to add to this a <code>todo.txt</code> file containing a todo list of
improvements or additions that could be made to the vocabulary. The
format of the <code>todo.txt</code> file will be a list of text, each on its own
line.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">arrays</span> <span class="nn">assocs</span> <span class="nn">formatting</span> <span class="nn">io</span> <span class="nn">io.pathnames</span> <span class="nn">kernel</span>
</span></span><span class="line"><span class="cl"><span class="nn">sequences</span> <span class="nn">vocabs</span> <span class="nn">vocabs.loader</span> <span class="nn">vocabs.metadata</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">todos</span>
</span></span></code></pre></div><p>The path to the <code>todo.txt</code> file is relative to the directory containing
the specified vocabulary:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">vocab-todo-path</span> <span class="nf">( </span><span class="nv">vocab</span> <span class="nf">-- </span><span class="nv">string</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> vocab-dir <span class="s">"todo.txt"</span> append-path <span class="k">;
</span></span></span></code></pre></div><p>We can get and set the list of todo items using
<a href="https://docs.factorcode.org/content/word-vocab-file-contents%2Cvocabs.metadata.html">vocab-file-contents</a>
and
<a href="https://docs.factorcode.org/content/word-set-vocab-file-contents%2Cvocabs.metadata.html">set-vocab-file-contents</a>,
respectively.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">vocab-todo</span> <span class="nf">( </span><span class="nv">vocab</span> <span class="nf">-- </span><span class="nv">todos</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>vocab-todo-path vocab-file-contents <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">set-vocab-todo</span> <span class="nf">( </span><span class="nv">todos</span> <span class="nv">vocab</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>vocab-todo-path set-vocab-file-contents <span class="k">;
</span></span></span></code></pre></div><p>We could add new todo items at runtime:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">add-vocab-todo</span> <span class="nf">( </span><span class="nv">todo</span> <span class="nv">vocab</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ vocab-todo <span class="nb">swap suffix </span>] <span class="nb">keep </span>set-vocab-todo <span class="k">;
</span></span></span></code></pre></div><p>Printing out the todo list for a specified vocabulary is pretty easy:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">todos.</span> <span class="nf">( </span><span class="nv">vocab</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> vocab-todo [ <span class="nb">print </span>] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>Using the
<a href="https://docs.factorcode.org/content/word-child-vocabs,vocabs.html">child-vocabs</a>
word, we can look through a vocabulary hierarchy for all todo files,
returning a map of vocabulary to non-empty list of todo items.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-todos</span> <span class="nf">( </span><span class="nv">vocab</span> <span class="nf">-- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> child-vocabs [ <span class="nb">dup </span>vocab-todo <span class="nb">2array </span>] <span class="nb">map
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">second empty? not </span>] <span class="nb">filter </span><span class="k">;
</span></span></span></code></pre></div><p>And then print them out from the Listener:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-todos.</span> <span class="nf">( </span><span class="nv">vocab</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> all-todos [
</span></span><span class="line"><span class="cl"> [ <span class="s">"%s:\n"</span> printf ] [ [ <span class="s">"- %s\n"</span> printf ] <span class="nb">each </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">assoc-each </span><span class="k">;
</span></span></span></code></pre></div><h3 id="try-it">Try It</h3>
<p>Although we could make the <code>todo.txt</code> files by hand, why not try using
Factor?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">tools.scaffold</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"foo"</span> scaffold-work
</span></span><span class="line"><span class="cl">Creating scaffolding for <span class="s">P" resource:work/foo/foo.factor"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"foo.bar"</span> scaffold-work
</span></span><span class="line"><span class="cl">Creating scaffolding for <span class="s">P" resource:work/foo/bar/bar.factor"</span>
</span></span><span class="line"><span class="cl">Loading resource:work/foo/bar/bar.factor
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"The first thing"</span> <span class="s">"foo"</span> add-vocab-todo
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"The second thing"</span> <span class="s">"foo"</span> add-vocab-todo
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Another thing"</span> <span class="s">"foo.bar"</span> add-vocab-todo
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"foo"</span> todos.
</span></span><span class="line"><span class="cl">The <span class="nb">first </span>thing
</span></span><span class="line"><span class="cl">The <span class="nb">second </span>thing
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"foo"</span> all-todos.
</span></span><span class="line"><span class="cl">foo:
</span></span><span class="line"><span class="cl"><span class="nb">- </span>The <span class="nb">first </span>thing
</span></span><span class="line"><span class="cl"><span class="nb">- </span>The <span class="nb">second </span>thing
</span></span><span class="line"><span class="cl">foo.bar:
</span></span><span class="line"><span class="cl"><span class="nb">- </span>Another thing
</span></span></code></pre></div><p>If you look in <code>$FACTOR/work</code>, you will now find the <code>foo/todo.txt</code> and
<code>foo/bar/todo.txt</code> files that we just created.</p>
<h3 id="help">help</h3>
<p>We can use these words to make a dynamic <a href="https://docs.factorcode.org/content/word-ARTICLE__colon__,help.syntax.html">help
article</a>
containing all of the todo entries for loaded vocabularies:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">assocs</span> <span class="nn">help.markup</span> <span class="nn">help.syntax</span> <span class="nn">kernel</span> <span class="nn">todos</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">$all-todos</span> <span class="nf">( </span><span class="nv">element</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">drop </span><span class="s">""</span> all-todos [
</span></span><span class="line"><span class="cl"> [ $heading ] [ $list ] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">assoc-each </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">ARTICLE: <span class="s">"vocab-todos"</span> <span class="s">"Vocabulary todos"</span>
</span></span><span class="line"><span class="cl">{ $all-todos } <span class="k">;
</span></span></span></code></pre></div><p>Once loaded, just run this to see the help article created by the
previous example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"vocab-todos"</span> help
</span></span></code></pre></div><p>
<img src="https://re.factorcode.org/images/2011-01-22-todo-lists-todo.png" alt="" width="512" height="225" />
</p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/tree/master/todos">GitHub</a>.</p>
Open URL
https://re.factorcode.org/2011/01/open-url.html
Wed, 19 Jan 2011 13:43:00 -0800https://re.factorcode.org/2011/01/open-url.html<p>A useful library in <a href="https://www.python.org">Python</a> is the
<a href="https://docs.python.org/library/webbrowser.html">webbrowser</a> module: it
allows you to open a URL in your web browser. When I was writing <a href="https://re.factorcode.org/2011/01/reddit-top.html">Reddit
“Top”</a>, this was
something I wanted but couldn’t find in
<a href="https://www.factorcode.org">Factor</a>. It would be great to have
cross-platform “open URL” functionality, so I thought I would show how
to build it.</p>
<h3 id="macosx">macosx</h3>
<p>Mac OS comes with a command-line utility called
<a href="https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/open.1.html">open</a>.
It will open files or directories as if you had double-clicked them in
the Finder. If you pass it a URL, it will open that URL in the default
web browser. We can use the
<a href="https://docs.factorcode.org/content/vocab-io.launcher.html">io.launcher</a>
vocabulary to run this command in a new process:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">formatting</span> <span class="nn">io.launcher</span> <span class="nn">urls.encoding</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">open-url</span> <span class="nf">( </span><span class="nv">url</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> url-encode <span class="s">"open \"%s\""</span> sprintf try-process <span class="k">;
</span></span></span></code></pre></div><p>Alternatively, you could use Applescript (via the
<a href="https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/osascript.1.html">osascript</a>
command) and the “open location” feature. This is how you might do it,
if you want to target a specific browser (“tell application… activate
OpenURL…”), indicate that it should open in a new window
("…toWindow…"), or open several URLs at the same time.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">formatting</span> <span class="nn">io.encodings.ascii</span> <span class="nn">io.launcher</span> <span class="nn">urls.encoding</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">open-url</span> <span class="nf">( </span><span class="nv">url</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"osascript"</span> ascii [
</span></span><span class="line"><span class="cl"> url-encode <span class="s">"open location \"%s\""</span> printf
</span></span><span class="line"><span class="cl"> ] with-process-writer <span class="k">;
</span></span></span></code></pre></div><h3 id="unix">unix</h3>
<p>On Linux, the situation is a little more complicated. Using Gnome, you
can run the
<a href="https://embraceubuntu.com/2006/12/16/gnome-open-open-anything-from-the-command-line/">gnome-open</a>
command. Using KDE, you could run the
<a href="https://developer.kde.org/documentation/other/kfmclient.html">kfmclient</a>
command. On other systems, maybe you could use
<a href="https://search.cpan.org/~pardus/File-MimeInfo-0.15/mimeopen">mimeopen</a>,
or maybe write your own. For this example, we will assume you are
running Gnome – but you could support other methods and/or detect which
method is appropriate to use:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">formatting</span> <span class="nn">io.launcher</span> <span class="nn">urls.encoding</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">open-url</span> <span class="nf">( </span><span class="nv">url</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> url-encode <span class="s">"gnome-open \"%s\""</span> sprintf try-process <span class="k">;
</span></span></span></code></pre></div><h3 id="windows">windows</h3>
<p>On Windows, we can use the
<a href="https://msdn.microsoft.com/en-us/library/bb762153(v=vs.85).aspx">ShellExecute</a>
function from <code>Shell32.dll</code>. In Factor, this is defined in the
<code>windows.shell32</code> vocabulary.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">urls.encoding</span> <span class="nn">windows.shell32</span> <span class="nn">windows.user32</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">open-url</span> <span class="nf">( </span><span class="nv">url</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> url-encode [ <span class="no">f </span><span class="s">"open"</span> ] <span class="nb">dip </span><span class="no">f f </span>SW_SHOWNORMAL ShellExecute <span class="nb">drop </span><span class="k">;
</span></span></span></code></pre></div><h3 id="try-it">Try It</h3>
<p>Once you have the appropriate <code>open-url</code> word loaded into your Factor
VM, you should be able to try it out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"https://www.factorcode.org"</span> open-url
</span></span></code></pre></div><p>The code for this (designed as a cross-platform <code>webbrowser</code> vocabulary
supporting <code>open-url</code>) is on my
<a href="https://github.com/mrjbq7/re-factor/tree/master/webbrowser">GitHub</a>.</p>
<p>Note: It would be nice if clicking on URLs in the Factor browser and
presentations would open them in your web browser, but I haven’t yet
figured out how to get that to work.</p>
Reddit "Top"
https://re.factorcode.org/2011/01/reddit-top.html
Mon, 17 Jan 2011 08:24:00 -0800https://re.factorcode.org/2011/01/reddit-top.html<p><a href="https://reddit.com">Reddit</a> has an
<a href="https://code.reddit.com/wiki/API">API</a> that can be used for accessing
much of the information available through their website. We can retrieve
a <a href="https://www.json.org/">JSON</a> list of recent stories posted to any
subreddit by going to <code>https://api.reddit.com/r/$NAME</code>. You can
experiment with this in the <a href="https://www.factorcode.org">Factor</a>
listener - to retrieve top stories for the
<a href="https://www.reddit.com/r/programming/">programming</a> subreddit:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USING:</span> <span class="nn">http.client</span> <span class="nn">json.reader</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"https://api.reddit.com/r/programming"</span>
</span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>json> <span class="m">.
</span></span></span></code></pre></div><p>Someone once <a href="https://www.catonmat.net/blog/how-reddit-top-and-hacker-top-programs-were-made">used the
API</a>
to build a <a href="https://github.com/pkrumins/reddit-top">reddit-top</a> program
for monitoring top stories from the console. We will use
<a href="https://www.factorcode.org">Factor</a> vocabularies to scrape Reddit and
produce something similar:</p>
<p>
<img src="https://re.factorcode.org/images/2011-01-17-reddit-top-subreddit-top.png" alt="" width="459" height="512" />
</p>
<p>We start by building a <code>(subreddit)</code> helper word to retrieve the JSON
response for a particular subreddit, extracting the top stories, and
returning an array of hashtables (one for each of the top stories).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(subreddit)</span> <span class="nf">( </span><span class="nv">name</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://api.reddit.com/r/%s"</span> sprintf http-get <span class="nb">nip
</span></span></span><span class="line"><span class="cl"> json> { <span class="s">"data"</span> <span class="s">"children"</span> } [ <span class="nb">swap at </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"data"</span> <span class="nb">swap at </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>We can then define a <code>story</code> tuple, with a slot for each attribute
returned by the API.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">story</span> <span class="nv">author</span> <span class="nv">clicked</span> <span class="nv">created</span> <span class="nv">created_utc</span> <span class="nv">domain</span> <span class="nv">downs</span>
</span></span><span class="line"><span class="cl"><span class="nv">hidden</span> <span class="nv">id</span> <span class="nv">is_self</span> <span class="nv">levenshtein</span> <span class="nv">likes</span> <span class="nv">media</span> <span class="nv">media_embed</span> <span class="nv">name</span>
</span></span><span class="line"><span class="cl"><span class="nv">num_comments</span> <span class="nv">over_18</span> <span class="nv">permalink</span> <span class="nv">saved</span> <span class="nv">score</span> <span class="nv">selftext</span>
</span></span><span class="line"><span class="cl"><span class="nv">selftext_html</span> <span class="nv">subreddit</span> <span class="nv">subreddit_id</span> <span class="nv">thumbnail</span> <span class="nv">title</span> <span class="nv">ups</span> <span class="nv">url</span> <span class="k">;
</span></span></span></code></pre></div><p>Once we have that, we can use the <code>set-slots</code> word from my previous post
on <a href="https://re.factorcode.org/2011/01/setting-attributes.html">setting
attributes</a>
to build a <code>subreddit</code> word that retrieves the top stories as objects:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">subreddit</span> <span class="nf">( </span><span class="nv">name</span> <span class="nf">-- </span><span class="nv">stories</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> (subreddit) [ story <span class="nb">new </span>[ set-slots ] <span class="nb">keep </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>Thats all we need to build the <code>subreddit-top</code> word demonstrated in the
beginning:</p>
<ol>
<li>Retrieve the top stories for a given subreddit.</li>
<li>Loop over each story.</li>
<li>Format and print the relevant attributes.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">subreddit-top</span> <span class="nf">( </span><span class="nv">subreddit</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> subreddit [
</span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="nb">+ </span><span class="s">"%2d. "</span> printf {
</span></span><span class="line"><span class="cl"> [ title>> ]
</span></span><span class="line"><span class="cl"> [ url>> ]
</span></span><span class="line"><span class="cl"> [ score>> ]
</span></span><span class="line"><span class="cl"> [ num_comments>> ]
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> created_utc>> unix-time>timestamp now <span class="nb">swap </span>time-
</span></span><span class="line"><span class="cl"> duration>hours <span class="s">"%d hours ago"</span> sprintf
</span></span><span class="line"><span class="cl"> ]
</span></span><span class="line"><span class="cl"> [ author>> ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave
</span></span></span><span class="line"><span class="cl"> <span class="s">"%s\n %s\n %d points, %d comments, posted %s by %s\n\n"</span>
</span></span><span class="line"><span class="cl"> printf
</span></span><span class="line"><span class="cl"> ] <span class="nb">each-index </span><span class="k">;
</span></span></span></code></pre></div><p>This (and some code for users and comments) is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/reddit/reddit.factor">GitHub</a>.</p>
Setting Attributes
https://re.factorcode.org/2011/01/setting-attributes.html
Fri, 14 Jan 2011 21:07:00 -0800https://re.factorcode.org/2011/01/setting-attributes.html<p>One test of dynamic languages is to try and set attribute values on an
object dynamically (e.g., without knowing until runtime which attributes
need to be set). Below, we compare a simple example in
<a href="https://www.python.org">Python</a>, a fairly dynamic language, to
<a href="https://www.factorcode.org">Factor</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Foo</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">obj</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">d</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"a"</span> <span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">"b"</span> <span class="p">:</span> <span class="mi">2</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">obj</span><span class="o">.</span><span class="n">a</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"a"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">obj</span><span class="o">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"b"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">obj</span><span class="o">.</span><span class="n">c</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"c"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="n">obj</span><span class="o">.</span><span class="n">a</span> <span class="c1"># 1</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="n">obj</span><span class="o">.</span><span class="n">b</span> <span class="c1"># 2</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="n">obj</span><span class="o">.</span><span class="n">c</span> <span class="c1"># None</span>
</span></span></code></pre></div><p>We might directly translate the previous example to Factor code, using
<a href="https://docs.factorcode.org/content/article-accessors.html">slot
accessors</a> to
set attributes on the tuple instance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">foo</span> <span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">foo <span class="nb">new
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">H{ { <span class="s">"a"</span> <span class="m">1 </span>} { <span class="s">"b"</span> <span class="m">2 </span>} } {
</span></span><span class="line"><span class="cl"> [ <span class="s">"a"</span> <span class="nb">swap at </span>>>a ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"b"</span> <span class="nb">swap at </span>>>b ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"c"</span> <span class="nb">swap at </span>>>c ]
</span></span><span class="line"><span class="cl">} <span class="nb">cleave
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">[ a>> <span class="m">. </span>] [ b>> <span class="m">. </span>] [ c>> <span class="m">. </span>] <span class="nb">tri
</span></span></span></code></pre></div><p>But, it’s much better if you don’t need to know ahead of time which
attributes a class has (i.e., needing to write code to handle each
attribute). In Python, you might instead set each value dynamically
using the <code>setattr</code> function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">d</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="nb">setattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
</span></span></code></pre></div><p>We can use the
<a href="https://docs.factorcode.org/content/word-set-slot-named,db.types.html">set-slot-named</a>
word from the
<a href="https://docs.factorcode.org/content/vocab-db.types.html">db.types</a>
vocabulary to do the same from Factor:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">assocs</span> <span class="nn">db.types</span> <span class="nn">fry</span> <span class="nn">kernel</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">set-slots</span> <span class="nf">( </span><span class="nv">assoc</span> <span class="nv">obj</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> '[ <span class="nb">swap </span>_ set-slot-named ] <span class="nb">assoc-each </span><span class="k">;
</span></span></span></code></pre></div><blockquote>
<p><em>Note: the <code>set-slot-named</code> word (and the <code>offset-of-slot</code> word that
it uses) should probably be moved to the <code>slots</code> vocabulary.</em></p>
</blockquote>
<p>We can simplify the previous example using our newly created <code>set-slots</code>
word and try it in the Factor listener:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="k">TUPLE:</span> <span class="nc">foo</span> <span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> foo <span class="nb">new
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> H{ { <span class="s">"a"</span> <span class="m">1 </span>} { <span class="s">"b"</span> <span class="m">2 </span>} } <span class="nb">over </span>set-slots <span class="m">.
</span></span></span><span class="line"><span class="cl">T{ foo { a <span class="m">1 </span>} { b <span class="m">2 </span>} }
</span></span></code></pre></div>Trashing Files: Part 3 (Windows)
https://re.factorcode.org/2011/01/trashing-files-part-3-windows.html
Thu, 13 Jan 2011 11:19:00 -0800https://re.factorcode.org/2011/01/trashing-files-part-3-windows.html<p>In <a href="https://re.factorcode.org/2011/01/trashing-files-part-1-mac-os.html">Part
1</a>
and <a href="https://re.factorcode.org/2011/01/trashing-files-part-2-unix.html">Part
2</a>,
we implemented <code>send-to-trash</code> on Mac OS and other Unix-like systems. In
<a href="https://re.factorcode.org/2011/01/trashing-files-part-3-windows.html">Part
3</a>,
we will be implementing support for the Windows Recycle Bin using
<a href="https://www.factorcode.org">Factor</a>.</p>
<h3 id="trashwindows">trash.windows</h3>
<p>First, we create the <code>trash.windows</code> vocabulary:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">alien.c-types</span> <span class="nn">alien.data</span> <span class="nn">alien.strings</span>
</span></span><span class="line"><span class="cl"><span class="nn">alien.syntax</span> <span class="nn">classes.struct</span> <span class="nn">classes.struct.packed</span> <span class="nn">destructors</span>
</span></span><span class="line"><span class="cl"><span class="nn">kernel</span> <span class="nn">io.encodings.utf16n</span> <span class="nn">libc</span> <span class="nn">math</span> <span class="nn">sequences</span> <span class="nn">system</span> <span class="nn">trash</span>
</span></span><span class="line"><span class="cl"><span class="nn">windows.types</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">trash.windows</span>
</span></span></code></pre></div><p>We will be using the
<a href="https://docs.factorcode.org/content/article-alien.html">alien</a>
vocabulary to call the
<a href="https://msdn.microsoft.com/en-us/library/bb762164(v=vs.85).aspx">SHFileOperationW</a>
function from the <code>shell32.dll</code> library. Unfortunately, this function
expects a “packed structure” (e.g., without <a href="https://en.wikipedia.org/wiki/Data_structure_alignment">data structure
padding</a>), so I
needed to <a href="https://github.com/mrjbq7/re-factor/blob/4a92988078e0f96e372e0915a3bbbd0682c97c71/classes/struct/packed/packed.factor">add
support</a>
for this first (in the <code>classes.struct.packed</code> vocabulary). Using this,
the <code>PACKED-STRUCT:</code> word creates a structure with each field aligned to
a single byte.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">LIBRARY: shell32
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">TYPEDEF:</span> <span class="nf">WORD</span> <span class="nf">FILEOP_FLAGS</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">PACKED-STRUCT: SHFILEOPSTRUCTW
</span></span><span class="line"><span class="cl"> { hwnd HWND }
</span></span><span class="line"><span class="cl"> { wFunc UINT }
</span></span><span class="line"><span class="cl"> { pFrom LPCWSTR* }
</span></span><span class="line"><span class="cl"> { pTo LPCWSTR* }
</span></span><span class="line"><span class="cl"> { fFlags FILEOP_FLAGS }
</span></span><span class="line"><span class="cl"> { fAnyOperationsAborted BOOL }
</span></span><span class="line"><span class="cl"> { hNameMappings LPVOID }
</span></span><span class="line"><span class="cl"> { lpszProgressTitle LPCWSTR } <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> int <span class="nf">SHFileOperationW</span> ( SHFILEOPSTRUCTW* lpFileOp )
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">FO_DELETE</span> <span class="m">0x0003
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">FOF_SILENT</span> <span class="m">0x0004
</span></span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">FOF_NOCONFIRMATION</span> <span class="m">0x0010
</span></span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">FOF_ALLOWUNDO</span> <span class="m">0x0040
</span></span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">FOF_NOERRORUI</span> <span class="m">0x0400
</span></span></span></code></pre></div><p>With these defined, we can implement <code>send-to-trash</code>, by simply creating
the <code>SHFILEOPSTRUCTW</code> structure (making sure to add extra null bytes to
the end of the path being trashed – since it should be “double null
terminated”), and then performing the <code>SHFileOperationW</code> function.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">windows</span> <span class="nf">send-to-trash</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> utf16n string>alien B{ <span class="m">0 0 </span>} <span class="nb">append
</span></span></span><span class="line"><span class="cl"> malloc-byte-array &free
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> SHFILEOPSTRUCTW <struct>
</span></span><span class="line"><span class="cl"> <span class="no">f </span>>>hwnd
</span></span><span class="line"><span class="cl"> FO_DELETE >>wFunc
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span>>>pFrom
</span></span><span class="line"><span class="cl"> <span class="no">f </span>>>pTo
</span></span><span class="line"><span class="cl"> FOF_ALLOWUNDO
</span></span><span class="line"><span class="cl"> FOF_NOCONFIRMATION <span class="nb">bitor
</span></span></span><span class="line"><span class="cl"> FOF_NOERRORUI <span class="nb">bitor
</span></span></span><span class="line"><span class="cl"> FOF_SILENT <span class="nb">bitor </span>>>fFlags
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> SHFileOperationW [ <span class="nb">throw </span>] <span class="nb">unless-zero
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> ] with-destructors <span class="k">;
</span></span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/trash/windows/windows.factor">GitHub</a>.</p>
Trashing Files: Part 2 (Unix)
https://re.factorcode.org/2011/01/trashing-files-part-2-unix.html
Wed, 12 Jan 2011 10:22:00 -0800https://re.factorcode.org/2011/01/trashing-files-part-2-unix.html<p>In <a href="https://re.factorcode.org/2011/01/trashing-files-part-1-mac-os.html">Part
1</a>,
we implemented <code>send-to-trash</code> on Mac OS. In <a href="https://re.factorcode.org/2011/01/trashing-files-part-2-unix.html">Part
2</a>,
we will be adding <a href="https://www.factorcode.org">Factor</a> support for the
<a href="https://www.ramendik.ru/docs/trashspec.html">FreeDesktop.org Trash
Specification</a> used on other
Unix systems (e.g., Linux or BSD).</p>
<h3 id="trashunix">trash.unix</h3>
<p>First, we need to create the <code>trash.unix</code> vocabulary:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">calendar</span> <span class="nn">combinators.short-circuit</span> <span class="nn">environment</span>
</span></span><span class="line"><span class="cl"><span class="nn">formatting</span> <span class="nn">io</span> <span class="nn">io.directories</span> <span class="nn">io.encodings.utf8</span> <span class="nn">io.files</span>
</span></span><span class="line"><span class="cl"><span class="nn">io.files.info</span> <span class="nn">io.files.info.unix</span> <span class="nn">io.files.types</span> <span class="nn">io.pathnames</span>
</span></span><span class="line"><span class="cl"><span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">math.parser</span> <span class="nn">sequences</span> <span class="nn">system</span> <span class="nn">trash</span> <span class="nn">unix.stat</span>
</span></span><span class="line"><span class="cl"><span class="nn">unix.users</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">trash.unix</span>
</span></span></code></pre></div><p>When trashing a file, we sometimes need to look for the “top directory”
of a mounted resource that contains a given path. We can use the
<a href="https://linux.die.net/man/3/lstat">lstat</a> function (using the
<code>link-status</code> word from <code>unix.stat</code>) to read information about the file
or symbol link pointed to by a path. If the file system details are
different between a path and its parent directory, then it is the top
directory of a mounted resource.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">top-directory?</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">".."</span> append-path [ link-status ] <span class="nb">bi@
</span></span></span><span class="line"><span class="cl"> [ [ st_dev>> ] <span class="nb">bi@ = not </span>] [ [ st_ino>> ] <span class="nb">bi@ = </span>] <span class="nb">2bi or </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">top-directory</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">path'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>top-directory? <span class="nb">not </span>] [ <span class="s">".."</span> append-path ] <span class="nb">while </span><span class="k">;
</span></span></span></code></pre></div><p>We need to be able to create trash directories with “user-only”
permissions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">make-user-directory</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ make-directories ] [ OCT: <span class="m">700 </span>set-file-permissions ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>To be a valid trash path, we need to check:</p>
<ol>
<li>The path is to a directory</li>
<li>The path has the sticky-bit set</li>
<li>The path should not be a symbolic link</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">check-trash-path</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ file-info directory? ]
</span></span><span class="line"><span class="cl"> [ sticky? ]
</span></span><span class="line"><span class="cl"> [ link-info type>> +symbolic-link+ <span class="nb">= not </span>]
</span></span><span class="line"><span class="cl"> } 1&& [ <span class="s">"invalid trash path"</span> <span class="nb">throw </span>] <span class="nb">unless </span><span class="k">;
</span></span></span></code></pre></div><p>The FreeDesktop.org Trash Specification defines various locations for
the trash directory, in order of preference:</p>
<ol>
<li>In <code>$XDG_DATA_HOME/Trash</code> (or <code>$HOME/.local/share/Trash</code>), if the
file being trashed is on the same mount point.</li>
<li>In the top directory of the path’s mount point,
<code>$TOPDIR/.Trash/$UID</code>, if the <code>.Trash</code> directory is available.</li>
<li>In the top directory of the path’s mount point,
<code>$TOPDIR/.Trash-$UID</code>, in a user-created directory.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">trash-home</span> <span class="nf">( -- </span><span class="nv">path</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"XDG_DATA_HOME"</span> os-env
</span></span><span class="line"><span class="cl"> home <span class="s">".local/share"</span> append-path <span class="nb">or
</span></span></span><span class="line"><span class="cl"> <span class="s">"Trash"</span> append-path <span class="nb">dup </span>check-trash-path <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">trash-1</span> <span class="nf">( </span><span class="nv">root</span> <span class="nf">-- </span><span class="nv">path</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">".Trash"</span> append-path <span class="nb">dup </span>check-trash-path
</span></span><span class="line"><span class="cl"> real-user-id number>string append-path <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">trash-2</span> <span class="nf">( </span><span class="nv">root</span> <span class="nf">-- </span><span class="nv">path</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> real-user-id <span class="s">".Trash-%d"</span> sprintf append-path <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">trash-path</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">path'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> top-directory <span class="nb">dup </span>trash-home top-directory <span class="nb">= </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">drop </span>trash-home
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">".Trash"</span> append-path exists?
</span></span><span class="line"><span class="cl"> [ trash-1 ] [ trash-2 ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> [ make-user-directory ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>We need to implement some logic to handle name collisions (e.g., when
trashing a file with the same name as a file already in the trash
directory). To do this, we use “safe” filenames (adding an incrementing
extension to ensure uniqueness):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(safe-file-name)</span> <span class="nf">( </span><span class="nv">path</span> <span class="nv">counter</span> <span class="nf">-- </span><span class="nv">path'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ parent-directory ]
</span></span><span class="line"><span class="cl"> [ file-stem ]
</span></span><span class="line"><span class="cl"> [ file-extension <span class="nb">dup </span>[ <span class="s">"."</span> <span class="nb">prepend </span>] <span class="nb">when </span>] <span class="nb">tri
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">dip swap </span><span class="s">"%s%s %s%s"</span> sprintf <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">safe-file-name</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">path'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span>[ <span class="nb">over </span>exists? ] [
</span></span><span class="line"><span class="cl"> [ parent-directory to-directory ] [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> [ (safe-file-name) ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while drop nip </span><span class="k">;
</span></span></span></code></pre></div><p>And, finally, we can implement the <code>send-to-trash</code> logic:</p>
<ol>
<li>Lookup the trash path for the file being trashed</li>
<li>Move the trashed file into a <code>files</code> sub-directory, using a safe
file name</li>
<li>Create an “information file” in an <code>info</code> sub-directory, with
details of the trashed file.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">unix</span> <span class="nf">send-to-trash</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>trash-path [
</span></span><span class="line"><span class="cl"> <span class="s">"files"</span> append-path [ make-user-directory ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> to-directory safe-file-name
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> <span class="s">"info"</span> append-path [ make-user-directory ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> to-directory <span class="s">".trashinfo"</span> <span class="nb">append </span>[ <span class="nb">over </span>] <span class="nb">dip </span>utf8 [
</span></span><span class="line"><span class="cl"> <span class="s">"[Trash Info]"</span> <span class="nb">write nl
</span></span></span><span class="line"><span class="cl"> <span class="s">"Path="</span> <span class="nb">write write nl
</span></span></span><span class="line"><span class="cl"> <span class="s">"DeletionDate="</span> <span class="nb">write
</span></span></span><span class="line"><span class="cl"> now <span class="s">"%Y-%m-%dT%H:%M:%S"</span> strftime <span class="nb">write nl
</span></span></span><span class="line"><span class="cl"> ] with-file-writer
</span></span><span class="line"><span class="cl"> ] <span class="nb">bi </span>move-file <span class="k">;
</span></span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/trash/unix/unix.factor">GitHub</a>.</p>
Trashing Files: Part 1 (Mac OS)
https://re.factorcode.org/2011/01/trashing-files-part-1-mac-os.html
Mon, 10 Jan 2011 23:02:00 -0800https://re.factorcode.org/2011/01/trashing-files-part-1-mac-os.html<p>Most operating systems provide support for sending files to the “trash
can” (or sometimes “recycle bin”). Inspired by a python project called
<a href="https://www.hardcoded.net/articles/send-files-to-trash-on-all-platforms.htm">“send2trash”</a>,
I thought <a href="https://www.factorcode.org">Factor</a> should have a similar
cross-platform library for trashing files.</p>
<h3 id="trash">trash</h3>
<p>First, we are going to define a <code>trash</code> vocabulary, and use a
<a href="https://docs.factorcode.org/content/word-HOOK__colon__,syntax.html">HOOK:</a>
that dispatches to the proper implementation, depending on which
operating system you are running.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">combinators</span> <span class="nn">system</span> <span class="nn">vocabs.loader</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">trash</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">HOOK:</span> <span class="nf">send-to-trash</span> <span class="nf">os</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> { [ os macosx? ] [ <span class="s">"trash.macosx"</span> ] }
</span></span><span class="line"><span class="cl"> { [ os unix? ] [ <span class="s">"trash.unix"</span> ] }
</span></span><span class="line"><span class="cl"> { [ os winnt? ] [ <span class="s">"trash.windows"</span> ] }
</span></span><span class="line"><span class="cl">} <span class="nb">cond </span>require
</span></span></code></pre></div><h3 id="trashmacosx">trash.macosx</h3>
<p>Next, we will create the <code>trash.macosx</code> vocabulary.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">alien.c-types</span> <span class="nn">alien.strings</span> <span class="nn">alien.syntax</span> <span class="nn">classes.struct</span>
</span></span><span class="line"><span class="cl"><span class="nn">core-foundation</span> <span class="nn">io.encodings.utf8</span> <span class="nn">kernel</span> <span class="nn">system</span> <span class="nn">trash</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">trash.macosx</span>
</span></span></code></pre></div><p>On the Mac OS, there are several methods of moving files to the trash. A
good <a href="https://www.cocoadev.com/index.pl?MoveToTrash">discussion</a> on
CocoaDev lists some of them. We are going to use the
<a href="https://docs.factorcode.org/content/article-alien.html">alien</a>
vocabulary to make calls into the <a href="https://developer.apple.com/library/mac/#documentation/Carbon/Reference/File_Manager/Reference/reference.html">File
Manager</a>
in the <code>CarbonCore.framework</code>. Some functions will return an <code>OSStatus</code>
flag (a signed 32-bit integer) to indicate if the operation succeeded.
We will add a
<a href="https://docs.factorcode.org/content/word-TYPEDEF__colon__,alien.syntax.html">TYPEDEF:</a>
for it, and then define the <code>GetMacOSStatusCommentString</code> function that
converts the status flag into a human readable error.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">TYPEDEF:</span> <span class="nf">SInt32</span> <span class="nf">OSStatus</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> char* <span class="nf">GetMacOSStatusCommentString</span> ( OSStatus err )
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">check-err</span> <span class="nf">( </span><span class="nv">err</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ GetMacOSStatusCommentString utf8 alien>string <span class="nb">throw </span>]
</span></span><span class="line"><span class="cl"> <span class="nb">unless-zero </span><span class="k">;
</span></span></span></code></pre></div><p>Many of the file operations act on an <code>FSRef</code> structure which represents
a path within the file system. We will define the
<code>FSPathMakeRefWithOptions</code> function which will allow us to create these
references:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">STRUCT:</span> <span class="nc">FSRef</span> { hidden UInt8[80] } <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">TYPEDEF:</span> <span class="nf">UInt32</span> <span class="nf">OptionBits</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> OSStatus <span class="nf">FSPathMakeRefWithOptions</span> (
</span></span><span class="line"><span class="cl"> UInt8* path,
</span></span><span class="line"><span class="cl"> OptionBits options,
</span></span><span class="line"><span class="cl"> FSRef* ref,
</span></span><span class="line"><span class="cl"> Boolean* isDirectory
</span></span><span class="line"><span class="cl">)
</span></span></code></pre></div><p>We can then make a <code><fs-ref></code> word for creating references, given a path
to a file (or directory).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">kFSPathMakeRefDoNotFollowLeafSymlink</span> <span class="m">0x01
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><fs-ref></span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">fs-ref</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> utf8 string>alien
</span></span><span class="line"><span class="cl"> kFSPathMakeRefDoNotFollowLeafSymlink
</span></span><span class="line"><span class="cl"> FSRef <struct>
</span></span><span class="line"><span class="cl"> [ <span class="no">f </span>FSPathMakeRefWithOptions check-err ] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div><p>There are several ways of “trashing” files, but one recommended way is
implemented by the <code>FSMoveObjectToTrashSync</code> function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">FUNCTION:</span> OSStatus <span class="nf">FSMoveObjectToTrashSync</span> (
</span></span><span class="line"><span class="cl"> FSRef* source,
</span></span><span class="line"><span class="cl"> FSRef* target,
</span></span><span class="line"><span class="cl"> OptionBits options
</span></span><span class="line"><span class="cl">)
</span></span></code></pre></div><p>Implementing the <code>send-to-trash</code> word is now pretty straightforward:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">kFSFileOperationDefaultOptions</span> <span class="m">0x00
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">macosx</span> <span class="nf">send-to-trash</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <fs-ref> <span class="no">f </span>kFSFileOperationDefaultOptions
</span></span><span class="line"><span class="cl"> FSMoveObjectToTrashSync check-err <span class="k">;
</span></span></span></code></pre></div><p>You can test this by creating a temporary file (e.g., <code>/tmp/foo</code>),
sending it to the trash, and then verifying that it exists by looking in
the Finder’s Trash.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USING:</span> <span class="nn">trash</span> <span class="nn">io.encodings.ascii</span> <span class="nn">io.files</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">""</span> <span class="s">"/tmp/foo"</span> ascii set-file-contents
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/tmp/foo"</span> send-to-trash
</span></span></code></pre></div><p><em>Note: This method does not appear to support the “Put Back”
functionality (to “undo” the trash operation). Perhaps there is some
metadata that we can add (or a different function we can call) that will
track the original file location so that the Finder knows where it
should be restored to.</em></p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/trash/macosx/macosx.factor">GitHub</a>.</p>
Configuration Files
https://re.factorcode.org/2011/01/configuration-files.html
Sat, 08 Jan 2011 16:40:00 -0800https://re.factorcode.org/2011/01/configuration-files.html<p><a href="https://www.factorcode.org">Factor</a> can use several configuration files
as part of its startup routine.</p>
<h3 id="factor-rc">factor-rc</h3>
<p>At startup, Factor looks for a <code>.factor-rc</code> (or <code>factor-rc</code> on Windows)
file in your <code>$HOME</code> directory. If found, it will attempt to run the
contents of this file as Factor source code.</p>
<p>For example, if you’d like to have Factor print <code>"Hello, World!"</code> when
it starts up, you can modify your <code>factor-rc</code> file to say:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USE:</span> <span class="nn">io</span>
</span></span><span class="line"><span class="cl"><span class="s">"Hello, World!"</span> <span class="nb">print
</span></span></span></code></pre></div><p>Then try and start Factor from the command-line and it should look
something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="no">$ factor</span>
</span></span><span class="line"><span class="cl">Loading $HOME/.factor-rc
</span></span><span class="line"><span class="cl">Hello, World!
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span>
</span></span></code></pre></div><p>More practically, if you want to always use a particular editor with
Factor (e.g., MacVim), you can <code>USE:</code> it in your <code>factor-rc</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USE:</span> <span class="nn">editors.macvim</span>
</span></span></code></pre></div><p>For more information, see
<a href="https://docs.factorcode.org/content/word-run-user-init,command-line.html">run-user-init</a>.</p>
<h3 id="factor-boot-rc">factor-boot-rc</h3>
<p>When performing the bootstrap process (e.g., making a new VM image),
Factor looks for the <code>.factor-boot-rc</code> (or <code>factor-boot-rc</code> on Windows)
file in your <code>$HOME</code> directory. In this file, you can use the
<a href="https://docs.factorcode.org/content/word-require,vocabs.loader.html">require</a>
word to load vocabularies you use frequently.</p>
<p>For example, if you’d like to have the
<a href="https://docs.factorcode.org/content/article-formatting.html">formatting</a>
vocabulary code loaded into the image (for the <code>printf</code> word), you can
add this to your <code>factor-boot-rc</code> file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USE:</span> <span class="nn">vocabs.loader</span>
</span></span><span class="line"><span class="cl"><span class="s">"formatting"</span> require
</span></span></code></pre></div><p>The next time you bootstrap Factor, the new image should have loaded the
<code>formatting</code> vocabulary.</p>
<p>For more information, see
<a href="https://docs.factorcode.org/content/word-run-bootstrap-init,command-line.html">run-bootstrap-init</a>.</p>
<h3 id="factor-roots">factor-roots</h3>
<p>By default, Factor looks in <code>$FACTOR/core</code>, <code>$FACTOR/basis</code>,
<code>$FACTOR/extra</code>, and <code>$FACTOR/work</code> for vocabularies. It is frequently
useful to specify additional vocabulary roots. Factor looks for the
<code>.factor-roots</code> (or <code>factor-roots</code> on Windows) in your <code>$HOME</code> directory
for additional vocabulary paths.</p>
<p>For example, if you want to use the code I’ve
<a href="https://github.com/mrjbq7/re-factor">written</a> as part of this blog, then
you can checkout the <code>re-factor</code> code somewhere. Then, add the full path
to the <code>re-factor</code> directory as a line in the <code>factor-roots</code> file. Next
time you run Factor, you should be able to <code>USE:</code> vocabularies from
<code>re-factor</code>.</p>
<p>For more information, see
<a href="https://docs.factorcode.org/content/word-load-vocab-roots,command-line.html">load-vocab-roots</a>
and
<a href="https://docs.factorcode.org/content/article-add-vocab-roots.html">add-vocab-roots</a>.</p>
Genetic Hello World
https://re.factorcode.org/2011/01/genetic-hello-world.html
Thu, 06 Jan 2011 15:28:00 -0800https://re.factorcode.org/2011/01/genetic-hello-world.html<p>A recent article called <a href="https://www.puremango.co.uk/2010/12/genetic-algorithm-for-hello-world/">Genetic Algorithm for Hello
World</a>
describes the basic concepts involved in programming genetic algorithms.
The original implementation is in Javascript, and has a nice <a href="https://www.puremango.co.uk/genetic-hello-world.html">online
simulator</a>. I
thought it would be fun to contribute an implementation in
<a href="https://www.factorcode.org">Factor</a>.</p>
<p>Genetic algorithms are generally comprised of the following concepts:</p>
<ol>
<li>A <strong>target</strong> chromosome which expresses a possible solution to the
problem</li>
<li>A <strong>fitness</strong> function which takes a chromosome as input and returns
a higher value for better solutions</li>
<li>A <strong>population</strong> which is just a set of many chromosomes</li>
<li>A <strong>selection</strong> method which determines how parents are selected for
breeding from the population</li>
<li>A <strong>crossover</strong> operation which determines how parents combine to
produce offspring</li>
<li>A <strong>mutation</strong> operation which determines how random deviations
manifest themselves</li>
</ol>
<p>First, we need to define the vocabularies that we will use and a
namespace:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">fry</span> <span class="nn">kernel</span> <span class="nn">make</span> <span class="nn">math</span> <span class="nn">math.order</span> <span class="nn">ranges</span> <span class="nn">random</span>
</span></span><span class="line"><span class="cl"><span class="nn">sequences</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">hello-ga</span>
</span></span></code></pre></div><h3 id="target">Target</h3>
<p>Our goal is to generate the target string “<code>Hello World!</code>”.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">TARGET</span> <span class="s">"Hello World!"</span>
</span></span></code></pre></div><h3 id="fitness">Fitness</h3>
<p>The fitness of a chromosome is the “sum of the character-wise
differences between the chromosome and the target string”.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fitness</span> <span class="nf">( </span><span class="nv">chromosome</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> TARGET <span class="m">0 </span>[ <span class="nb">- abs - </span>] <span class="nb">2reduce </span><span class="k">;
</span></span></span></code></pre></div><h3 id="population">Population</h3>
<p>Our starting population will be made up by “creating 400 totally random
12 character strings”.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">POPULATION</span> <span class="m">400
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-chromosome</span> <span class="nf">( -- </span><span class="nv">chromosome</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> TARGET <span class="nb">length </span>[ <span class="m">256 </span>random ] <span class="s">""</span> <span class="nb">replicate-as </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">random-population</span> <span class="nf">( -- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> POPULATION [ random-chromosome ] <span class="nb">replicate </span><span class="k">;
</span></span></span></code></pre></div><h3 id="selection">Selection</h3>
<p>We select two parents to survive or breed into the next generation by
“taking two members of the population at complete random and keep the
fittest as the first parent, then do the same with another two members
and keep the fittest as the other parent”.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fittest</span> <span class="nf">( </span><span class="nv">parent1</span> <span class="nv">parent2</span> <span class="nf">-- </span><span class="nv">parent1'</span> <span class="nv">parent2'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">2dup </span>[ fitness ] <span class="nb">bi@ > </span>[ <span class="nb">swap </span>] <span class="nb">when </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">tournament</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">parent</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ random ] <span class="nb">bi@ </span>fittest <span class="nb">nip </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parents</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">parent1</span> <span class="nv">parent2</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ tournament ] <span class="nb">bi@ </span><span class="k">;
</span></span></span></code></pre></div><h3 id="crossover">Crossover</h3>
<p>After choosing two parents, we want to “ensure their genes survive in
the next generation”. Sometimes (10% chance) the parents survive into
the next generation, but most frequently, we mix the parents by picking
a split point and then making one child from the head of parent1 plus
tail of parent2 and another from the head of parent2 plus tail of
parent1.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">CHILDREN-PROBABILITY</span> <span class="m">0.9
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">children?</span> <span class="nf">( -- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0.0 1.0 </span>uniform-random-float CHILDREN-PROBABILITY <span class="nb">< </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">head/tail</span> <span class="nf">( </span><span class="nv">seq1</span> <span class="nv">seq2</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">head1</span> <span class="nv">tail2</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">head </span>] [ <span class="nb">tail </span>] <span class="nb">bi-curry bi* </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">tail/head</span> <span class="nf">( </span><span class="nv">seq1</span> <span class="nv">seq2</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">tail1</span> <span class="nv">head2</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">tail </span>] [ <span class="nb">head </span>] <span class="nb">bi-curry bi* </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">children</span> <span class="nf">( </span><span class="nv">parent1</span> <span class="nv">parent2</span> <span class="nf">-- </span><span class="nv">child1</span> <span class="nv">child2</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> TARGET <span class="nb">length </span><span class="m">1 </span><span class="nb">- </span>[1..b) random
</span></span><span class="line"><span class="cl"> [ head/tail <span class="nb">append </span>] [ tail/head <span class="nb">prepend </span>] <span class="nb">3bi </span><span class="k">;
</span></span></span></code></pre></div><h3 id="mutation">Mutation</h3>
<p>When a mutation occurs (20% chance), we “choose a random position and
alter the character that’s there by up to 5 places”.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">MUTATION-PROBABILITY</span> <span class="m">0.2
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">mutation?</span> <span class="nf">( -- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0.0 1.0 </span>uniform-random-float MUTATION-PROBABILITY <span class="nb">< </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">mutate</span> <span class="nf">( </span><span class="nv">chromosome</span> <span class="nf">-- </span><span class="nv">chromosome'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>random <span class="nb">over </span>[ <span class="m">-5 5 </span>[a..b] random <span class="nb">+ </span>] <span class="nb">change-nth </span><span class="k">;
</span></span></span></code></pre></div><h3 id="simulation">Simulation</h3>
<p>Perform a single generation by selecting the parents (or children) and
allowing mutations to occur. We do this in such a way that the number of
chromosomes in each generation remains constant.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(1generation)</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">child1</span> <span class="nv">child2</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> parents children? [ children ] <span class="nb">when
</span></span></span><span class="line"><span class="cl"> mutation? [ [ mutate ] <span class="nb">bi@ </span>] <span class="nb">when </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">1generation</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span><span class="m">2 </span><span class="nb">/ </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> '[ _ [ _ (1generation) , , ] <span class="nb">times </span>] { } make <span class="k">;
</span></span></span></code></pre></div><p>Computing all generations required to achieve the target string is
fairly easy:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">finished?</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> TARGET <span class="nb">swap member? </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-generations</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seqs</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ 1generation <span class="nb">dup </span>, <span class="nb">dup </span>finished? <span class="nb">not </span>] <span class="nb">loop drop
</span></span></span><span class="line"><span class="cl"> ] { } make <span class="k">;
</span></span></span></code></pre></div><h3 id="try-it">Try It</h3>
<p>Compute all generations for a random population.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> random-population all-generations
</span></span></code></pre></div><p>See how many generations it took.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup length </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">56
</span></span></span></code></pre></div><p>See how the fitness of the best chromosome changes over the generations.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup </span>[ [ fitness ] [ max ] <span class="nb">map-reduce </span>] <span class="nb">map </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> <span class="m">-414
</span></span></span><span class="line"><span class="cl"> <span class="m">-382
</span></span></span><span class="line"><span class="cl"> <span class="m">-329
</span></span></span><span class="line"><span class="cl"> <span class="m">-288
</span></span></span><span class="line"><span class="cl"> <span class="m">-271
</span></span></span><span class="line"><span class="cl"> <span class="m">-238
</span></span></span><span class="line"><span class="cl"> <span class="m">-217
</span></span></span><span class="line"><span class="cl"> <span class="m">-191
</span></span></span><span class="line"><span class="cl"> <span class="m">-169
</span></span></span><span class="line"><span class="cl"> <span class="m">-167
</span></span></span><span class="line"><span class="cl"> <span class="m">-160
</span></span></span><span class="line"><span class="cl"> <span class="m">-143
</span></span></span><span class="line"><span class="cl"> <span class="m">-134
</span></span></span><span class="line"><span class="cl"> <span class="m">-113
</span></span></span><span class="line"><span class="cl"> <span class="m">-119
</span></span></span><span class="line"><span class="cl"> <span class="m">-94
</span></span></span><span class="line"><span class="cl"> <span class="m">-83
</span></span></span><span class="line"><span class="cl"> <span class="m">-79
</span></span></span><span class="line"><span class="cl"> <span class="m">-63
</span></span></span><span class="line"><span class="cl"> <span class="m">-59
</span></span></span><span class="line"><span class="cl"> <span class="m">-61
</span></span></span><span class="line"><span class="cl"> <span class="m">-54
</span></span></span><span class="line"><span class="cl"> <span class="m">-50
</span></span></span><span class="line"><span class="cl"> <span class="m">-43
</span></span></span><span class="line"><span class="cl"> <span class="m">-39
</span></span></span><span class="line"><span class="cl"> <span class="m">-38
</span></span></span><span class="line"><span class="cl"> <span class="m">-36
</span></span></span><span class="line"><span class="cl"> <span class="m">-31
</span></span></span><span class="line"><span class="cl"> <span class="m">-32
</span></span></span><span class="line"><span class="cl"> <span class="m">-32
</span></span></span><span class="line"><span class="cl"> <span class="m">-28
</span></span></span><span class="line"><span class="cl"> <span class="m">-27
</span></span></span><span class="line"><span class="cl"> <span class="m">-22
</span></span></span><span class="line"><span class="cl"> <span class="m">-18
</span></span></span><span class="line"><span class="cl"> <span class="m">-15
</span></span></span><span class="line"><span class="cl"> <span class="m">-15
</span></span></span><span class="line"><span class="cl"> <span class="m">-14
</span></span></span><span class="line"><span class="cl"> <span class="m">-13
</span></span></span><span class="line"><span class="cl"> <span class="m">-13
</span></span></span><span class="line"><span class="cl"> <span class="m">-11
</span></span></span><span class="line"><span class="cl"> <span class="m">-11
</span></span></span><span class="line"><span class="cl"> <span class="m">-10
</span></span></span><span class="line"><span class="cl"> <span class="m">-8
</span></span></span><span class="line"><span class="cl"> <span class="m">-7
</span></span></span><span class="line"><span class="cl"> <span class="m">-7
</span></span></span><span class="line"><span class="cl"> <span class="m">-7
</span></span></span><span class="line"><span class="cl"> <span class="m">-6
</span></span></span><span class="line"><span class="cl"> <span class="m">-6
</span></span></span><span class="line"><span class="cl"> <span class="m">-4
</span></span></span><span class="line"><span class="cl"> <span class="m">-4
</span></span></span><span class="line"><span class="cl"> <span class="m">-3
</span></span></span><span class="line"><span class="cl"> <span class="m">-3
</span></span></span><span class="line"><span class="cl"> <span class="m">-2
</span></span></span><span class="line"><span class="cl"> <span class="m">-2
</span></span></span><span class="line"><span class="cl"> <span class="m">-2
</span></span></span><span class="line"><span class="cl"> <span class="m">0
</span></span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>The code is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/hello-ga/hello-ga.factor">GitHub</a>.</p>
Estimating CPU Speed
https://re.factorcode.org/2010/11/estimating-cpu-speed.html
Mon, 29 Nov 2010 22:19:00 -0800https://re.factorcode.org/2010/11/estimating-cpu-speed.html<p><a href="https://www.factorcode.org">Factor</a> contains a nice DSL for writing
assembly code. I thought it would be fun to investigate how it works by
accessing the CPU’s <a href="https://en.wikipedia.org/wiki/Time_Stamp_Counter">Time Stamp
Counter</a> to estimate
CPU speed.</p>
<p>The X86 instruction for accessing the timestamp value (incremented every
CPU tick) is called RDTSC (a 2-byte instruction <code>0x0f 0x31</code>). Some C
code for calling the 32-bit or 64-bit versions of this looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#if defined(__i386__)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">__inline__</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span> <span class="nf">rdtsc</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span> <span class="kt">int</span> <span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">__asm__</span> <span class="nf">__volatile__</span> <span class="p">(</span><span class="s">".byte 0x0f, 0x31"</span> <span class="o">:</span> <span class="s">"=A"</span> <span class="p">(</span><span class="n">x</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#elif defined(__x86_64__)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">__inline__</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span> <span class="nf">rdtsc</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">hi</span><span class="p">,</span> <span class="n">lo</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">__asm__</span> <span class="nf">__volatile__</span> <span class="p">(</span><span class="s">"rdtsc"</span> <span class="o">:</span> <span class="s">"=a"</span><span class="p">(</span><span class="n">lo</span><span class="p">),</span> <span class="s">"=d"</span><span class="p">(</span><span class="n">hi</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">(</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">)</span><span class="n">lo</span><span class="p">)</span><span class="o">|</span><span class="p">(</span> <span class="p">((</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">)</span><span class="n">hi</span><span class="p">)</span><span class="o"><<</span><span class="mi">32</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span></code></pre></div><p>Factor provides utilities for <a href="https://docs.factorcode.org/content/article-alien-assembly.html">calling arbitrary assembly
code</a> in
the <a href="https://docs.factorcode.org/content/vocab-alien.html">alien</a>
vocabulary. Using this, we can create corresponding Factor code
(supporting both 32 and 64 bits):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">alien</span> <span class="nn">alien.c-types</span> <span class="nn">cpu.x86.assembler</span>
</span></span><span class="line"><span class="cl"><span class="nn">cpu.x86.assembler.operands</span> <span class="nn">system</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">HOOK:</span> <span class="nf">rdtsc</span> <span class="nf">cpu</span> <span class="nf">( -- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">x86.32</span> <span class="nf">rdtsc</span>
</span></span><span class="line"><span class="cl"> longlong { } cdecl [
</span></span><span class="line"><span class="cl"> RDTSC
</span></span><span class="line"><span class="cl"> ] alien-assembly <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">M:</span> <span class="nc">x86.64</span> <span class="nf">rdtsc</span>
</span></span><span class="line"><span class="cl"> longlong { } cdecl [
</span></span><span class="line"><span class="cl"> RAX <span class="m">0 </span>MOV
</span></span><span class="line"><span class="cl"> RDTSC
</span></span><span class="line"><span class="cl"> RDX <span class="m">32 </span>SHL
</span></span><span class="line"><span class="cl"> RAX RDX OR
</span></span><span class="line"><span class="cl"> ] alien-assembly <span class="k">;
</span></span></span></code></pre></div><p>You can see in the implementation above how Factor uses the type of CPU
(contained in the <code>cpu</code> variable) to dispatch on the correct version of
the <code>rdtsc</code> word.</p>
<p>To estimate CPU speed, we will need to define two “benchmarking” words:</p>
<ol>
<li>Calculate the number of CPU ticks it takes to execute some Factor
code.</li>
<li>Calculate the time it takes to execute some Factor code.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">system</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">#ticks</span> <span class="nf">( </span><span class="nv">quot</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> rdtsc [ <span class="nb">call </span>rdtsc ] <span class="nb">dip - </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">#nanos</span> <span class="nf">( </span><span class="nv">quot</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> nano-count [ <span class="nb">call </span>nano-count ] <span class="nb">dip - </span><span class="k">; inline
</span></span></span></code></pre></div><p>We can then create a “busy loop” that runs for some time, then estimates
CPU speed as ticks-per-second:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">busy-loop</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="m">100000000 </span>[ <span class="m">1 </span><span class="nb">- dup </span><span class="m">0 </span><span class="nb">> </span>] <span class="nb">loop drop </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">cpu-speed</span> <span class="nf">( -- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ busy-loop ] #nanos ] #ticks <span class="nb">swap / </span><span class="m">1000000000.0 </span><span class="nb">* </span><span class="k">;
</span></span></span></code></pre></div><p>Running this on my MacBook Pro (with a 2.66 GHz processor) produces this
estimate:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> cpu-speed <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">2660324566.190773
</span></span></span></code></pre></div><p><em>The <code>rdtsc</code> and <code>#ticks</code> words are distributed with Factor as
<code>instruction-count</code> and <code>count-instructions</code> and are available in the
<code>cpu.x86.features</code> vocabulary. The <code>#nanos</code> word is called <code>benchmark</code>
and is available in the <code>tools.time</code> vocabulary.</em></p>
GitHub supports Factor!
https://re.factorcode.org/2010/11/github-supports-factor.html
Wed, 17 Nov 2010 20:17:00 -0800https://re.factorcode.org/2010/11/github-supports-factor.html<p><em>I’ve been working with GitHub over the past three months to get
<a href="https://www.factorcode.org">Factor</a> added as a recognized programming
language. The main roadblock was proper syntax highlighting and
deployment within the GitHub environment. Since GitHub uses the
<a href="https://pygments.org">pygments</a> project, I implemented (and
<a href="https://bitbucket.org/birkenfeld/pygments-main/changeset/af39e39b4c10">contributed</a>)
a Factor lexer which will be part of the next release of pygments
(version 1.4) and is available now on their <a href="https://bitbucket.org/birkenfeld/pygments-main">development
branch</a>.</em></p>
<p>As of today, GitHub has officially <a href="https://www.github.com/languages/Factor">added
support</a> for the Factor
programming language. In celebration of this, I wanted to highlight some
of the projects using Factor on GitHub. As you might know, I’ve been
keeping code examples and tutorials from my blog posts on my
<a href="https://github.com/mrjbq7/re-factor">GitHub</a>.</p>
<p>The <a href="https://github.com/factor/factor">main repository</a> (where a lot
of the library development occurs) has 114 watches, 47 forks, and at
least 17 unofficial copies.</p>
<p>There are a variety of other projects and tutorials:</p>
<ul>
<li><a href="https://github.com/jckarter/Trixel">Trixel (low resolution voxel renderer)</a></li>
<li><a href="https://github.com/inforichland/Joy">Implementation of Joy in Factor</a></li>
<li><a href="https://github.com/malu/factor-blog-gitutorial">Factor Blog Tutorial</a></li>
<li><a href="https://github.com/mikejs/factor-problems">99 Prolog Problems in Factor</a></li>
<li><a href="https://github.com/okomok/ainan">Factor Training Ground</a></li>
<li><a href="https://github.com/mikejs/factor-sunlightapi">Sunlight Labs API in Factor</a></li>
<li><a href="https://github.com/wookay/factor-bert">BERT in Factor</a></li>
<li><a href="https://github.com/jckarter/rowsdower">Static Blog Generator</a></li>
<li><a href="https://github.com/caesarhu/fgo">Go implemented in Factor</a></li>
<li><a href="https://github.com/jonenst/factorino">Controlling a Robotino from Factor</a></li>
<li><a href="https://github.com/mjm/factor-code">Math and crypto code.</a></li>
<li><a href="https://github.com/graycode/sensors">Sensors</a> and <a href="https://github.com/graycode/radar">Radar</a></li>
<li><a href="https://github.com/harold/work">Turtles, SVG, Image gadgets</a></li>
<li><a href="https://github.com/tubbymctubs/factor-graph">Graphing gadgets</a></li>
<li><a href="https://github.com/sykopomp/facile">Facile: a CouchDB view server in Factor</a></li>
</ul>
<p>Some patches (probably a little outdated):</p>
<ul>
<li><a href="https://github.com/mp248778/factor-regexp-minimalize">New regexp minimalization code for Factor</a></li>
<li><a href="https://github.com/ageldama/factor-io.encodings.korean">Korean I/O support.</a></li>
<li><a href="https://github.com/caesarhu/factor-xim">X input method</a></li>
</ul>
<p>And, some related projects:</p>
<ul>
<li><a href="https://github.com/Leonidas-from-XIV/aur-factor">Packaging Factor for ArchLinux</a></li>
<li><a href="https://github.com/leto/kea">Port of Factor to the Parrot Virtual Machine</a></li>
</ul>
<p>If you know of other Factor projects with source code, let me know!</p>
<p><em>By the way, repositories get classified on push, so as the Factor
projects get pushed to, they will be added to the
<a href="https://www.github.com/languages/Factor">list</a>.</em></p>
Syntax Highlighting
https://re.factorcode.org/2010/10/syntax-highlighting.html
Thu, 28 Oct 2010 08:28:00 -0700https://re.factorcode.org/2010/10/syntax-highlighting.html<p><em>Update: I modified the code examples below to use the new <code>colors.hex</code>
vocabulary.</em></p>
<p>It’s a sometimes overlooked feature that Factor contains a syntax
highlighting vocabulary that supports a decent number of programming
languages. The vocabulary is called
<a href="https://docs.factorcode.org/content/vocab-xmode.html">xmode</a>, and is
used to highlight entries submitted to Factor’s
<a href="https://paste.factorcode.org/">pastebin</a>.</p>
<p>I thought it would be fun to use
<a href="https://docs.factorcode.org/content/vocab-xmode.html">xmode</a> to
implement syntax highlighting in the listener. When we’re done, it will
look something like this:</p>
<p>
<img src="https://re.factorcode.org/images/2010-10-28-syntax-highlighting-rot13.png" alt="" width="444" height="378" />
</p>
<p>First, we will need to use several vocabularies:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">assocs</span> <span class="nn">colors</span> <span class="nn">io</span> <span class="nn">io.encodings.utf8</span> <span class="nn">io.files</span>
</span></span><span class="line"><span class="cl"><span class="nn">io.styles</span> <span class="nn">kernel</span> <span class="nn">literals</span> <span class="nn">locals</span> <span class="nn">math</span> <span class="nn">math.parser</span> <span class="nn">sequences</span>
</span></span><span class="line"><span class="cl"><span class="nn">xmode.catalog</span> <span class="nn">xmode.marker</span> <span class="k">;
</span></span></span></code></pre></div><p>We define named styles that will apply to the tokens that are parsed by the
syntax highlighter. For convenience, we will use the
<a href="https://docs.factorcode.org/content/article-colors.html">colors</a> vocabulary to
convert hexadecimal values to <code>color</code> tuples to build the stylesheet. In the
stylesheet below, we use the same colors that are used in the
<a href="https://paste.factorcode.org/">pastebin</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">STYLES</span> H{
</span></span><span class="line"><span class="cl"> { <span class="s">"NULL"</span> H{ { foreground COLOR: #000000 } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"COMMENT1"</span> H{ { foreground COLOR: #cc0000 } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"COMMENT2"</span> H{ { foreground COLOR: #ff8400 } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"COMMENT3"</span> H{ { foreground COLOR: #6600cc } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"COMMENT4"</span> H{ { foreground COLOR: #cc6600 } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"DIGIT"</span> H{ { foreground COLOR: #ff0000 } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"FUNCTION"</span> H{ { foreground COLOR: #9966ff } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"INVALID"</span> H{ { background COLOR: #ffffcc }
</span></span><span class="line"><span class="cl"> { foreground COLOR: #ff0066 } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"KEYWORD1"</span> H{ { foreground COLOR: #006699 }
</span></span><span class="line"><span class="cl"> { font-style bold } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"KEYWORD2"</span> H{ { foreground COLOR: #009966 }
</span></span><span class="line"><span class="cl"> { font-style bold } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"KEYWORD3"</span> H{ { foreground COLOR: #0099ff }
</span></span><span class="line"><span class="cl"> { font-style bold } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"KEYWORD4"</span> H{ { foreground COLOR: #66ccff }
</span></span><span class="line"><span class="cl"> { font-style bold } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"LABEL"</span> H{ { foreground COLOR: #02b902 } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"LITERAL1"</span> H{ { foreground COLOR: #ff00cc } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"LITERAL2"</span> H{ { foreground COLOR: #cc00cc } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"LITERAL3"</span> H{ { foreground COLOR: #9900cc } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"LITERAL4"</span> H{ { foreground COLOR: #6600cc } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"MARKUP"</span> H{ { foreground COLOR: #0000ff } } }
</span></span><span class="line"><span class="cl"> { <span class="s">"OPERATOR"</span> H{ { foreground COLOR: #000000 }
</span></span><span class="line"><span class="cl"> { font-style bold } } }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>The
<a href="https://docs.factorcode.org/content/vocab-xmode.catalog.html">xmode.catalog</a>
vocabulary provides support for looking up the type (or “mode”) of the
file. The
<a href="https://docs.factorcode.org/content/vocab-xmode.marker.html">xmode.marker</a>
vocabulary then provides support for converting each line into a stream
of tokens. Each token allows access to a named style. Once we have the
name of the appropriate style, we can then look it up and format the
output.</p>
<p>Putting that all together, we can implement the <code>highlight.</code> word.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">highlight-tokens</span> <span class="nf">( </span><span class="nv">tokens</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ str>> ] [ id>> ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> [ name>> STYLES <span class="nb">at </span>] [ <span class="no">f </span>] <span class="nb">if* </span>format
</span></span><span class="line"><span class="cl"> ] <span class="nb">each nl </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">highlight-lines</span> <span class="nf">( </span><span class="nv">lines</span> <span class="nv">mode</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <span class="no">f </span>] <span class="nb">2dip </span>load-mode [
</span></span><span class="line"><span class="cl"> tokenize-line highlight-tokens
</span></span><span class="line"><span class="cl"> ] <span class="nb">curry each drop </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">highlight.</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> path utf8 file-lines [
</span></span><span class="line"><span class="cl"> path <span class="nb">over first </span>find-mode highlight-lines
</span></span><span class="line"><span class="cl"> ] <span class="nb">unless-empty </span><span class="k">;
</span></span></span></code></pre></div><p>You should be able to paste the above code examples into your listener,
to try it for yourself.</p>
Trending GitHub Projects
https://re.factorcode.org/2010/10/trending-github-projects.html
Mon, 25 Oct 2010 17:02:00 -0700https://re.factorcode.org/2010/10/trending-github-projects.html<p>This weekend someone posted an
<a href="https://agiliq.com/blog/2010/10/getting-trending-github-projects-via/">article</a>
describing using <a href="https://www.python.org">Python</a> with
<a href="https://developer.yahoo.com/yql/">YQL</a> to parse
<a href="https://repopular.com/">Repopular</a> to retrieve a list of popular GitHub
projects. Since mashups are all the rage these days, I thought I would
implement this in <a href="https://www.factorcode.org">Factor</a>.</p>
<p>
<img src="https://re.factorcode.org/images/2010-10-25-trending-github-projects-repopular.png" alt="" width="304" height="92" />
</p>
<h3 id="the-yahoo-way">The Yahoo! way</h3>
<p>The YQL that was used in the original article is:</p>
<pre tabindex="0"><code>use 'https://yqlblog.net/samples/data.html.cssselect.xml'
as data.html.cssselect;
select * from data.html.cssselect
where url="repopular.com"
and css="div.pad a"
</code></pre><p>We can use the
<a href="https://docs.factorcode.org/content/vocab-http.client.html">http.client</a>
to send this query to Yahoo!, parse the returned JSON data using
<a href="https://docs.factorcode.org/content/vocab-json.reader.html">json.reader</a>,
and extract the HREFs to the popular projects. We can then filter them
for the links which point to <a href="https://github.com">GitHub</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">assocs</span> <span class="nn">http.client</span> <span class="nn">json.reader</span> <span class="nn">kernel</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">the-yahoo-way</span> <span class="nf">( -- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://query.yahooapis.com/v1/public/yql?q=use%20'http%3A%2F%2Fyqlb
</span></span></span><span class="line"><span class="cl"><span class="s">log.net%2Fsamples%2Fdata.html.cssselect.xml'%20as%20data.html.cssselect%
</span></span></span><span class="line"><span class="cl"><span class="s">3B%20select%20*%20from%20data.html.cssselect%20where%20url%3D%22repopula
</span></span></span><span class="line"><span class="cl"><span class="s">r.com%22%20and%20css%3D%22div.pad%20a%22&format=json&diagnostics=true&ca
</span></span></span><span class="line"><span class="cl"><span class="s">llback="</span>
</span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>json> { <span class="s">"query"</span> <span class="s">"results"</span> <span class="s">"results"</span> <span class="s">"a"</span> }
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap at </span>] <span class="nb">each </span>[ <span class="s">"href"</span> <span class="nb">swap at </span>] <span class="nb">map </span>
</span></span><span class="line"><span class="cl"> [ <span class="s">"https://github.com"</span> <span class="nb">head? </span>] <span class="nb">filter </span><span class="k">;
</span></span></span></code></pre></div><p>We can run this to see what the current trending projects are on GitHub.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> the-yahoo-way [ <span class="m">. </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"><span class="s">"https://github.com/sinatra/sinatra"</span>
</span></span><span class="line"><span class="cl"><span class="s">"https://github.com/Sutto/barista"</span>
</span></span><span class="line"><span class="cl"><span class="s">"https://github.com/pypy/pypy"</span>
</span></span><span class="line"><span class="cl"><span class="s">"https://github.com/dysinger/apparatus"</span>
</span></span><span class="line"><span class="cl"><span class="s">"https://github.com/videlalvaro/Thumper"</span>
</span></span><span class="line"><span class="cl"><span class="s">"https://github.com/alunny/sleight"</span>
</span></span><span class="line"><span class="cl"><span class="s">"https://github.com/vimpr/vimperator-plugins"</span>
</span></span></code></pre></div><h3 id="the-other-way">The other way</h3>
<p>We can use the <a href="">html.parser</a> vocabulary to do it another way. Given
some knowledge of the HTML returned by Repopular, we can extract the
HREFs directly:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">assocs</span> <span class="nn">html.parser</span> <span class="nn">http.client</span> <span class="nn">kernel</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">the-other-way</span> <span class="nf">( -- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://repopular.com"</span> http-get <span class="nb">nip </span>parse-html
</span></span><span class="line"><span class="cl"> [ [ name>> <span class="s">"aside"</span> <span class="nb">= </span>] <span class="nb">find drop </span>]
</span></span><span class="line"><span class="cl"> [ [ name>> <span class="s">"aside"</span> <span class="nb">= </span>] <span class="nb">find-last drop </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb"><slice> </span>] <span class="nb">tri
</span></span></span><span class="line"><span class="cl"> [ name>> <span class="s">"a"</span> <span class="nb">= </span>] <span class="nb">filter
</span></span></span><span class="line"><span class="cl"> [ attributes>> <span class="s">"href"</span> <span class="nb">swap at </span>] <span class="nb">map
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"https://github.com"</span> <span class="nb">head? </span>] <span class="nb">filter </span><span class="k">;
</span></span></span></code></pre></div><p>We can see it produces the same results:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> the-other-way [ <span class="m">. </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"><span class="s">"https://github.com/sinatra/sinatra"</span>
</span></span><span class="line"><span class="cl"><span class="s">"https://github.com/Sutto/barista"</span>
</span></span><span class="line"><span class="cl"><span class="s">"https://github.com/pypy/pypy"</span>
</span></span><span class="line"><span class="cl"><span class="s">"https://github.com/dysinger/apparatus"</span>
</span></span><span class="line"><span class="cl"><span class="s">"https://github.com/videlalvaro/Thumper"</span>
</span></span><span class="line"><span class="cl"><span class="s">"https://github.com/alunny/sleight"</span>
</span></span><span class="line"><span class="cl"><span class="s">"https://github.com/vimpr/vimperator-plugins"</span>
</span></span></code></pre></div>Longest Palindrome
https://re.factorcode.org/2010/10/longest-palindrome.html
Sun, 24 Oct 2010 14:04:00 -0700https://re.factorcode.org/2010/10/longest-palindrome.html<p>Greplin issued a <a href="https://challenge.greplin.com/">programming challenge</a>
recently, where the first question involved finding the longest
palindrome substring. It was then
<a href="https://programmingpraxis.com/2010/10/15/find-the-longest-palindrome-in-a-string/">posted</a>
as a challenge to the Programming Praxis blog, and I thought I would
contribute a solution in <a href="https://www.factorcode.org">Factor</a>.</p>
<p>First, some vocabularies that we will be using:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">fry</span> <span class="nn">kernel</span> <span class="nn">locals</span> <span class="nn">make</span> <span class="nn">ranges</span> <span class="nn">sequences</span> <span class="nn">unicode.case</span>
</span></span><span class="line"><span class="cl"><span class="nn">unicode.categories</span> <span class="k">;
</span></span></span></code></pre></div><p>As part of the Factor language tutorial, the <a href="https://docs.factorcode.org/content/article-first-program.html">first
program</a>
many people write in Factor is a word for detecting if something is a
<a href="https://en.wikipedia.org/wiki/Palindrome">palindrome</a>. The
implementation of <code>palindrome?</code> (extended to support case-insensitive
comparisons using the
<a href="https://docs.factorcode.org/content/vocab-unicode.html">unicode</a>
vocabulary) looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">normalize</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">) </span>[ Letter? ] <span class="nb">filter </span>>lower <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">palindrome?</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">) </span>normalize <span class="nb">dup reverse = </span><span class="k">;
</span></span></span></code></pre></div><p>The “obvious” (but not that fast) way to solve the problem is to examine
every possible substring, adding to a list if it is a palindrome. The
list of palindrome substrings can then be used to answer the question.
This is how we’ll implement it.</p>
<p>I thought it would be useful to split the problem into two steps. First,
we need a way to enumerate all possible substrings (not including the
“empty” substring), applying a quotation to each in turn.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">each-subseq</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">seq</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">...</span> <span class="nv">x</span> <span class="nf">-- </span><span class="nv">...</span> <span class="nf">) -- </span><span class="nv">...</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> seq <span class="nb">length </span>[0,b] [
</span></span><span class="line"><span class="cl"> :> from
</span></span><span class="line"><span class="cl"> from seq <span class="nb">length </span>(a..b] [
</span></span><span class="line"><span class="cl"> :> to
</span></span><span class="line"><span class="cl"> from to seq <span class="nb">subseq </span>quot call( x -- )
</span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>You can try this out in the listener, to see how it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"abc"</span> [ <span class="m">. </span>] each-subseq
</span></span><span class="line"><span class="cl"><span class="s">"a"</span>
</span></span><span class="line"><span class="cl"><span class="s">"ab"</span>
</span></span><span class="line"><span class="cl"><span class="s">"abc"</span>
</span></span><span class="line"><span class="cl"><span class="s">"b"</span>
</span></span><span class="line"><span class="cl"><span class="s">"bc"</span>
</span></span><span class="line"><span class="cl"><span class="s">"c"</span>
</span></span></code></pre></div><p>Once we have that, it’s pretty easy to build a word to look for
palindrome substrings:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">palindromes</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>palindrome? [ , ] [ <span class="nb">drop </span>] <span class="nb">if </span>] each-subseq
</span></span><span class="line"><span class="cl"> ] { } make <span class="k">;
</span></span></span></code></pre></div><p>We can use the <code>longest</code> word that I implemented for my
<a href="https://re.factorcode.org/2010/08/anagrams.html">anagrams</a> post to
find the longest palindrome substring:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">longest</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">subseq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span>[ <span class="nb">length </span>max ] <span class="nb">reduce </span>'[ <span class="nb">length </span>_ <span class="nb">= </span>] <span class="nb">filter </span><span class="k">;
</span></span></span></code></pre></div><p>Using this on the 1169-character string from the original challenge, we
find 52 unique palindromes of 2 or more characters. The longest
palindrome substring I found was a 7-character sequence.</p>
Presentations in Factor
https://re.factorcode.org/2010/10/presentations-in-factor.html
Fri, 22 Oct 2010 22:54:00 -0700https://re.factorcode.org/2010/10/presentations-in-factor.html<p>Just a few days ago, Daniel Ehrenberg gave a presentation at the Dynamic
Language Symposium 2010 in Reno, Nevada. It was quite good, and gave a
nice overview of the different language features that
<a href="https://www.factorcode.org">Factor</a> supports.</p>
<p>Like many of the talks about Factor, it was made (<em>and presented!</em>)
using Factor. Specifically, the
<a href="https://docs.factorcode.org/content/vocab-slides.html">slides</a>
vocabulary, which is a simple DSL for presentations. To show how it
works, let’s just jump directly to an example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USING:</span> <span class="nn">help.markup</span> <span class="nn">slides</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> {
</span></span><span class="line"><span class="cl"> { $slide <span class="s">"Factor!"</span>
</span></span><span class="line"><span class="cl"> { $url <span class="s">"https://factorcode.org"</span> }
</span></span><span class="line"><span class="cl"> <span class="s">"Development started in 2003"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Open source (BSD license)"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Influenced by Forth, Lisp, and Smalltalk"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Blurs the line between language and library"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Interactive development"</span>
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> } <span class="s">"Slides"</span> slides-window
</span></span></code></pre></div><p>When run, this pops up a window that can be used for presentations. It
will look something like this:</p>
<p>
<img src="https://re.factorcode.org/images/2010-10-22-presentations-in-factor-slides.png" alt="" width="931" height="605" />
</p>
<p>You can control the slideshow window using the keyboard:</p>
<ul>
<li>Press <code>DOWN</code> to move to the next slide.</li>
<li>Press <code>UP</code> to move to the previous slide.</li>
<li>Press <code>F</code> to toggle full-screen mode.</li>
</ul>
<p>In addition, the
<a href="https://docs.factorcode.org/content/vocab-slides.html">slides</a>
vocabulary supports some other features, such as embedding code and
clickable links to vocabularies or words. Here is an example which does
both of these:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> {
</span></span><span class="line"><span class="cl"> { $slide <span class="s">"Code!"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Try clicking on these:"</span>
</span></span><span class="line"><span class="cl"> { $code <span class="s">"2 2 +"</span> }
</span></span><span class="line"><span class="cl"> { $vocab-link <span class="s">"sequences"</span> }
</span></span><span class="line"><span class="cl"> { $link <span class="nb">nth </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> } <span class="s">"Slides"</span> slides-window
</span></span></code></pre></div><p>If you want to customize the look-and-feel of the presentation, you need
to modify the
<a href="https://docs.factorcode.org/content/word-stylesheet%2Cslides.html">stylesheet</a>
used to render the presentation.</p>
<p>To see it in action, you can watch Slava Pestov give a <a href="https://www.youtube.com/watch?v=f_0QlhYlS8g">Google Tech Talk
on Factor</a>. The slides he
used are available in the Factor project under
<a href="https://github.com/factor/factor-talks/tree/main/google-tech-talk">google-tech-talk</a>.</p>
<p>Slides for most of the other talks that have been given are also
available (they are a nice way to get a quick overview of the “big
features” in Factor):</p>
<ul>
<li><a href="https://github.com/slavapestov/boston-lisp-talk">boston-lisp-talk</a></li>
<li><a href="https://github.com/factor/factor-talks/tree/main/chicago-talk">chicago-talk</a></li>
<li><a href="https://github.com/slavapestov/emerging-langs-talk">emerging-langs-talk</a></li>
<li><a href="https://github.com/factor/factor-talks/tree/main/galois-talk">galois-talk</a></li>
<li><a href="https://github.com/factor/factor-talks/tree/main/jvm-summit-talk">jvm-summit-talk</a></li>
<li><a href="https://github.com/factor/factor-talks/tree/main/minneapolis-talk">minneapolis-talk</a></li>
<li><a href="https://github.com/factor/factor-talks/tree/main/otug-talk">otug-talk</a></li>
<li><a href="https://github.com/factor/factor-talks/tree/main/tc-lisp-talk">tc-lisp-talk</a></li>
<li><a href="https://github.com/factor/factor-talks/tree/main/vpri-talk">vpri-talk</a></li>
</ul>
<p>It’s so cool, even Zed Shaw gave a
<a href="https://vimeo.com/2723800">presentation</a> using the
<a href="https://docs.factorcode.org/content/vocab-slides.html">slides</a>
vocabulary.</p>
Text-to-PDF
https://re.factorcode.org/2010/10/text-to-pdf.html
Fri, 15 Oct 2010 08:17:00 -0700https://re.factorcode.org/2010/10/text-to-pdf.html<p>In this article, we will be building step-by-step a program for
converting text files into PDF. The PDF specification is at version 1.7
(approximately 750 pages plus some supplements) and is <a href="https://www.adobe.com/devnet/pdf/pdf_reference.html">available for
download</a> from the
Adobe website.</p>
<blockquote>
<p><em>The entire solution listed below is approximately 140 lines of code,
and compares favorably to a 600 line <a href="https://code.activestate.com/recipes/532908-text-to-pdf-converter-rewrite/">Python
version</a>
and a 450 line <a href="https://www.eprg.org/pdfcorner/text2pdf/">C version</a>.</em></p>
</blockquote>
<p>First, we will need a list of imports and a namespace:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">assocs</span> <span class="nn">calendar</span> <span class="nn">combinators</span> <span class="nn">environment</span> <span class="nn">formatting</span>
</span></span><span class="line"><span class="cl"><span class="nn">grouping</span> <span class="nn">io</span> <span class="nn">io.files</span> <span class="nn">kernel</span> <span class="nn">make</span> <span class="nn">math</span> <span class="nn">ranges</span> <span class="nn">sequences</span>
</span></span><span class="line"><span class="cl"><span class="nn">splitting</span> <span class="nn">xml.entities</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">text-to-pdf</span>
</span></span></code></pre></div><p>PDF files are essentially text files that contain numbered objects with
display instructions and a cross-reference table that allows random
access to those objects. We will need a word to create an object from
its contents and number (e.g., start with <code>n 0 obj</code> and end with
<code>endobj</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pdf-object</span> <span class="nf">( </span><span class="nv">str</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"%d 0 obj\n"</span> sprintf <span class="s">"\nendobj"</span> <span class="nb">surround </span><span class="k">;
</span></span></span></code></pre></div><p>References (used to provide pointers to objects) are of the form <code>n 0 R</code>
where <code>n</code> is the number of the object being referred to.</p>
<p>The PDF standard has support for various data types, one of which are
text strings. These strings require escaping to be able to include
certain characters (and are surrounded by parentheses):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pdf-string</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> H{
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: \b </span> <span class="s">"\\b"</span> }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: \f </span> <span class="s">"\\f"</span> }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: \n </span> <span class="s">"\\n"</span> }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: \r </span> <span class="s">"\\r"</span> }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: \t </span> <span class="s">"\\t"</span> }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: \\ </span> <span class="s">"\\\\"</span> }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: ( </span> <span class="s">"\\("</span> }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: ) </span> <span class="s">"\\)"</span> }
</span></span><span class="line"><span class="cl"> } escape-string-by <span class="s">"("</span> <span class="s">")"</span> <span class="nb">surround </span><span class="k">;
</span></span></span></code></pre></div><p>Hashtables of key/value properties are used frequently within the
specification and are essentially space-separated key/value pairs
surrounded by <code><<</code> and <code>>></code>.</p>
<p>Our PDF file will start with an “info” section that contains a creation
timestamp, author, and software details:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pdf-info</span> <span class="nf">( -- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="s">"<<"</span> ,
</span></span><span class="line"><span class="cl"> <span class="s">"/CreationDate D:"</span> now <span class="s">"%Y%m%d%H%M%S"</span> strftime <span class="nb">append </span>,
</span></span><span class="line"><span class="cl"> <span class="s">"/Producer (Factor)"</span> ,
</span></span><span class="line"><span class="cl"> <span class="s">"/Author "</span> <span class="s">"USER"</span> os-env <span class="s">"unknown"</span> <span class="nb">or </span>pdf-string <span class="nb">append </span>,
</span></span><span class="line"><span class="cl"> <span class="s">"/Creator (created with Factor)"</span> ,
</span></span><span class="line"><span class="cl"> <span class="s">">>"</span> ,
</span></span><span class="line"><span class="cl"> ] { } make <span class="s">"\n"</span> <span class="nb">join </span><span class="k">;
</span></span></span></code></pre></div><p>We will follow it with a catalog indicating which object (by reference)
contains the list of pages.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pdf-catalog</span> <span class="nf">( -- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> <span class="s">"<<"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"/Type /Catalog"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"/Pages 4 0 R"</span>
</span></span><span class="line"><span class="cl"> <span class="s">">>"</span>
</span></span><span class="line"><span class="cl"> } <span class="s">"\n"</span> <span class="nb">join </span><span class="k">;
</span></span></span></code></pre></div><p>As is typical of “text2pdf” programs, we will use the Courier monospace
font. We can just refer to it by name since it is one of the fonts
directly supported by the PDF specification.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pdf-font</span> <span class="nf">( -- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> <span class="s">"<<"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"/Type /Font"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"/Subtype /Type1"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"/BaseFont /Courier"</span>
</span></span><span class="line"><span class="cl"> <span class="s">">>"</span>
</span></span><span class="line"><span class="cl"> } <span class="s">"\n"</span> <span class="nb">join </span><span class="k">;
</span></span></span></code></pre></div><p>Each page is composed of two objects: a resource object and a contents
object. We convert the number of pages into an index specifying the
canvas size (US Letter - “612 by 792 points” or “8.5 by 11 inches”) and
a list of pages (or “kids”) specified as references to each pages'
resource object:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pdf-pages</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="s">"<<"</span> ,
</span></span><span class="line"><span class="cl"> <span class="s">"/Type /Pages"</span> ,
</span></span><span class="line"><span class="cl"> <span class="s">"/MediaBox [ 0 0 612 792 ]"</span> ,
</span></span><span class="line"><span class="cl"> [ <span class="s">"/Count %d"</span> sprintf , ]
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="m">5 </span><span class="nb">swap </span><span class="m">2 </span>range <span class="nb">boa
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"%d 0 R "</span> sprintf ] <span class="nb">map concat
</span></span></span><span class="line"><span class="cl"> <span class="s">"/Kids [ "</span> <span class="s">"]"</span> <span class="nb">surround </span>,
</span></span><span class="line"><span class="cl"> ] <span class="nb">bi
</span></span></span><span class="line"><span class="cl"> <span class="s">">>"</span> ,
</span></span><span class="line"><span class="cl"> ] { } make <span class="s">"\n"</span> <span class="nb">join </span><span class="k">;
</span></span></span></code></pre></div><p>The resources for each page <code>n</code> includes a reference to the contents
object (<code>n + 1</code>) and the font object:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pdf-page</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">page</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="s">"<<"</span> ,
</span></span><span class="line"><span class="cl"> <span class="s">"/Type /Page"</span> ,
</span></span><span class="line"><span class="cl"> <span class="s">"/Parent 4 0 R"</span> ,
</span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="nb">+ </span><span class="s">"/Contents %d 0 R"</span> sprintf ,
</span></span><span class="line"><span class="cl"> <span class="s">"/Resources << /Font << /F1 3 0 R >> >>"</span> ,
</span></span><span class="line"><span class="cl"> <span class="s">">>"</span> ,
</span></span><span class="line"><span class="cl"> ] { } make <span class="s">"\n"</span> <span class="nb">join </span><span class="k">;
</span></span></span></code></pre></div><p>Pages are essentially objects which contain a stream of text operations.
The stream is prefixed with the length of its contents:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pdf-stream</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span><span class="m">1 </span><span class="nb">+ </span><span class="s">"<<\n/Length %d\n>>"</span> sprintf ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"\nstream\n"</span> <span class="s">"\nendstream"</span> <span class="nb">surround </span>] <span class="nb">bi append </span><span class="k">;
</span></span></span></code></pre></div><p>The text operations that we will use to draw lines of text on each page:</p>
<ul>
<li><code>BT</code> - begin text</li>
<li><code>Td</code> - location (where <code>0,0</code> is the bottom left of the page)</li>
<li><code>Tf</code> - font and size</li>
<li><code>TL</code> - line height</li>
<li><code>' </code> - insert newline and draw a line text</li>
<li><code>ET</code> - end text</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pdf-text</span> <span class="nf">( </span><span class="nv">lines</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="s">"BT"</span> ,
</span></span><span class="line"><span class="cl"> <span class="s">"54 738 Td"</span> ,
</span></span><span class="line"><span class="cl"> <span class="s">"/F1 10 Tf"</span> ,
</span></span><span class="line"><span class="cl"> <span class="s">"12 TL"</span> ,
</span></span><span class="line"><span class="cl"> [ pdf-string <span class="s">"'"</span> <span class="nb">append </span>, ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> <span class="s">"ET"</span> ,
</span></span><span class="line"><span class="cl"> ] { } make <span class="s">"\n"</span> <span class="nb">join </span>pdf-stream <span class="k">;
</span></span></span></code></pre></div><p>Using 10-point Courier font (6 points wide by 10 points tall), 12-points
of line spacing, and 3/4 inch left and right margins: each page supports
57 lines of text (where each line is 84 characters long). We use
<a href="https://docs.factorcode.org/content/vocab-splitting.html">splitting</a> and
<a href="https://docs.factorcode.org/content/vocab-grouping.html">grouping</a> words
to convert a string into pages of text:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">string>lines</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">lines</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"\t"</span> split <span class="s">" "</span> <span class="nb">join </span>string-lines
</span></span><span class="line"><span class="cl"> [ [ <span class="s">" "</span> ] <span class="nb">when-empty </span>] <span class="nb">map </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">lines>pages</span> <span class="nf">( </span><span class="nv">lines</span> <span class="nf">-- </span><span class="nv">pages</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">84 </span><groups> ] <span class="nb">map concat </span><span class="m">57 </span><groups> <span class="k">;
</span></span></span></code></pre></div><p>We can then take these “pages” and assemble PDF objects (including the
info, catalog, font, and page index objects):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pages>objects</span> <span class="nf">( </span><span class="nv">pages</span> <span class="nf">-- </span><span class="nv">objects</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> pdf-info ,
</span></span><span class="line"><span class="cl"> pdf-catalog ,
</span></span><span class="line"><span class="cl"> pdf-font ,
</span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>pdf-pages ,
</span></span><span class="line"><span class="cl"> <span class="nb">dup length </span><span class="m">5 </span><span class="nb">swap </span><span class="m">2 </span>range <span class="nb">boa zip
</span></span></span><span class="line"><span class="cl"> [ pdf-page , pdf-text , ] <span class="nb">assoc-each
</span></span></span><span class="line"><span class="cl"> ] { } make
</span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>[1..b] <span class="nb">zip </span>[ <span class="nb">first2 </span>pdf-object ] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>Given a list of objects, and a 9-byte <code>%PDF-1.4</code> version header in the
file, we can write a PDF complete with cross-reference table trailer and
<code>%%EOF</code> “end-of-file” marker:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">pdf-trailer</span> <span class="nf">( </span><span class="nv">objects</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="s">"xref"</span> ,
</span></span><span class="line"><span class="cl"> <span class="nb">dup length </span><span class="m">1 </span><span class="nb">+ </span><span class="s">"0 %d"</span> sprintf ,
</span></span><span class="line"><span class="cl"> <span class="s">"0000000000 65535 f"</span> ,
</span></span><span class="line"><span class="cl"> <span class="m">9 </span><span class="nb">over </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">over </span><span class="s">"%010X 00000 n"</span> sprintf , <span class="nb">length </span><span class="m">1 </span><span class="nb">+ +
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each drop
</span></span></span><span class="line"><span class="cl"> <span class="s">"trailer"</span> ,
</span></span><span class="line"><span class="cl"> <span class="s">"<<"</span> ,
</span></span><span class="line"><span class="cl"> <span class="nb">dup length </span><span class="m">1 </span><span class="nb">+ </span><span class="s">"/Size %d"</span> sprintf ,
</span></span><span class="line"><span class="cl"> <span class="s">"/Info 1 0 R"</span> ,
</span></span><span class="line"><span class="cl"> <span class="s">"/Root 2 0 R"</span> ,
</span></span><span class="line"><span class="cl"> <span class="s">">>"</span> ,
</span></span><span class="line"><span class="cl"> <span class="s">"startxref"</span> ,
</span></span><span class="line"><span class="cl"> [ <span class="nb">length </span><span class="m">1 </span><span class="nb">+ </span>] <span class="nb">map-sum </span><span class="m">9 </span><span class="nb">+ </span><span class="s">"%d"</span> sprintf ,
</span></span><span class="line"><span class="cl"> <span class="s">"%%EOF"</span> ,
</span></span><span class="line"><span class="cl"> ] { } make <span class="s">"\n"</span> <span class="nb">join </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">objects>pdf</span> <span class="nf">( </span><span class="nv">objects</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"\n"</span> <span class="nb">join </span><span class="s">"\n"</span> <span class="nb">append </span><span class="s">"%PDF-1.4\n"</span> ]
</span></span><span class="line"><span class="cl"> [ pdf-trailer ] <span class="nb">bi surround </span><span class="k">;
</span></span></span></code></pre></div><p>Putting it all together, we can convert a text string to PDF, and a text
file to PDF:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">text-to-pdf</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> string>lines lines>pages pages>objects objects>pdf <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">file-to-pdf</span> <span class="nf">( </span><span class="nv">path</span> <span class="nv">encoding</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ file-contents text-to-pdf ]
</span></span><span class="line"><span class="cl"> [ [ <span class="s">".pdf"</span> <span class="nb">append </span>] <span class="nb">dip </span>set-file-contents ] <span class="nb">2bi </span><span class="k">;
</span></span></span></code></pre></div><p>Running this code on itself, produces <a href="https://github.com/mrjbq7/re-factor/raw/master/text-to-pdf/text-to-pdf.pdf">this PDF
file</a>
(so you can see what it will look like).</p>
<p>As usual, the code is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/text-to-pdf/text-to-pdf.factor">GitHub</a>.</p>
Is Rotation?
https://re.factorcode.org/2010/10/is-rotation.html
Wed, 13 Oct 2010 15:13:00 -0700https://re.factorcode.org/2010/10/is-rotation.html<p>I noticed an <a href="https://stackoverflow.com/questions/2553522/interview-question-check-if-one-string-is-a-rotation-of-other-string">“interview
question”</a>
that was posted on StackOverflow awhile ago. It’s not particularly
complicated – basically asking “given two strings, how to tell if one
is the rotated version of the other?”</p>
<p>Some discussion in the question deals with various faster methods, but
the simplest answer is a <a href="https://www.python.org">Python</a> version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">isrotation</span><span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="n">s2</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">s1</span><span class="p">)</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="n">s2</span><span class="p">)</span> <span class="ow">and</span> <span class="n">s1</span> <span class="ow">in</span> <span class="mi">2</span><span class="o">*</span><span class="n">s2</span>
</span></span></code></pre></div><p>If we wanted to implement this in <a href="https://www.factorcode.org">Factor</a>,
we might want to consider using <a href="https://docs.factorcode.org/content/article-combinators.short-circuit.html">“short
circuit”</a>
combinators (which will apply a series of boolean tests and stop on the
first test that fails). We will also use the convention that a <code>word?</code>
(ending in a “?”) returns a boolean.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">rotation?</span> <span class="nf">( </span><span class="nv">s1</span> <span class="nv">s2</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { [ [ <span class="nb">length </span>] <span class="nb">bi@ = </span>] [ <span class="nb">dup append subseq? </span>] } 2&& <span class="k">;
</span></span></span></code></pre></div><p>We can test it, to make sure it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"stack"</span> <span class="s">"tacks"</span> rotation? <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">t
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"foo"</span> <span class="s">"bar"</span> rotation? <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">f
</span></span></span></code></pre></div><p>Since strings are sequences of characters and this solution uses
<a href="https://docs.factorcode.org/content/article-sequences.html">sequence
operations</a>
(<code>length</code>, <code>append</code>, and <code>subseq?</code>), it is already generalized to
operate on other types of sequences. For example, arrays:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1 2 3 </span>} { <span class="m">2 3 1 </span>} rotation? <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">t
</span></span></span></code></pre></div><p>So, the next time you get this question in an interview, maybe you can
solve it in Factor!</p>
Fortune-telling
https://re.factorcode.org/2010/09/fortune-telling.html
Mon, 27 Sep 2010 11:00:00 -0700https://re.factorcode.org/2010/09/fortune-telling.html<p>One program that some new users of Unix-like systems are introduced to
when learning the command-line is
<a href="https://en.wikipedia.org/wiki/Fortune_(Unix)">fortune</a>. The purpose of
the program is simple: to display a random message from a database of
quotations.</p>
<blockquote>
<p><em>Note: fortune is available on the Mac OS using
<a href="https://www.macports.org/">MacPorts</a> (<code>port install fortune</code>), Ubuntu
(<code>apt-get install fortune-mod</code>), and Fedora
(<code>yum install fortune-mod</code>).</em></p>
</blockquote>
<p>A demonstration of running it:</p>
<pre tabindex="0"><code>$ fortune
The human race has one really effective weapon, and that is laughter.
-- Mark Twain
</code></pre><p>I thought it would be fun to get this to work in
<a href="https://www.factorcode.org">Factor</a>. We’re going to do it two different
ways. The first way is to use the
<a href="https://docs.factorcode.org/content/article-io.launcher.html">io.launcher</a>
vocabulary for launching processes and printing the output directly to
the listener.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/opt/local/bin/fortune"</span> utf8
</span></span><span class="line"><span class="cl"> [ <span class="nb">contents print </span>] with-process-reader
</span></span><span class="line"><span class="cl">It'll be just <span class="nb">like </span>Beggars Canyon back home.
</span></span><span class="line"><span class="cl"> -- Luke Skywalker
</span></span></code></pre></div><p>Another way is to parse the fortune files directly, and then choose a
random quotation to print (i.e., implementing the fortune logic inside
of Factor). First, we will need to list the vocabularies we will be
using and define a “fortune” namespace:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">io</span> <span class="nn">io.encodings.ascii</span> <span class="nn">io.files</span> <span class="nn">kernel</span> <span class="nn">make</span> <span class="nn">memoize</span>
</span></span><span class="line"><span class="cl"><span class="nn">random</span> <span class="nn">sequences</span> <span class="nn">splitting</span> <span class="nn">strings</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">fortune</span>
</span></span></code></pre></div><p>The fortune program uses data files which are stored in a “cookie-jar
format”. From <a href="https://www.faqs.org/docs/artu/ch05s02.html">faqs.org</a>:</p>
<blockquote>
<p><em>It is appropriate for records that are just bags of unstructured
text. It simply uses newline followed by %% (or sometimes newline
followed by %) as a record separator.</em></p>
</blockquote>
<p>Parsing a fortune data file (given a string containing the contents in
cookie-jar format) is pretty straightforward. We are using
<a href="https://docs.factorcode.org/content/article-sequences-slices.html">slices</a>
which allow us to keep pointers to sections of text within the data
file.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-fortune</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ <span class="s">"%\n"</span> split1-slice <span class="nb">dup </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap </span>, ] <span class="nb">while drop </span>,
</span></span><span class="line"><span class="cl"> ] { } make <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">load-fortunes</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> ascii file-contents parse-fortune <span class="k">;
</span></span></span></code></pre></div><p>The fortune data files are installed in different locations on different
systems, so we can define a search path which includes some of the
locations that are often used. We can then use this list to load the
fortunes from the first data file that exists (and
<a href="https://docs.factorcode.org/content/article-memoize.html">memoize</a> the
results for performance):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">FORTUNES</span> {
</span></span><span class="line"><span class="cl"> <span class="s">"/usr/games/fortune/fortunes"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"/usr/share/games/fortune/fortunes"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"/usr/local/share/games/fortune/fortunes"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"/opt/local/share/games/fortune/fortunes"</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MEMO:</span> <span class="nf">default-fortunes</span> <span class="nf">( -- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> FORTUNES [ exists? ] <span class="nb">find nip </span>load-fortunes <span class="k">;
</span></span></span></code></pre></div><p>Getting a fortune is then just a matter of choosing a random quotation
and printing it out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">fortune</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> default-fortunes random <span class="nb">>string print </span><span class="k">;
</span></span></span></code></pre></div><p>If you want, you could define a
<a href="https://docs.factorcode.org/content/word-MAIN__colon__,syntax.html">MAIN:</a>
entry point and use the <a href="https://docs.factorcode.org/content/article-tools.deploy.html">deploy
tool</a> to
make this into a deployed binary.</p>
<p>The source code for this is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/fortune/fortune.factor">GitHub</a>.</p>
<p><em>Update: Doug Coleman suggested a one-liner that can produce the same
result:</em></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="s">"/opt/local/share/games/fortune/fortunes"</span> ascii file-lines
</span></span><span class="line"><span class="cl">{ <span class="s">"%"</span> } split random [ <span class="nb">print </span>] <span class="nb">each
</span></span></span></code></pre></div>Visual REPL
https://re.factorcode.org/2010/09/visual-repl.html
Wed, 08 Sep 2010 12:35:00 -0700https://re.factorcode.org/2010/09/visual-repl.html<p>Many of the recently successful programming languages have a
<a href="https://en.wikipedia.org/wiki/Read-eval-print_loop">REPL</a>
(“read-eval-print-loop”) for experimenting or iterative programming.
Mostly these seem to be for dynamic languages, although there is even a
<a href="https://neugierig.org/software/c-repl/">C REPL</a>! Almost invariably,
these are text-based (with notable exceptions for math systems like
<a href="https://www.wolfram.com/products/mathematica/index.html">Mathematica</a>).</p>
<p>I thought it would be fun to highlight some of the visual aspects of
<a href="https://www.factorcode.org/">Factor’s</a> REPL (called the “listener”). For
the examples below, it would be helpful to use these vocabularies first:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USING:</span> <span class="nn">colors</span> <span class="nn">http.client</span> <span class="nn">images.http</span> <span class="nn">io.styles</span> <span class="k">;
</span></span></span></code></pre></div><h3 id="styled-text">Styled Text</h3>
<p>Print <a href="https://docs.factorcode.org/content/article-styles.html">styled
text</a> directly
to the listener’s output. You can change font sizes, foreground and
background colors, font family, font style, and other aspects of the
text:</p>
<p>
<img src="https://re.factorcode.org/images/2010-09-08-visual-repl-font-sizes.png" alt="" width="487" height="191" />
</p>
<p>
<img src="https://re.factorcode.org/images/2010-09-08-visual-repl-font-colors.png" alt="" width="381" height="227" />
</p>
<h3 id="tab-completion">Tab Completion</h3>
<p>You can get word completion as you write code (by pressing <code>TAB</code>) in the
listener:</p>
<p>
<img src="https://re.factorcode.org/images/2010-09-08-visual-repl-tab-completion.png" alt="" width="498" height="247" />
</p>
<h3 id="word-definitions">Word Definitions</h3>
<p>You can <a href="https://docs.factorcode.org/content/article-see.html">print the
definition</a> of any
word using <code>see</code>. In addition, you can click on most parts of the output
to link to vocabulary pages, word definitions, etc.:</p>
<p>
<img src="https://re.factorcode.org/images/2010-09-08-visual-repl-see.png" alt="" width="268" height="62" />
</p>
<h3 id="helpful-search">Helpful Search</h3>
<p>Use
<a href="https://docs.factorcode.org/content/word-apropos,help.apropos.html">apropos</a>
to search for words, vocabularies, and help articles. It uses some
<a href="https://docs.factorcode.org/content/article-tools.completion.html">fuzzy
completion</a>
algorithms to be more helpful for typos or approximations.</p>
<p>
<img src="https://re.factorcode.org/images/2010-09-08-visual-repl-apropos.png" alt="" width="410" height="574" />
</p>
<h3 id="viewing-images">Viewing Images</h3>
<p>Use the handy <code>images.viewer</code> vocabulary (written by Doug Coleman) to
load and <a href="https://docs.factorcode.org/content/article-images.viewer.html">display
images</a>
directly in the listener:</p>
<p>
<img src="https://re.factorcode.org/images/2010-09-08-visual-repl-image-viewer.png" alt="" width="498" height="213" />
</p>
Approximating Rationals
https://re.factorcode.org/2010/09/approximating-rationals.html
Mon, 06 Sep 2010 22:25:00 -0700https://re.factorcode.org/2010/09/approximating-rationals.html<p>A few months ago, there was an
<a href="https://cdsmith.wordpress.com/2009/05/23/approximations-of-pi-and-other-numbers-as-fractions/">article</a>
about approximating numbers with fractions using the <code>approxRational</code>
function in <a href="https://haskell.org/">Haskell</a>. The documentation for the
function states:</p>
<blockquote>
<p><code>approxRational</code>, applied to two real fractional numbers <code>x</code> and
<code>epsilon</code>, returns the simplest rational number within <code>epsilon</code> of
<code>x</code>. A rational number <code>y</code> is said to be simpler than another <code>y'</code> if</p>
<ol>
<li><code>abs (numerator y) <= abs (numerator y')</code>, and</li>
<li><code>denominator y <= denominator y'</code>.</li>
</ol>
<p>Any real interval contains a unique simplest rational; in particular,
note that <code>0/1</code> is the simplest rational of all.</p>
</blockquote>
<p>The <a href="https://haskell.org/ghc/docs/6.12.1/html/libraries/base-4.2.0.0/src/Data-Ratio.html#approxRational">source
code</a>
for <code>approxRational</code> is:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-haskell" data-lang="haskell"><span class="line"><span class="cl"><span class="nf">approxRational</span> <span class="ow">::</span> <span class="p">(</span><span class="kt">RealFrac</span> <span class="n">a</span><span class="p">)</span> <span class="ow">=></span> <span class="n">a</span> <span class="ow">-></span> <span class="n">a</span> <span class="ow">-></span> <span class="kt">Rational</span>
</span></span><span class="line"><span class="cl"><span class="nf">approxRational</span> <span class="n">rat</span> <span class="n">eps</span> <span class="ow">=</span> <span class="n">simplest</span> <span class="p">(</span><span class="n">rat</span><span class="o">-</span><span class="n">eps</span><span class="p">)</span> <span class="p">(</span><span class="n">rat</span><span class="o">+</span><span class="n">eps</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="kr">where</span> <span class="n">simplest</span> <span class="n">x</span> <span class="n">y</span> <span class="o">|</span> <span class="n">y</span> <span class="o"><</span> <span class="n">x</span> <span class="ow">=</span> <span class="n">simplest</span> <span class="n">y</span> <span class="n">x</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">x</span> <span class="o">==</span> <span class="n">y</span> <span class="ow">=</span> <span class="n">xr</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">x</span> <span class="o">></span> <span class="mi">0</span> <span class="ow">=</span> <span class="n">simplest'</span> <span class="n">n</span> <span class="n">d</span> <span class="n">n'</span> <span class="n">d'</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">y</span> <span class="o"><</span> <span class="mi">0</span> <span class="ow">=</span> <span class="o">-</span> <span class="n">simplest'</span> <span class="p">(</span><span class="o">-</span><span class="n">n'</span><span class="p">)</span> <span class="n">d'</span> <span class="p">(</span><span class="o">-</span><span class="n">n</span><span class="p">)</span> <span class="n">d</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">otherwise</span> <span class="ow">=</span> <span class="mi">0</span> <span class="kt">:%</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="kr">where</span> <span class="n">xr</span> <span class="ow">=</span> <span class="n">toRational</span> <span class="n">x</span>
</span></span><span class="line"><span class="cl"> <span class="n">n</span> <span class="ow">=</span> <span class="n">numerator</span> <span class="n">xr</span>
</span></span><span class="line"><span class="cl"> <span class="n">d</span> <span class="ow">=</span> <span class="n">denominator</span> <span class="n">xr</span>
</span></span><span class="line"><span class="cl"> <span class="n">nd'</span> <span class="ow">=</span> <span class="n">toRational</span> <span class="n">y</span>
</span></span><span class="line"><span class="cl"> <span class="n">n'</span> <span class="ow">=</span> <span class="n">numerator</span> <span class="n">nd'</span>
</span></span><span class="line"><span class="cl"> <span class="n">d'</span> <span class="ow">=</span> <span class="n">denominator</span> <span class="n">nd'</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">simplest'</span> <span class="n">n</span> <span class="n">d</span> <span class="n">n'</span> <span class="n">d'</span> <span class="c1">-- assumes 0 < n%d < n'%d'</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">r</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">=</span> <span class="n">q</span> <span class="kt">:%</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">q</span> <span class="o">/=</span> <span class="n">q'</span> <span class="ow">=</span> <span class="p">(</span><span class="n">q</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span> <span class="kt">:%</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">otherwise</span> <span class="ow">=</span> <span class="p">(</span><span class="n">q</span><span class="o">*</span><span class="n">n''</span><span class="o">+</span><span class="n">d''</span><span class="p">)</span> <span class="kt">:%</span> <span class="n">n''</span>
</span></span><span class="line"><span class="cl"> <span class="kr">where</span> <span class="p">(</span><span class="n">q</span><span class="p">,</span><span class="n">r</span><span class="p">)</span> <span class="ow">=</span> <span class="n">quotRem</span> <span class="n">n</span> <span class="n">d</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">q'</span><span class="p">,</span><span class="n">r'</span><span class="p">)</span> <span class="ow">=</span> <span class="n">quotRem</span> <span class="n">n'</span> <span class="n">d'</span>
</span></span><span class="line"><span class="cl"> <span class="n">nd''</span> <span class="ow">=</span> <span class="n">simplest'</span> <span class="n">d'</span> <span class="n">r'</span> <span class="n">d</span> <span class="n">r</span>
</span></span><span class="line"><span class="cl"> <span class="n">n''</span> <span class="ow">=</span> <span class="n">numerator</span> <span class="n">nd''</span>
</span></span><span class="line"><span class="cl"> <span class="n">d''</span> <span class="ow">=</span> <span class="n">denominator</span> <span class="n">nd''</span>
</span></span></code></pre></div><p>I couldn’t find an equivalent algorithm in
<a href="https://www.factorcode.org">Factor</a>, so I decided to translate it (using
<a href="https://docs.factorcode.org/content/article-locals.html">lexical
variables</a> to
make the comparison between the languages as direct as possible).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">combinators</span> <span class="nn">kernel</span> <span class="nn">locals</span> <span class="nn">math</span> <span class="nn">math.functions</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">(simplest)</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">d</span> <span class="nv">n'</span> <span class="nv">d'</span> <span class="nf">-- </span><span class="nv">val</span> <span class="nf">) </span><span class="c">! assumes 0 < n/d < n'/d'</span>
</span></span><span class="line"><span class="cl"> n d <span class="nb">/mod </span>:> <span class="nf">( </span><span class="nv">q</span> <span class="nv">r</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> n' d' <span class="nb">/mod </span>:> <span class="nf">( </span><span class="nv">q'</span> <span class="nv">r'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ r <span class="nb">zero? </span>] [ q ] }
</span></span><span class="line"><span class="cl"> { [ q q' <span class="nb">= not </span>] [ q <span class="m">1 </span><span class="nb">+ </span>] }
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> d' r' d r (simplest) >fraction :> <span class="nf">( </span><span class="nv">n''</span> <span class="nv">d''</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> q n'' <span class="nb">* </span>d'' <span class="nb">+ </span>n'' <span class="nb">/
</span></span></span><span class="line"><span class="cl"> ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">simplest</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nf">-- </span><span class="nv">val</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { [ x y <span class="nb">> </span>] [ y x simplest ] }
</span></span><span class="line"><span class="cl"> { [ x y <span class="nb">= </span>] [ x ] }
</span></span><span class="line"><span class="cl"> { [ x <span class="m">0 </span><span class="nb">> </span>] [ x y [ >fraction ] <span class="nb">bi@ </span>(simplest) ] }
</span></span><span class="line"><span class="cl"> { [ y <span class="m">0 </span><span class="nb">< </span>] [ y x [ <span class="nb">neg </span>>fraction ] <span class="nb">bi@ </span>(simplest) <span class="nb">neg </span>] }
</span></span><span class="line"><span class="cl"> [ <span class="m">0 </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">approximate</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">epsilon</span> <span class="nf">-- </span><span class="nv">y</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">- </span>] [ <span class="nb">+ </span>] <span class="nb">2bi </span>simplest <span class="k">;
</span></span></span></code></pre></div><p>We can experiment with the function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> 33/100 1/10 approximate <span class="m">.
</span></span></span><span class="line"><span class="cl">1/3
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> 1/3 1/10 approximate <span class="m">.
</span></span></span><span class="line"><span class="cl">1/3
</span></span></code></pre></div><p>We can use the code I
<a href="https://re.factorcode.org/2010/08/floating-point-fractions.html">posted</a>
a few days ago to convert floating-point numbers to fractions. That will
be useful for running the experiment from the original <a href="https://cdsmith.wordpress.com/2009/05/23/approximations-of-pi-and-other-numbers-as-fractions/">blog
post</a>
(to approximate <code>pi</code> with varying degrees of accuracy):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USING:</span> <span class="nn">math.constants</span> <span class="nn">math.statistics</span> <span class="nn">math.vectors</span> <span class="k">;
</span></span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10000 </span>[1..b] [ <span class="m">0.99 </span><span class="nb">swap </span>^ float>ratio ] <span class="nb">map
</span></span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> pi float>ratio '[ _ <span class="nb">swap </span>approximate ] <span class="nb">map
</span></span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> sorted-histogram <span class="nb">reverse </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> { <span class="m">3+39854788871587/281474976710656 3447 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">3+16/113 572 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">3+464086704149/3277618523158 433 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">3+1/7 297 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">3+244252/1725033 288 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">3+1470786781/10387451211 248 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">3 194 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">3+11080585/78256779 186 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">3+56667685227/400216280932 176 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">3+449714866/3176117225 172 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Looking at the top 10 most common approximations, you can see the first
(and most frequent) result is the exact fractional value of the constant
<code>pi</code>, the fourth result is the common “grade school” estimate <code>22/7</code>,
and the second result is the “best” simple estimate <code>355/113</code>.</p>
<p><em>Note: the output is slightly different than the Haskell program. Factor
considers several of these rational numbers to be equal when converted to
floating-point:</em></p>
<ul>
<li><code>3+39854788871587/281474976710656</code></li>
<li><code>3+464086704149/3277618523158</code></li>
<li><code>3+1470786781/10387451211</code></li>
<li><code>3+11080585/78256779</code></li>
<li><code>3+56667685227/400216280932</code></li>
</ul>
<p>The code (and some tests) for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/math/approx/approx.factor">GitHub</a>.</p>
Internet Checksum
https://re.factorcode.org/2010/09/internet-checksum.html
Sat, 04 Sep 2010 12:08:00 -0700https://re.factorcode.org/2010/09/internet-checksum.html<p>I needed to calculate the “internet checksum” for a small project I’ve
been working on. While <a href="https://www.factorcode.org">Factor</a> includes
several <a href="https://en.wikipedia.org/wiki/Checksum">checksum algorithms</a>, it
didn’t have support (until recently) for the “internet checksum”.</p>
<p>The “internet checksum” is a 16-bit checksum value specified in <a href="https://www.faqs.org/rfcs/rfc1071.html">RFC
1071</a> and used in many places
within standard network protocols (such as the <a href="https://en.wikipedia.org/wiki/Header_checksum">IP
header</a>). The RFC includes
this description of the algorithm:</p>
<blockquote>
<p><em>Adjacent octets to be checksummed are paired to form 16-bit integers,
and the 1’s complement sum of these 16-bit integers is formed.</em></p>
</blockquote>
<p>Some C code is provided in the RFC as an example of the algorithm for
performing this computation (including comments):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/* Compute Internet Checksum for "count" bytes
</span></span></span><span class="line"><span class="cl"><span class="cm"> * beginning at location "addr".
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="k">register</span> <span class="kt">long</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">while</span><span class="p">(</span> <span class="n">count</span> <span class="o">></span> <span class="mi">1</span> <span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="cm">/* This is the inner loop */</span>
</span></span><span class="line"><span class="cl"> <span class="n">sum</span> <span class="o">+=</span> <span class="o">*</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">short</span><span class="p">)</span> <span class="n">addr</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">count</span> <span class="o">-=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/* Add left-over byte, if any */</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span> <span class="n">count</span> <span class="o">></span> <span class="mi">0</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">sum</span> <span class="o">+=</span> <span class="o">*</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="p">)</span> <span class="n">addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/* Fold 32-bit sum to 16 bits */</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="p">(</span><span class="n">sum</span><span class="o">>></span><span class="mi">16</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">sum</span> <span class="o">=</span> <span class="p">(</span><span class="n">sum</span> <span class="o">&</span> <span class="mh">0xffff</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">sum</span> <span class="o">>></span> <span class="mi">16</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">checksum</span> <span class="o">=</span> <span class="o">~</span><span class="n">sum</span><span class="p">;</span>
</span></span></code></pre></div><p>In Factor, we can use some high-level concepts like
<a href="https://docs.factorcode.org/content/article-grouping.html">grouping</a> to
walk through the bytes two at a time. In fact, the entire implementation
is only a few lines and pretty readable:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">internet-checksum</span> <span class="nf">( </span><span class="nv">bytes</span> <span class="nf">-- </span><span class="nv">value</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">2 </span><sliced-groups> [ le> ] <span class="nb">map-sum
</span></span></span><span class="line"><span class="cl"> [ <span class="m">-16 </span><span class="nb">shift </span>] [ <span class="m">0xffff </span><span class="nb">bitand </span>] <span class="nb">bi +
</span></span></span><span class="line"><span class="cl"> [ <span class="m">-16 </span><span class="nb">shift </span>] <span class="nb">keep + bitnot </span><span class="m">2 </span>>le <span class="k">;
</span></span></span></code></pre></div><p>This is available as a <code>checksum</code> instance in
<a href="https://gitweb.factorcode.org/gitweb.cgi?p=factor/.git;a=blob_plain;f=basis/checksums/internet/internet.factor;hb=HEAD">checksums.internet</a>.</p>
What can you get from 1, 2, 3, 4, +, -, *, /, and ^?
https://re.factorcode.org/2010/09/what-can-you-get-from-1-2-3-4.html
Thu, 02 Sep 2010 10:57:00 -0700https://re.factorcode.org/2010/09/what-can-you-get-from-1-2-3-4.html<p>Recently, a Haskell program was
<a href="https://kpreid.livejournal.com/30751.html">posted</a> that computed all
possible combinations of the numbers “1, 2, 3, and 4” and the operators
“+, -, *, /, and ^” (allowing the operators to be repeated, but not the
numbers). It’s a fun little problem, and I thought it might be a good
example for iterative development in <a href="https://www.factorcode.org">Factor</a>
using the listener.</p>
<p>First, some vocabularies that we’ll be using.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USING:</span> <span class="nn">arrays</span> <span class="nn">continuations</span> <span class="nn">kernel</span> <span class="nn">math</span>
</span></span><span class="line"><span class="cl"> <span class="nn">math.combinatorics</span> <span class="nn">math.statistics</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span></code></pre></div><p>We can calculate
<a href="https://docs.factorcode.org/content/word-all-permutations,math.combinatorics.html">all-permutations</a>
of the numbers (for a total of 4!, or 24):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1 2 3 4 </span>} all-permutations
</span></span></code></pre></div><p>And, similarly, we can find all possible
<a href="https://docs.factorcode.org/content/word-selections,math.combinatorics.html">selections</a>
of three operations (for a total of 5<sup>3</sup>, or 125):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="nb">+ - * / </span>^ } <span class="m">3 </span>selections
</span></span></code></pre></div><p>Now, we can compute the
<a href="https://docs.factorcode.org/content/word-cartesian-product,sequences.html">cartesian-product</a>
to produce all possible pairings of the two sequences (for a total of 24
× 125, or 3000):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">cartesian-product concat
</span></span></span></code></pre></div><p>You can inspect the list by clicking on it in the listener, printing it
out (e.g., “<code>dup .</code>”, or showing what the first result looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup first </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ { <span class="m">1 2 3 4 </span>} { <span class="nb">+ + + </span>} }
</span></span></code></pre></div><p>Use
<a href="https://docs.factorcode.org/content/word-concat-as,sequences.html">concat-as</a>
to make all the entries quotations (so they can be called).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ [ ] <span class="nb">concat-as </span>] <span class="nb">map
</span></span></span></code></pre></div><p>We can then try calling the first element to make sure it produces the
right result:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup first dup </span><span class="m">. </span><span class="nb">call </span><span class="m">.
</span></span></span><span class="line"><span class="cl">[ <span class="m">1 2 3 4 </span><span class="nb">+ + + </span>]
</span></span><span class="line"><span class="cl"><span class="m">10
</span></span></span></code></pre></div><p>Let’s call each quotation, creating an <a href="https://docs.factorcode.org/content/article-alists.html">association
list</a> where the
key is the quotation and the value is the result:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="nb">dup call 2array </span>] <span class="nb">map
</span></span></span><span class="line"><span class="cl">Division by zero
</span></span><span class="line"><span class="cl">x <span class="m">2
</span></span></span></code></pre></div><p>Whoops, some of the formulas produce
<a href="https://docs.factorcode.org/content/word-division-by-zero,math.ratios.html">division-by-zero</a>
errors. We can use
<a href="https://docs.factorcode.org/content/vocab-continuations.html">continuations</a>
to
<a href="https://docs.factorcode.org/content/word-recover,continuations.html">recover</a>
(storing a <code>f</code> result when there is an error) and continue:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ [ <span class="nb">dup call </span>] [ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">recover 2array </span>] <span class="nb">map
</span></span></span></code></pre></div><p>Each element of the resulting sequence is a pairing of a quotation and a
result:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup first </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ [ <span class="m">1 2 3 4 </span><span class="nb">+ + + </span>] <span class="m">10 </span>}
</span></span></code></pre></div><p>We can see how many unique results (including <code>f</code>) are found:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup values </span>unique <span class="nb">assoc-size </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">430
</span></span></span></code></pre></div><p>You could calculate the 10 most common results using
<a href="https://docs.factorcode.org/content/word-sorted-histogram,math.statistics.html">sorted-histogram</a>.
It turns out “1” is the most common result:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup values </span>sorted-histogram <span class="nb">reverse </span><span class="m">10 </span><span class="nb">head </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> { <span class="m">1 200 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">4 116 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">2 116 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">3 96 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">6 82 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">9 65 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">5 60 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">-2 57 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">24 56 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">1+1/2 53 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Some other things you might try:</p>
<ul>
<li>Count how many “divide-by-zero” errors are produced, perhaps using
<a href="https://docs.factorcode.org/content/word-assoc-filter,assocs.html">assoc-filter</a>
to examine the “bad” formulas.</li>
<li>Create an
<a href="https://docs.factorcode.org/content/article-assocs.html">association</a>
between each unique value and the list of all quotations that
produced it.</li>
<li>Print each quotation and result out,
<a href="https://docs.factorcode.org/content/article-sequences-sorting.html">sorted</a>
by numerical result.</li>
<li>Define a word that, given a sequence of numbers and a sequence of
operations, produces all the result pairings (using
[call(](<a href="https://docs.factorcode.org/content/word-call(,syntax.html)">https://docs.factorcode.org/content/word-call(,syntax.html)</a>
to make it compile properly).</li>
<li>Find the <a href="https://docs.factorcode.org/content/word-supremum,sequences.html">most
positive</a>
and <a href="https://docs.factorcode.org/content/word-infimum,sequences.html">most
negative</a>
result, and output the quotations that produced them.</li>
</ul>
<p>Update: it was pointed out by <a href="https://switchb.org/kpreid/">Kevin Reid</a> (the
author of the Haskell version) that I’m missing <a href="https://en.wikipedia.org/wiki/Operator_associativity">left-associative
operators</a>. I think
the only modification that is required is to add “swapped” versions of the
operators to the possible choices:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">quotations</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="nb">+ - * / </span>^ } [ 1quotation ] <span class="nb">map
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup </span>[ [ <span class="nb">swap </span>] <span class="nb">prepend </span>] <span class="nb">map append dup </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> [ <span class="nb">+ </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">- </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">* </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">/ </span>]
</span></span><span class="line"><span class="cl"> [ ^ ]
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap + </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap - </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap * </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap / </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap </span>^ ]
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">3 </span>selections [ <span class="nb">concat </span>] <span class="nb">map
</span></span></span></code></pre></div><p>This produces 10<sup>3</sup>, or 1,000, possible operations. When
combined with the original 24 permutations of <code>{ 1 2 3 4 }</code>, that makes
24,000 possible formulas. Running through the logic above makes 677
unique results. Still not sure why this is close, but doesn’t quite
match, the original results in Haskell.</p>
<p>Update 2: it was pointed out by <a href="https://joop.kiefte.eu/">joop</a> that
addition and multiplication don’t need “swapped” versions.</p>
<p>Update 3: it was pointed out by
<a href="https://www.blogger.com/profile/04879037634107186407">Scaevolus</a> that I am
missing <code>(4 ^ 4) ^ (4 ^ 4)</code>. In Factor, that could be represented by <code>[ 4 4 ^ 4 4 ^ ^ ]</code> or <code>[ 4 4 4 4 ^ [ ^ ] dip ^ ]</code>. One fix would be to include the
“dipped” operators in the first two positions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> {
</span></span><span class="line"><span class="cl"> [ <span class="nb">+ </span>] [ <span class="nb">- </span>] [ <span class="nb">* </span>] [ <span class="nb">/ </span>] [ ^ ]
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap - </span>] [ <span class="nb">swap / </span>] [ <span class="nb">swap </span>^ ]
</span></span><span class="line"><span class="cl"> [ [ <span class="nb">- </span>] <span class="nb">dip </span>] [ [ <span class="nb">swap - </span>] <span class="nb">dip </span>]
</span></span><span class="line"><span class="cl"> [ [ <span class="nb">/ </span>] <span class="nb">dip </span>] [ [ <span class="nb">swap / </span>] <span class="nb">dip </span>]
</span></span><span class="line"><span class="cl"> [ [ ^ ] <span class="nb">dip </span>] [ [ <span class="nb">swap </span>^ ] <span class="nb">dip </span>]
</span></span><span class="line"><span class="cl"> } <span class="m">2 </span>selections
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> {
</span></span><span class="line"><span class="cl"> [ <span class="nb">+ </span>] [ <span class="nb">- </span>] [ <span class="nb">* </span>] [ <span class="nb">/ </span>] [ ^ ]
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap - </span>] [ <span class="nb">swap / </span>] [ <span class="nb">swap </span>^ ]
</span></span><span class="line"><span class="cl"> } <span class="m">1 </span>selections
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">cartesian-product concat </span>[ <span class="nb">concat </span>[ ] <span class="nb">concat-as </span>] <span class="nb">map
</span></span></span></code></pre></div><p>This gives us 14 × 14 × 8, or 1,568 possible formulas. But, it feels a
bit like a kludge. What would be the proper way to include these?</p>
Floating-point Fractions
https://re.factorcode.org/2010/08/floating-point-fractions.html
Tue, 31 Aug 2010 19:24:00 -0700https://re.factorcode.org/2010/08/floating-point-fractions.html<p>Recently, I wanted a way to convert floating-point numbers into
fractions using <a href="https://www.factorcode.org">Factor</a>. To do this (with
any hope of being correct), I spent some time understanding how
floating-point numbers are represented.</p>
<p>Two useful resources about floating-point numbers are an article entitled
<a href="https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html">“What Every Computer Scientist Should Know About Floating-Point
Arithmetic”</a>
and a website called <a href="https://floating-point-gui.de/">The Floating-Point
Guide</a>.</p>
<p>Basic floating-point numbers are specified using a sign bit, an
exponent, and a mantissa. Aside from some special numbers (e.g., +Inf,
-Inf, NaN) and <a href="https://en.wikipedia.org/wiki/Denormal_number">denormal</a>
numbers, the value of a floating-point can be calculated using the
formula:</p>
<blockquote>
<p>(-1)<sup>sign</sup> × 2<sup>exponent\ -\ exponent\ bias</sup> ×
1.mantissa</p>
</blockquote>
<p>We will be working with <a href="https://en.wikipedia.org/wiki/Double_precision_floating-point_format">double
precision</a>
floating point values (e.g., 64-bit values):</p>
<p>
<img src="https://re.factorcode.org/images/2010-08-31-floating-point-fractions-500px-IEEE_754_Double_Floating_Point_Format.svg.png" alt="" width="618" height="125" />
</p>
<p>To extract the sign, exponent, and mantissa bits is fairly easy:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">math.bitwise</span> <span class="nn">math.functions</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">sign</span> <span class="nf">( </span><span class="nv">bits</span> <span class="nf">-- </span><span class="nv">sign</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">-63 </span><span class="nb">shift </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">exponent</span> <span class="nf">( </span><span class="nv">bits</span> <span class="nf">-- </span><span class="nv">exponent</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">-52 </span><span class="nb">shift </span><span class="m">11 </span>on-bits mask <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">mantissa</span> <span class="nf">( </span><span class="nv">bits</span> <span class="nf">-- </span><span class="nv">mantissa</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">52 </span>on-bits mask <span class="k">;
</span></span></span></code></pre></div><p>We are not going to support special values, so we
<a href="https://docs.factorcode.org/content/word-throw,kernel.html">throw</a> an
error if we encounter one:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">check-special</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup fp-special? </span>[ <span class="s">"cannot be special"</span> <span class="nb">throw </span>] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><p>Converting to a ratio (e.g., numerator and denominator) is just a matter
of computing the formula (with some special handling for denormal
numbers where the exponent is zero):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">float>ratio</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">a/b</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> check-special <span class="nb">double>bits
</span></span></span><span class="line"><span class="cl"> [ sign <span class="nb">zero? </span><span class="m">1 -1 </span><span class="nb">? </span>] [ mantissa <span class="m">52 </span><span class="nb">2^ / </span>] [ exponent ] <span class="nb">tri
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup zero? </span>[ <span class="m">1 </span><span class="nb">+ </span>] [ [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">dip </span>] <span class="nb">if </span><span class="m">1023 </span><span class="nb">- </span><span class="m">2 </span><span class="nb">swap </span>^ <span class="nb">* * </span><span class="k">;
</span></span></span></code></pre></div><p>You can see this in action:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">0.5 </span>float>ratio <span class="m">.
</span></span></span><span class="line"><span class="cl">1/2
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">12.5 </span>float>ratio <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">12+1/2
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">0.333333333333333 </span>float>ratio <span class="m">.
</span></span></span><span class="line"><span class="cl">6004799503160655/18014398509481984
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">math.constants</span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> pi float>ratio <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">3+39854788871587/281474976710656
</span></span></span></code></pre></div>Hello, web!
https://re.factorcode.org/2010/08/hello-web.html
Sat, 28 Aug 2010 21:13:00 -0700https://re.factorcode.org/2010/08/hello-web.html<p>One thing that surprises many people when they come to
<a href="https://www.factorcode.org">Factor</a>, is that a lot of the Factor
infrastructure (<a href="https://www.factorcode.org">main site</a>,
<a href="https://planet.factorcode.org">planet</a>,
<a href="https://paste.factorcode.org/">pastebin</a>,
<a href="https://docs.factorcode.org">documentation</a>, and
<a href="https://www.concatenative.org">wiki</a>) is written in Factor, and runs on
a Factor web server.</p>
<p>The Factor web server is very capable, supporting static files, CGI
scripts, SSL authentication, session management, and dynamic web pages.
Some of the vocabularies that are involved:</p>
<ul>
<li><a href="https://docs.factorcode.org/content/vocab-http.server.html">http.server</a>,
the web server</li>
<li><a href="https://docs.factorcode.org/content/vocab-furnace.html">furnace</a>,
the web framework</li>
<li><a href="https://docs.factorcode.org/content/vocab-db.html">db</a>, the database
support</li>
<li><a href="https://docs.factorcode.org/content/vocab-html.templates.html">html.templates</a>
(both
<a href="https://docs.factorcode.org/content/vocab-html.templates.chloe.html">chloe</a>
and
<a href="https://docs.factorcode.org/content/vocab-html.templates.fhtml.html">fhtml</a>)</li>
<li><a href="https://docs.factorcode.org/content/vocab-html.forms.html">html.forms</a>
and form
<a href="https://docs.factorcode.org/content/vocab-validators.html">validators</a></li>
</ul>
<h3 id="hello-world">Hello, world!</h3>
<p>This is a simple application that returns a plain text page that says
“Hello, world!”. Our web application is structured into a dispatcher
(our “main responder”), an action, and words to create and run the web
server.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">furnace.actions</span> <span class="nn">http.server</span>
</span></span><span class="line"><span class="cl"><span class="nn">http.server.dispatchers</span> <span class="nn">http.server.responses</span> <span class="nn">io.servers</span> <span class="nn">kernel</span>
</span></span><span class="line"><span class="cl"><span class="nn">namespaces</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">webapps.hello</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">hello</span> < <span class="nc">dispatcher</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><hello-action></span> <span class="nf">( -- </span><span class="nv">action</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <page-action>
</span></span><span class="line"><span class="cl"> [ <span class="s">"Hello, world!"</span> <span class="s">"text/plain"</span> <content> ] >>display <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><hello></span> <span class="nf">( -- </span><span class="nv">dispatcher</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> hello new-dispatcher
</span></span><span class="line"><span class="cl"> <hello-action> <span class="s">""</span> add-responder <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">run-hello</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <hello>
</span></span><span class="line"><span class="cl"> main-responder <span class="nb">set-global
</span></span></span><span class="line"><span class="cl"> <span class="m">8080 </span>httpd wait-for-server <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">run-hello</span>
</span></span></code></pre></div><p>Run the code by calling <code>run-hello</code>, then navigate to
<a href="https://localhost:8080">https://localhost:8080</a> and you will see the response.</p>
<h3 id="templates">Templates</h3>
<p>To begin experimenting with templates, lets change the logic to include
a form where a name can be provided. We will create a <a href="https://docs.factorcode.org/content/article-html.templates.chloe.tags.html">Chloe
template</a>
file. Let’s create a <code>hello.xml</code> file in the same location as the
<code>webapps.hello</code> vocabulary:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp"><?xml version='1.0' ?></span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt"><t:chloe</span> <span class="na">xmlns:t=</span><span class="s">"https://factorcode.org/chloe/1.0"</span><span class="nt">></span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nt"><t:form</span> <span class="na">t:action=</span><span class="s">"$hello"</span><span class="nt">></span>
</span></span><span class="line"><span class="cl"> <span class="nt"><label></span>What is your name?<span class="nt"></label></span>
</span></span><span class="line"><span class="cl"> <span class="nt"><t:field</span> <span class="na">t:name=</span><span class="s">"name"</span> <span class="nt">/></span>
</span></span><span class="line"><span class="cl"> <span class="nt"><input</span> <span class="na">type=</span><span class="s">"submit"</span> <span class="nt">/></span>
</span></span><span class="line"><span class="cl"> <span class="nt"></t:form></span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt"></t:chloe></span>
</span></span></code></pre></div><p>Now, modify the <code>hello-action</code> to load the template. The default form
submission is via <code>POST</code> and can be supported using the <code>submit</code> slot of
the <code>action</code>. We respond to a form submission by returning a plain text
response saying “Hello, $name!”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USE:</span> <span class="nn">formatting</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><hello-action></span> <span class="nf">( -- </span><span class="nv">action</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <page-action>
</span></span><span class="line"><span class="cl"> { hello <span class="s">"hello"</span> } >>template
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="s">"name"</span> param <span class="s">"Hello, %s!"</span> sprintf
</span></span><span class="line"><span class="cl"> <span class="s">"text/plain"</span> <content>
</span></span><span class="line"><span class="cl"> ] >>submit <span class="k">;
</span></span></span></code></pre></div><p>When you navigate to <a href="https://localhost:8080">https://localhost:8080</a>, you will see a simple form
prompting you to type in a name. After submitting the form, you will see
a customized response depending on the name provided.</p>
<h3 id="form-validation">Form Validation</h3>
<p>It is frequently useful to validate parameters that are submitted via
forms (e.g., for numbers, e-mail addresses, ranges, required or
optional, etc.). To support this, we need to add validation logic for
every parameter desired (using words from the
<a href="https://docs.factorcode.org/content/vocab-validators.html">validators</a>
vocabulary). In this case, the name should be a required parameter:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USE:</span> <span class="nn">validators</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><hello-action></span> <span class="nf">( -- </span><span class="nv">action</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <page-action>
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> { { <span class="s">"name"</span> [ v-required ] } } validate-params
</span></span><span class="line"><span class="cl"> ] >>validate
</span></span><span class="line"><span class="cl"> { hello <span class="s">"hello"</span> } >>template
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="s">"name"</span> value <span class="s">"Hello, %s!"</span> sprintf
</span></span><span class="line"><span class="cl"> <span class="s">"text/plain"</span> <content>
</span></span><span class="line"><span class="cl"> ] >>submit <span class="k">;
</span></span></span></code></pre></div><p>Next, wrap the dispatcher in an
<a href="https://docs.factorcode.org/content/word-__lt__alloy__gt__,furnace.alloy.html"><alloy></a>,
which provides support for session-persistence, form validation, and
database persistence.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USE:</span> <span class="nn">furnace.alloy</span>
</span></span><span class="line"><span class="cl"><span class="kn">USE:</span> <span class="nn">db.sqlite</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><hello></span> <span class="nf">( -- </span><span class="nv">dispatcher</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> hello new-dispatcher
</span></span><span class="line"><span class="cl"> <hello-action> <span class="s">""</span> add-responder
</span></span><span class="line"><span class="cl"> <span class="s">"resource:hello.db"</span> <sqlite-db> <alloy> <span class="k">;
</span></span></span></code></pre></div><p>If you navigate to the website now, and don’t provide a <code>name</code>, you’ll
be redirected back to the form with the validation error specified.</p>
<h3 id="other-tips">Other tips</h3>
<p>There is a
<a href="https://docs.factorcode.org/content/word-development__que__,http.server.html">development?</a>
symbol that can be set to <code>t</code> to make sure the web server is running the
latest code from your application and that errors generate nice stack
traces.</p>
<p>Malu has a <a href="https://github.com/malu/factor-blog-gitutorial">nice
tutorial</a> on GitHub about
building a blog application in Factor.</p>
<p>All of the Factor websites (as well as some nice examples like a
“counter”, “todo list”, “tiny url”, and “ip address”) are in
<code>resource:extra/webapps</code>.</p>
Calculator with GUI
https://re.factorcode.org/2010/08/calculator-with-gui.html
Mon, 23 Aug 2010 22:08:00 -0700https://re.factorcode.org/2010/08/calculator-with-gui.html<p><em>Update: Kyle Cordes has made some nice
<a href="https://kylecordes.com/2010/refactoring-factor">refactoring</a> to avoid
the “code smell” of passing global variables around while building the
gadgets.</em></p>
<p>I started playing around with the <a href="https://www.factorcode.org">Factor</a>
GUI framework recently. The
<a href="https://docs.factorcode.org/content/article-ui.html">documentation</a> is
very detailed, but sometimes it is nice to have simple examples to learn
from.</p>
<p>I thought it would be fun to build a simple calculator application. A
teaser of what it will look like when we are done:</p>
<p>
<img src="https://re.factorcode.org/images/2010-08-23-calculator-with-gui-calc-ui.png" alt="" width="155" height="217" />
</p>
<p>First, some imports and a namespace.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">colors.constants</span> <span class="nn">combinators.smart</span> <span class="nn">kernel</span> <span class="nn">fry</span>
</span></span><span class="line"><span class="cl"><span class="nn">math</span> <span class="nn">math.parser</span> <span class="nn">models</span> <span class="nn">namespaces</span> <span class="nn">sequences</span> <span class="nn">ui</span> <span class="nn">ui.gadgets</span>
</span></span><span class="line"><span class="cl"><span class="nn">ui.gadgets.borders</span> <span class="nn">ui.gadgets.buttons</span> <span class="nn">ui.gadgets.labels</span>
</span></span><span class="line"><span class="cl"><span class="nn">ui.gadgets.tracks</span> <span class="nn">ui.pens.solid</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">FROM:</span> <span class="nn">models</span> => <span class="nf">change-model</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">calc-ui</span>
</span></span></code></pre></div><p><em>Note: we have to specifically import <code>change-model</code> from the <code>models</code>
vocabulary, since it might conflict with an <code>accessor</code>.</em></p>
<p><a href="https://www.factorcode.org">Factor</a> user interface elements are called
<a href="https://docs.factorcode.org/content/article-building-ui.html">gadgets</a>.
Many of them support being dynamically updated by being connected to
<a href="https://docs.factorcode.org/content/article-models.html">models</a>. Each
model maintains a list of connections that should be updated when the
value being held by the model changes.</p>
<h3 id="the-model">The Model</h3>
<p>Our <code>calculator</code> model is based on the notion that we have two numbers
(<code>x</code> and <code>y</code>) and an operator that can be applied to produce a new
value.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">calculator</span> < <span class="nc">model</span> <span class="nv">x</span> <span class="nv">y</span> <span class="nv">op</span> <span class="nv">valid</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><calculator></span> <span class="nf">( -- </span><span class="nv">model</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"0"</span> calculator new-model <span class="m">0 </span>>>x <span class="k">;
</span></span></span></code></pre></div><p>If we want to reset the model (such as when we press the “clear”
button):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">reset</span> <span class="nf">( </span><span class="nv">model</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>>>x <span class="no">f </span>>>y <span class="no">f </span>>>op <span class="no">f </span>>>valid <span class="s">"0"</span> <span class="nb">swap </span>set-model <span class="k">;
</span></span></span></code></pre></div><p>We’re storing all values as floating-point numbers, but (for display
purposes) we’ll show integers when possible:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">display</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">>float </span>number>string <span class="nb">dup </span><span class="s">".0"</span> <span class="nb">tail? </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">dup length </span><span class="m">2 </span><span class="nb">- head
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">when </span><span class="k">;
</span></span></span></code></pre></div><p>Each of <code>x</code> and <code>y</code> can be set based on the <code>value</code>, and the <code>op</code> is
specified as a quotation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">set-x</span> <span class="nf">( </span><span class="nv">model</span> <span class="nf">-- </span><span class="nv">model</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>value>> string>number >>x <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">set-y</span> <span class="nf">( </span><span class="nv">model</span> <span class="nf">-- </span><span class="nv">model</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>value>> string>number >>y <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">set-op</span> <span class="nf">( </span><span class="nv">model</span> <span class="nv">quot:</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">y</span> <span class="nf">-- </span><span class="nv">z</span> <span class="nf">) -- )
</span></span></span><span class="line"><span class="cl"> >>op set-x <span class="no">f </span>>>y <span class="no">f </span>>>valid <span class="nb">drop </span><span class="k">;
</span></span></span></code></pre></div><p>Pushing the “=” button triggers the calculation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(solve)</span> <span class="nf">( </span><span class="nv">model</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ x>> ] [ y>> ] [ op>> ] <span class="nb">tri </span>call( x y -- z )
</span></span><span class="line"><span class="cl"> [ >>x ] <span class="nb">keep </span>display <span class="nb">swap </span>set-model <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">solve</span> <span class="nf">( </span><span class="nv">model</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>op>> [ <span class="nb">dup </span>y>> [ set-y ] <span class="nb">unless </span>(solve) ] [ <span class="nb">drop </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>We support negating the number:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">negate</span> <span class="nf">( </span><span class="nv">model</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>valid>> [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>value>> <span class="s">"-"</span> <span class="nb">head?
</span></span></span><span class="line"><span class="cl"> [ [ <span class="m">1 </span><span class="nb">tail </span>] change-model ]
</span></span><span class="line"><span class="cl"> [ [ <span class="s">"-"</span> <span class="nb">prepend </span>] change-model ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] [ <span class="nb">drop </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>And pushing the “.” button (to add a decimal), or a number (to add a
digit):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">decimal</span> <span class="nf">( </span><span class="nv">model</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>valid>
</span></span><span class="line"><span class="cl"> [ [ <span class="nb">dup </span><span class="s">"."</span> <span class="nb">subseq? </span>[ <span class="s">"."</span> <span class="nb">append </span>] <span class="nb">unless </span>] change-model ]
</span></span><span class="line"><span class="cl"> [ <span class="no">t </span>>>valid <span class="s">"0."</span> <span class="nb">swap </span>set-model ] <span class="nb">if </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">digit</span> <span class="nf">( </span><span class="nv">n</span> <span class="nv">model</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>valid>
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap </span>[ <span class="nb">append </span>] <span class="nb">curry </span>change-model ]
</span></span><span class="line"><span class="cl"> [ <span class="no">t </span>>>valid set-model ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>That pretty much rounds out the basic features of the model.</p>
<h3 id="the-gui">The GUI</h3>
<p>For convenience, I store the calculator model in a global symbol:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">calc</span>
</span></span><span class="line"><span class="cl"><calculator> calc <span class="nb">set-global
</span></span></span></code></pre></div><p>I can use that to create buttons for each type (using short names and
unicode characters to make the code a bit prettier):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">[C]</span> <span class="nf">( -- </span><span class="nv">button</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"C"</span> calc <span class="nb">get-global </span>'[ <span class="nb">drop </span>_ reset ] <border-button> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">[±]</span> <span class="nf">( -- </span><span class="nv">button</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"±"</span> calc <span class="nb">get-global </span>'[ <span class="nb">drop </span>_ negate ] <border-button> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">[+]</span> <span class="nf">( -- </span><span class="nv">button</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"+"</span> calc <span class="nb">get-global </span>'[ <span class="nb">drop </span>_ [ <span class="nb">+ </span>] set-op ] <border-button> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">[-]</span> <span class="nf">( -- </span><span class="nv">button</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"-"</span> calc <span class="nb">get-global </span>'[ <span class="nb">drop </span>_ [ <span class="nb">- </span>] set-op ] <border-button> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">[×]</span> <span class="nf">( -- </span><span class="nv">button</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"×"</span> calc <span class="nb">get-global </span>'[ <span class="nb">drop </span>_ [ <span class="nb">* </span>] set-op ] <border-button> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">[÷]</span> <span class="nf">( -- </span><span class="nv">button</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"÷"</span> calc <span class="nb">get-global </span>'[ <span class="nb">drop </span>_ [ <span class="nb">/ </span>] set-op ] <border-button> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">[=]</span> <span class="nf">( -- </span><span class="nv">button</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"="</span> calc <span class="nb">get-global </span>'[ <span class="nb">drop </span>_ solve ] <border-button> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">[.]</span> <span class="nf">( -- </span><span class="nv">button</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"."</span> calc <span class="nb">get-global </span>'[ <span class="nb">drop </span>_ decimal ] <border-button> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">[#]</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">button</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>calc <span class="nb">get-global </span>'[ <span class="nb">drop </span>_ _ digit ] <border-button> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">[_]</span> <span class="nf">( -- </span><span class="nv">label</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">""</span> <label> <span class="k">;
</span></span></span></code></pre></div><p>We will create a
<a href="https://docs.factorcode.org/content/article-ui.gadgets.labels.html">label</a>
that is updated when the model changes.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><display></span> <span class="nf">( -- </span><span class="nv">label</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> calc <span class="nb">get-global </span><label-control> { <span class="m">5 5 </span>} <border>
</span></span><span class="line"><span class="cl"> { <span class="m">1 </span>1/2 } >>align
</span></span><span class="line"><span class="cl"> COLOR: gray <solid> >>boundary <span class="k">;
</span></span></span></code></pre></div><p>And, finally, creating the GUI (using vertical and horizontal
<a href="https://docs.factorcode.org/content/article-ui-track-layout.html">track</a>
layouts):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><col></span> <span class="nf">( </span><span class="nv">quot</span> <span class="nf">-- </span><span class="nv">track</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> vertical <track> <span class="m">1 </span>>>fill { <span class="m">5 5 </span>} >>gap
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span>output>array [ <span class="m">1 </span>track-add ] <span class="nb">each </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><row></span> <span class="nf">( </span><span class="nv">quot</span> <span class="nf">-- </span><span class="nv">track</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> horizontal <track> <span class="m">1 </span>>>fill { <span class="m">5 5 </span>} >>gap
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span>output>array [ <span class="m">1 </span>track-add ] <span class="nb">each </span><span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">calc-ui</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <display>
</span></span><span class="line"><span class="cl"> [ [C] [±] [÷] [×] ] <row>
</span></span><span class="line"><span class="cl"> [ <span class="s">"7"</span> [#] <span class="s">"8"</span> [#] <span class="s">"9"</span> [#] [-] ] <row>
</span></span><span class="line"><span class="cl"> [ <span class="s">"4"</span> [#] <span class="s">"5"</span> [#] <span class="s">"6"</span> [#] [+] ] <row>
</span></span><span class="line"><span class="cl"> [ <span class="s">"1"</span> [#] <span class="s">"2"</span> [#] <span class="s">"3"</span> [#] [=] ] <row>
</span></span><span class="line"><span class="cl"> [ <span class="s">"0"</span> [#] [.] [_] [_] ] <row>
</span></span><span class="line"><span class="cl"> ] <col> { <span class="m">10 10 </span>} <border> <span class="s">"Calculator"</span> open-window <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">calc-ui</span>
</span></span></code></pre></div><p>Then, running the calculator application:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"calc-ui"</span> run
</span></span></code></pre></div><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/calc-ui/calc-ui.factor">GitHub</a>.</p>
Building "cat"
https://re.factorcode.org/2010/08/building-cat.html
Sat, 21 Aug 2010 11:42:00 -0700https://re.factorcode.org/2010/08/building-cat.html<p>One neat feature of <a href="https://www.factorcode.org">Factor</a> is the ability
to create and deploy programs as compiled binaries – both CLI
(command-line) or UI (graphical) applications.</p>
<p>I thought it might be fun to build the
<a href="https://en.wikipedia.org/wiki/Cat_(Unix)">cat</a> command-line program in
Factor, and show how it can be deployed as a binary. From the man pages:</p>
<blockquote>
<p><em>The cat utility reads files sequentially, writing them to the
standard output. The file operands are processed in command-line
order. If file is a single dash (’-’) or absent, cat reads from the
standard input.</em></p>
</blockquote>
<p>We’ll start by creating the <code>cat</code> vocabulary. You can either create the
<code>cat.factor</code> file yourself, or use
<a href="https://docs.factorcode.org/content/vocab-tools.scaffold.html">tools.scaffold</a>
to do it for you:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">tools.scaffold</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"cat"</span> scaffold-work
</span></span><span class="line"><span class="cl">Creating scaffolding for <span class="s">P" resource:work/cat/cat.factor"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"cat"</span> vocab edit
</span></span></code></pre></div><p>Begin the implementation by listing some imports and a namespace:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">command-line</span> <span class="nn">kernel</span> <span class="nn">io</span> <span class="nn">io.encodings.binary</span> <span class="nn">io.files</span>
</span></span><span class="line"><span class="cl"><span class="nn">namespaces</span> <span class="nn">sequences</span> <span class="nn">strings</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">cat</span>
</span></span></code></pre></div><p>Printing each line from a stream is easy using the
<a href="https://docs.factorcode.org/content/word-each-line,io.html">each-line</a>
word (flushing after each write to match the behavior of <code>cat</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">cat-lines</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">write nl flush </span>] <span class="nb">each-line </span><span class="k">;
</span></span></span></code></pre></div><p>I chose to treat files (which might be <a href="https://re.factorcode.org/2010/08/text-or-binary.html">text or
binary</a>) as
binary, reading and writing 1024 bytes at a time. We check that the file
exists, printing an error if not found:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">cat-stream</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> [ <span class="m">1024 </span><span class="nb">read dup </span>] [ <span class="nb">>string write flush </span>] <span class="nb">while drop </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">cat-file</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>exists?
</span></span><span class="line"><span class="cl"> [ binary [ cat-stream ] with-file-reader ]
</span></span><span class="line"><span class="cl"> [ <span class="nb">write </span><span class="s">": not found"</span> <span class="nb">write nl flush </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Given a list of files, with a special case for “-” (to read from
standard input), we can <code>cat</code> each one:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">cat-files</span> <span class="nf">( </span><span class="nv">paths</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span><span class="s">"-"</span> <span class="nb">= </span>[ <span class="nb">drop </span>cat-lines ] [ cat-file ] <span class="nb">if </span>] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>Finally, we need an entry point that checks if
<a href="https://docs.factorcode.org/content/vocab-command-line.html">command-line</a>
arguments have been provided:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">run-cat</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> command-line <span class="nb">get </span>[ cat-lines ] [ cat-files ] <span class="nb">if-empty </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">run-cat</span>
</span></span></code></pre></div><p>Using the
<a href="https://docs.factorcode.org/content/word-deploy-tool,ui.tools.deploy.html">deploy-tool</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"cat"</span> deploy-tool
</span></span></code></pre></div><p>
<img src="https://re.factorcode.org/images/2010-08-21-building-cat-cat.png" alt="" width="322" height="575" />
</p>
<p>Click “Save” to persist the deploy settings into a <code>deploy.factor</code> file,
and “Deploy” to create a binary. You should see output like the
following:</p>
<pre tabindex="0"><code>Deploying cat...
Writing vocabulary manifest
Preparing deployed libraries
Stripping manual memory management debug code
Stripping destructor debug code
Stripping stack effect checking from call( and execute(
Stripping specialized arrays
Stripping startup hooks
Stripping default methods
Stripping compiler classes
Finding megamorphic caches
Stripping globals
Compressing objects
Compressing quotations
Stripping word properties
Stripping symbolic word definitions
Stripping word names
Clearing megamorphic caches
Saving final image
</code></pre><p>And your binary should be in the same directory as your Factor
installation (in a <code>cat.app</code> sub-directory on the Mac).</p>
<pre tabindex="0"><code>$ ls -hl cat.app/Contents/MacOS/cat
-rwxr-xr-x 1 user staff 421k Aug 21 11:11 cat.app/Contents/MacOS/cat*
$ cat.app/Contents/MacOS/cat
hello, world
hello, world
^D
</code></pre><p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/cat/cat.factor">GitHub</a>.</p>
Marriage Sort
https://re.factorcode.org/2010/08/marriage-sort.html
Mon, 16 Aug 2010 09:43:00 -0700https://re.factorcode.org/2010/08/marriage-sort.html<p>Several months ago, someone
<a href="https://www.thelowlyprogrammer.com/2010/04/introducing-marriage-sort.html">introduced</a>
a sorting algorithm called “Marriage Sort”. The inspiration for it came
from an article analyzing how to (mathematically) <a href="https://www.mathpages.com/home/kmath018/kmath018.htm">select the best
wife/husband</a>.</p>
<p>The “conclusion” drawn from the article is that, given <code>N</code> candidates,
the strategy with the best expected value is to skip past the first
<code>sqrt(N) - 1</code> candidates and then choose the next “best so far”.</p>
<p>Translated loosely into a sorting algorithm, it goes something like
this:</p>
<ol>
<li>Given <code>N</code> candidates, calculate the number to skip.</li>
<li>Find the “best” candidate within the skip distance.</li>
<li>Move all the better candidates beyond the skip distance to the end.</li>
<li>Reduce <code>N</code> by the number of candidates moved.</li>
<li>Repeat from Step 1 until we run out of candidates.</li>
<li>Perform insertion sort.</li>
</ol>
<p>The marriage sort algorithm is not particularly fast, with a runtime of
O(n<sup>1.5</sup>), but sorting algorithms are fundamental to computing,
so I thought it would be fun to implement in
<a href="https://www.factorcode.org">Factor</a>.</p>
<p><em>Note: Factor comes with some sorting algorithms. The <code>sorting</code>
vocabulary implements merge sort and the <code>sorting.insertion</code> vocabulary
implements an in-place insertion sort.</em></p>
<p>First, some vocabularies and a namespace (we will be using
<a href="https://docs.factorcode.org/content/article-locals.html">locals</a> to
implement a couple of the words):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">kernel</span> <span class="nn">locals</span> <span class="nn">math</span> <span class="nn">math.functions</span> <span class="nn">sequences</span>
</span></span><span class="line"><span class="cl"><span class="nn">sorting.insertion</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">sorting.marriage</span>
</span></span></code></pre></div><p>We can take the loose algorithm and structure the <code>marriage-sort</code> word,
leaving the bulk of the work for the <code>(marriage-sort)</code> inner loop:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">marriage-sort</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup length
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>sqrt <span class="m">1 </span><span class="nb">- >fixnum dup </span><span class="m">0 </span><span class="nb">> </span>]
</span></span><span class="line"><span class="cl"> [ (marriage-sort) ] <span class="nb">while 2drop
</span></span></span><span class="line"><span class="cl"> [ ] insertion-sort <span class="k">;
</span></span></span></code></pre></div><p>We’ll need to find the index of the maximum element in a range:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">find-max</span> <span class="nf">( </span><span class="nv">from</span> <span class="nv">to</span> <span class="nv">seq</span> <span class="nf">-- </span><span class="nv">i</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> from to <span class="nb">>= </span>[ <span class="no">f </span>] [
</span></span><span class="line"><span class="cl"> from from <span class="m">1 </span><span class="nb">+ </span>[ <span class="nb">dup </span>to <span class="nb">< </span>] [
</span></span><span class="line"><span class="cl"> <span class="nb">2dup </span>[ seq <span class="nb">nth </span>] <span class="nb">bi@ < </span>[ <span class="nb">nip dup </span>] <span class="nb">when </span><span class="m">1 </span><span class="nb">+
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while drop
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>That leaves the <code>(marriage-sort)</code> word (probably more complex than
necessary, but it works):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">(marriage-sort)</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">end</span> <span class="nv">skip</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nv">end'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>skip seq find-max
</span></span><span class="line"><span class="cl"> skip end [ <span class="nb">2dup < </span>] [
</span></span><span class="line"><span class="cl"> <span class="nb">2over </span>[ seq <span class="nb">nth </span>] <span class="nb">bi@ <=
</span></span></span><span class="line"><span class="cl"> [ <span class="m">1 </span><span class="nb">- </span>[ seq <span class="nb">exchange </span>] <span class="nb">2keep </span>]
</span></span><span class="line"><span class="cl"> [ [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">dip </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while nip </span><span class="m">1 </span><span class="nb">- </span>[ seq <span class="nb">exchange </span>seq ] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div><p>Some performance numbers (given a 10,000 element random array):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10000 </span>[ random-32 ] <span class="nb">replicate
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup clone </span>[ natural-sort <span class="nb">drop </span>] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.004123694 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup clone </span>[ marriage-sort ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">0.063077446 </span>seconds
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup clone </span>[ [ ] insertion-sort ] time
</span></span><span class="line"><span class="cl">Running time: <span class="m">10.972027614 </span>seconds
</span></span></code></pre></div><p>As you can see, slower than <code>natural-sort</code> (which uses merge sort), but
much faster than <code>insertion-sort</code>, with similar in-place semantics. It’s
worth noting that the code for <code>insertion-sort</code> seems a little slow and
could probably be sped up quite a bit.</p>
<p>The code and some tests for this is on my
<a href="https://github.com/mrjbq7/re-factor/tree/master/sorting/marriage/">GitHub</a>.</p>
"Maybe" Accessor
https://re.factorcode.org/2010/08/maybe-accessor.html
Wed, 11 Aug 2010 12:37:00 -0700https://re.factorcode.org/2010/08/maybe-accessor.html<p><a href="https://factorcode.org">Factor</a> has support for standard
“object-oriented” programming concepts such as classes and attributes.
Recently, I wanted to “get an attributes value (setting it first if not
set)”. I came up with a technique to do this, and wanted to share.</p>
<p>First, some background. Defining a class “person” with attributes “name”
and “age”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">person</span> <span class="nv">name</span> <span class="nv">age</span> <span class="k">;
</span></span></span></code></pre></div><p>You can then create a
<a href="https://docs.factorcode.org/content/word-new,kernel.html%22">new</a>
instance with all attributes unset (e.g., set to <code>f</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> person <span class="nb">new </span><span class="m">.
</span></span></span><span class="line"><span class="cl">T{ person }
</span></span></code></pre></div><p>Or, you can create an instance <a href="https://docs.factorcode.org/content/word-boa,kernel.html">by order of
arguments</a>
(taking values from the stack):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Frank"</span> <span class="m">20 </span>person <span class="nb">boa </span><span class="m">.
</span></span></span><span class="line"><span class="cl">T{ person { name <span class="s">"Frank"</span> } { age <span class="m">20 </span>} }
</span></span></code></pre></div><p>Alternatively, you can use the
<a href="https://docs.factorcode.org/content/vocab-accessors.html">accessors</a>
vocabulary to set attributes on the instance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> person <span class="nb">new </span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Frank"</span> >>name <span class="m">20 </span>>>age <span class="m">.
</span></span></span><span class="line"><span class="cl">T{ person { name <span class="s">"Frank"</span> } { age <span class="m">20 </span>} }
</span></span></code></pre></div><p>Reading attributes from an instance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Frank"</span> <span class="m">20 </span>person <span class="nb">boa
</span></span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> name>
</span></span><span class="line"><span class="cl"><span class="s">"Frank"</span>
</span></span></code></pre></div><p>Sometimes it is useful to change attributes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Frank"</span> <span class="m">20 </span>person <span class="nb">boa </span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">1 </span><span class="nb">+ </span>] change-age <span class="m">.
</span></span></span><span class="line"><span class="cl">T{ person { name <span class="s">"Frank"</span> } { age <span class="m">21 </span>} }
</span></span></code></pre></div><p>If you want to change an attribute only if it was not already set, we
could use <code>change-name</code>. The definition of <code>change-name</code> is built using
“get” and “set” words (first get the current value, then call the
quotation and set the result as the new value).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> person <span class="nb">new </span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"Frank"</span> <span class="nb">or </span>] change-name <span class="m">.
</span></span></span><span class="line"><span class="cl">T{ person { name <span class="s">"Frank"</span> } }
</span></span></code></pre></div><p>Coming back to the original problem: how can I “set an attribute if not
set and then immediately get the attribute”? Using the “get, set, or
change” concepts, we could first change the name, then get the current
value:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> person <span class="nb">new </span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"Frank"</span> <span class="nb">or </span>] change-name
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> name>> <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"Frank"</span>
</span></span></code></pre></div><p>One problem with that is it performs two get’s and a set (and
potentially does work in the quotation that is not necessary if a value
already exists). It would be more efficient if we could do something
like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> person <span class="nb">new </span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="nb">dup </span>name>> [ <span class="nb">nip </span>] [ <span class="s">"Frank"</span> [ >>name <span class="nb">drop </span>] <span class="nb">keep </span>] <span class="nb">if* </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"Frank"</span>
</span></span></code></pre></div><p>But that code is pretty verbose, and obscures our intentions. It would
be better if we could define a <code>maybe-name</code> word that performs this
action:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">maybe-name</span> <span class="nf">( </span><span class="nv">object</span> <span class="nv">quot:</span> <span class="nf">( -- </span><span class="nv">x</span> <span class="nf">) -- </span><span class="nv">value</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ >>name <span class="nb">drop </span>] <span class="nb">keep </span>] <span class="nb">compose
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>name>> [ <span class="nb">nip </span>] ] <span class="nb">dip if* </span><span class="k">; inline
</span></span></span></code></pre></div><blockquote>
<p><em>Perhaps a better name for this word could be <code>?name>></code> or <code>|name>></code>,
both of which I like also.</em></p>
</blockquote>
<p>This works like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> person <span class="nb">new
</span></span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"Joe"</span> ] maybe-name <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"Joe"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"Frank"</span> <span class="m">30 </span>person <span class="nb">boa </span>
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"Joe"</span> ] maybe-name <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"Frank"</span>
</span></span></code></pre></div><p>It would be even better if we could define these words automatically for
every attribute in the class (the way the <code>accessors</code> vocab does). Well,
this isn’t too difficult (although the code that builds the word
programmatically is a little involved). We can take advantage of the
very dynamic nature of Factor:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">arrays</span> <span class="nn">kernel</span> <span class="nn">make</span> <span class="nn">quotations</span> <span class="nn">sequences</span>
</span></span><span class="line"><span class="cl"><span class="nn">slots</span> <span class="nn">words</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">accessors.maybe</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">maybe-word</span> <span class="nf">( </span><span class="nv">name</span> <span class="nf">-- </span><span class="nv">word</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"maybe-"</span> <span class="nb">prepend </span><span class="s">"accessors"</span> create <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">define-maybe</span> <span class="nf">( </span><span class="nv">name</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>maybe-word <span class="nb">dup </span>deferred? [
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="nb">over </span>setter-word <span class="no">\ drop</span> <span class="nb">2array </span>>quotation
</span></span><span class="line"><span class="cl"> [ <span class="nb">keep </span>] <span class="nb">curry </span>, <span class="no">\ compose</span> ,
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span>reader-word [ <span class="nb">dup </span>] <span class="nb">swap </span>1quotation <span class="nb">compose
</span></span></span><span class="line"><span class="cl"> [ [ <span class="nb">nip </span>] ] <span class="nb">compose </span>, <span class="no">\ dip</span> , <span class="no">\ if*</span> ,
</span></span><span class="line"><span class="cl"> ] [ ] make (( <span class="nb">object </span>quot: <span class="nf">( -- </span><span class="nv">x</span> <span class="nf">) </span>-- value )) define-inline
</span></span><span class="line"><span class="cl"> ] [ <span class="nb">2drop </span>] <span class="nb">if </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">define-maybe-accessors</span> <span class="nf">( </span><span class="nv">class</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="s">"slots"</span> word-prop [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>read-only>> [ <span class="nb">drop </span>] [ name>> define-maybe ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>Calling it will define a “maybe” accessor word for each slot in the
tuple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> << person define-maybe-accessors <span class="nb">>
</span></span></span></code></pre></div><p>This code and some tests is available on my
<a href="https://github.com/mrjbq7/re-factor/tree/master/accessors/maybe/">GitHub</a>.</p>
Anagrams
https://re.factorcode.org/2010/08/anagrams.html
Mon, 09 Aug 2010 08:48:00 -0700https://re.factorcode.org/2010/08/anagrams.html<p>Dave Thomas, one of the <a href="https://www.pragprog.com/">Pragmatic
Programmers</a>, has developed a series of
exercises on his site <a href="codekata.com">codekata.com</a>. The idea behind it
is based on the concept that to get good at something you should
“practice, practice, practice”. Malcolm Gladwell calls this the <a href="https://www.gladwell.com/outliers/outliers_excerpt1.html">10,000
Hour Rule</a> in
his book <em>Outliers</em>.</p>
<p>While browsing the site, I came across <a href="https://codekata.pragprog.com/2007/01/kata_six_anagra.html#more">Kata Six:
Anagrams</a>.
Below is my attempt in <a href="https://factorcode.org">Factor</a>.</p>
<blockquote>
<p><em>If you want to avoid spoilers and attempt this yourself, you might
not want to read the rest of this post.</em></p>
</blockquote>
<p>First, some preliminary imports and a namespace:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">arrays</span> <span class="nn">ascii</span> <span class="nn">assocs</span> <span class="nn">fry</span> <span class="nn">io.encodings.ascii</span> <span class="nn">io.files</span>
</span></span><span class="line"><span class="cl"><span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">memoize</span> <span class="nn">sequences</span> <span class="nn">sorting</span> <span class="nn">strings</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">anagrams</span>
</span></span></code></pre></div><p>One way to check if two words are anagrams is to sort their letters and
compare. For example, “listen” and “silent” are anagrams of each other
(i.e., when sorted, their letters are both “eilnst”).</p>
<p>We will use this approach to take a list of words and create a mapping
of their sorted letters to a list of words that are anagrams of each
other. After we do that, we’ll filter the map to only have words that
have anagrams (where two or more words share the same mapping).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(all-anagrams)</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">assoc</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> '[ <span class="nb">dup </span>natural-sort <span class="nb">>string </span>_ <span class="nb">push-at </span>] <span class="nb">each </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-anagrams</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> H{ } <span class="nb">clone </span>[ (all-anagrams) ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">nip length </span><span class="m">1 </span><span class="nb">> </span>] <span class="nb">assoc-filter </span><span class="k">;
</span></span></span></code></pre></div><p>You can see it in action:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="s">"listen"</span> <span class="s">"silent"</span> <span class="s">"orange"</span> } all-anagrams <span class="m">.
</span></span></span><span class="line"><span class="cl">H{ { <span class="s">"eilnst"</span> V{ <span class="s">"listen"</span> <span class="s">"silent"</span> } } }
</span></span></code></pre></div><p>Now that we have that, we need a word list. The link on the original
blog post no longer works, but most systems come with a dictionary, so
we will use that (making all the words lowercase so that we can compare
in a case-insensitive way).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MEMO:</span> <span class="nf">dict-words</span> <span class="nf">( -- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"/usr/share/dict/words"</span> ascii file-lines [ >lower ] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>Given a list of dictionary words, we can calculate all anagrams:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">MEMO:</span> <span class="nf">dict-anagrams</span> <span class="nf">( -- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> dict-words all-anagrams <span class="k">;
</span></span></span></code></pre></div><p>On my MacBook Pro, I see 234,936 words and 15,048 groups of anagrams.
Using these, we can write a word to look for anagrams by checking the
dictionary.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">anagrams</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">seq/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> >lower natural-sort <span class="nb">>string </span>dict-anagrams <span class="nb">at </span><span class="k">;
</span></span></span></code></pre></div><p>I chose to return <code>f</code> if no anagrams are found. You can try it out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"listen"</span> anagrams <span class="m">.
</span></span></span><span class="line"><span class="cl">V{ <span class="s">"enlist"</span> <span class="s">"listen"</span> <span class="s">"silent"</span> <span class="s">"tinsel"</span> }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"banana"</span> anagrams <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">f
</span></span></span></code></pre></div><p>The blog goes further and asks a couple questions:</p>
<ul>
<li>What sets of anagrams contain the most words?</li>
<li>What are the longest words that are anagrams?</li>
</ul>
<p>Both of these share a common process, which is to take a sequence and
filter it for the elements that have the longest length:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">longest</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">subseq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span>[ <span class="nb">length </span>max ] <span class="nb">reduce </span>'[ <span class="nb">length </span>_ <span class="nb">= </span>] <span class="nb">filter </span><span class="k">;
</span></span></span></code></pre></div><p>This works pretty simply:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="s">"a"</span> <span class="s">"ab"</span> <span class="s">"abc"</span> <span class="s">"abcd"</span> <span class="s">"hjkl"</span> } <span class="nb">longest </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="s">"abcd"</span> <span class="s">"hjkl"</span> }
</span></span></code></pre></div><p>Now we can write the words to answer those two questions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">most-anagrams</span> <span class="nf">( -- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> dict-anagrams <span class="nb">values longest </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">longest-anagrams</span> <span class="nf">( -- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> dict-anagrams [ <span class="nb">keys longest </span>] <span class="nb">keep </span>'[ _ <span class="nb">at </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>The answer is? The set of anagrams containing “groan” is the most (10
words). And two anagrams are tied for longest:
“pneumohydropericardium/hydropneumopericardium” and
“cholecystoduodenostomy/duodenocholecystostomy”. Wouldn’t you know,
they’d be medical words…</p>
<p>The code for this is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/anagrams/anagrams.factor">GitHub</a>.</p>
Fat Arrows
https://re.factorcode.org/2010/08/fat-arrows.html
Fri, 06 Aug 2010 11:55:00 -0700https://re.factorcode.org/2010/08/fat-arrows.html<p><em>Update: I found out that the <code>extra/pair-rocket</code> vocabulary implements
this functionality, apparently named after Ruby’s hash-rockets.</em></p>
<p>Today, I saw a <a href="https://efene.tumblr.com/post/910889826/fat-arrows-some-syntactic-sugar-for-dicts">blog
post</a>
about adding “fat arrows” (syntactic sugar to support dictionaries) to
the <a href="https://marianoguerra.com.ar/efene/">efene</a> programming language.</p>
<p>One of <a href="https://factorcode.org">Factor’s</a> strengths is defining new
syntax to make certain problems more possible or more elegant. In this
post, I will show what it takes to add “fat arrows” to Factor.</p>
<p>First, what is a “fat arrow”? It appears to be simple syntax to create
two-element arrays, but without using “array syntax”. Assuming we had
already defined it, it would work something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1 </span>=> <span class="m">2 .
</span></span></span><span class="line"><span class="cl">{ <span class="m">1 2 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="no">t </span>=> <span class="s">"some text"</span> <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="no">t </span><span class="s">"some text"</span> }
</span></span></code></pre></div><p>So, how do we implement it? The
<a href="https://docs.factorcode.org">documentation</a> is pretty detailed and
includes an article about the
<a href="https://docs.factorcode.org/content/article-parser.html">parser</a> and, in
particular, <a href="https://docs.factorcode.org/content/article-parsing-words.html">parsing
words</a>.</p>
<p>The basic strategy is:</p>
<ol>
<li>get the last object parsed</li>
<li>parse ahead to read the next object</li>
<li>wrap both elements into an array</li>
<li>place the array back onto the parse sequence</li>
</ol>
<p>The core to this is reading ahead to parse the next object. This can be
accomplished with the
<a href="https://docs.factorcode.org/content/word-scan-object,parser.html">scan-object</a>
word. Once we know that, it’s pretty straightforward to implement:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYNTAX: </span>=>
</span></span><span class="line"><span class="cl"> <span class="nb">unclip-last </span>scan-object <span class="nb">2array suffix! </span><span class="k">;
</span></span></span></code></pre></div><p>I’m not a huge fan of this syntax (since it is very close to
<a href="https://docs.factorcode.org/content/word-__lt__=__gt__,math.order.html"><=></a>,
one of the comparative words), but it does make
<a href="https://docs.factorcode.org/content/word-case,combinators.html">case</a>
statements look prettier. Here’s one from the examples:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYMBOLS: </span><span class="nf">yes</span> <span class="nf">no</span> <span class="nf">maybe</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">maybe {
</span></span><span class="line"><span class="cl"> yes => [ ] <span class="c">! Do nothing</span>
</span></span><span class="line"><span class="cl"> no => [ <span class="s">"No way!"</span> <span class="nb">throw </span>]
</span></span><span class="line"><span class="cl"> maybe => [ <span class="s">"Make up your mind!"</span> <span class="nb">print </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">"Invalid input; try again."</span> <span class="nb">print </span>]
</span></span><span class="line"><span class="cl">} <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div>Happy Numbers
https://re.factorcode.org/2010/08/happy-numbers.html
Thu, 05 Aug 2010 14:33:00 -0700https://re.factorcode.org/2010/08/happy-numbers.html<p>Another recent
<a href="https://programmingpraxis.com/2010/07/23/happy-numbers/">challenge</a> was
to implement a method to check for <a href="https://en.wikipedia.org/wiki/Happy_number">happy
numbers</a>.</p>
<blockquote>
<p><em>“Starting with any positive integer, replace the number by the sum of
the squares of its digits, and repeat the process until the number
equals 1 (where it will stay), or it loops endlessly in a cycle which
does not include 1. Those numbers for which this process ends in 1 are
<strong>happy numbers</strong>, while those that do not end in 1 are unhappy
numbers (or sad numbers).”</em></p>
</blockquote>
<p>Many of the solutions listed are short, and this might qualify as a nice
question to use when interviewing programmers. Here is one solution in
Python from Wikipedia:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">is_happy</span><span class="p">(</span><span class="n">k</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">s</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">k</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">digits</span> <span class="o">=</span> <span class="p">[</span><span class="nb">int</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">str</span><span class="p">(</span><span class="n">k</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="n">k</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">([</span><span class="n">i</span><span class="o">**</span><span class="mi">2</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">digits</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">s</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">True</span>
</span></span></code></pre></div><p>We first need a way to compute the “sum of the squares of its digits”.
We will be assuming all calculations are in base 10. One method is to
use “mod 10” to calculate the last digit, then compute “div 10” and
repeat until you’ve exhausted all digits (e.g., the number is zero). We
will be using the
<a href="https://docs.factorcode.org/content/word-__slash__mod,math.html">/mod</a>
(“divmod”) word to compute both at once.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">squares</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">s</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>[ <span class="nb">over </span><span class="m">0 </span><span class="nb">> </span>] [ [ <span class="m">10 </span><span class="nb">/mod sq </span>] <span class="nb">dip + </span>] <span class="nb">while nip </span><span class="k">;
</span></span></span></code></pre></div><p>You can show this works (e.g., 125 = 1<sup>2</sup> + 2<sup>2</sup> +
5<sup>2</sup> = 30):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">125 </span>squares <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">30
</span></span></span></code></pre></div><p>There are two simple ways to detect a cycle. One involves keeping the
“sum of the squares” in a set, checking each to see if its been seen
before (like the Python example above). The other is similar to how you
might detect a cycle in a singly linked list (also an occasional
interview question): allocate two pointers and advance one twice as
fast, if they ever point to the same object, it’s a cycle. I chose to
implement the latter method.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(happy?)</span> <span class="nf">( </span><span class="nv">n1</span> <span class="nv">n2</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ squares ] [ squares squares ] <span class="nb">bi* </span>{
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">1 </span><span class="nb">= </span>] [ <span class="nb">2drop </span><span class="no">t </span>] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">2dup = </span>] [ <span class="nb">2drop </span><span class="no">f </span>] }
</span></span><span class="line"><span class="cl"> [ (happy?) ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">happy?</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>(happy?) <span class="k">;
</span></span></span></code></pre></div><p>The happy numbers under 50 can be easily calculated:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">50 </span><iota> [ happy? ] <span class="nb">filter </span><span class="m">.
</span></span></span><span class="line"><span class="cl">V{ <span class="m">1 7 10 13 19 23 28 31 32 44 49 </span>}
</span></span></code></pre></div><p>We can use the
<a href="https://docs.factorcode.org/content/vocab-math.primes.html">math.primes</a>
vocabulary to check for “happy primes” under 200:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">200 </span><iota> [ [ happy? ] [ prime? ] <span class="nb">bi and </span>] <span class="nb">filter </span><span class="m">.
</span></span></span><span class="line"><span class="cl">V{ <span class="m">7 13 19 23 31 79 97 103 109 139 167 193 </span>}
</span></span></code></pre></div><p>Using <a href="https://www.factorcode.org">Factor’s</a> support for large numbers,
we can even check one of the large number claims that are made on the
Wikipedia page:</p>
<blockquote>
<p><em>“The palindromic prime 10<sup>150,006</sup> + 7426247 ×
10<sup>75,000</sup> + 1 is also a happy prime with 150,007 digits…”</em></p>
</blockquote>
<p>Waiting just a little while for the result (since its a rather large
number), we see that it is indeed a happy number:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">10 150006 </span>^ <span class="m">7426247 10 75000 </span>^ <span class="nb">* </span><span class="m">1 </span><span class="nb">+ + </span>happy? <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">t
</span></span></span></code></pre></div><p>The code for this is in my
<a href="https://github.com/mrjbq7/re-factor/blob/master/happy-numbers/happy-numbers.factor">GitHub</a>.</p>
Text-or-Binary?
https://re.factorcode.org/2010/08/text-or-binary.html
Wed, 04 Aug 2010 13:34:00 -0700https://re.factorcode.org/2010/08/text-or-binary.html<p>Sometimes it is useful to be able to tell if a file should be treated as
a stream of text or binary characters. Rather than use the file
extension (which might be missing or wrong), Subversion has a simple
<a href="https://subversion.apache.org/faq.html#binary-files">heuristic</a> based on
the file contents:</p>
<blockquote>
<p><em>Currently, Subversion just looks at the first 1024 bytes of the file;
if any of the bytes are zero, or if more than 15% are not ASCII
printing characters, then Subversion calls the file binary.</em></p>
</blockquote>
<p>Someone implemented this in a
<a href="https://github.com/qertoip/istext">library</a> written in Clojure. Here’s
my take, but in <a href="https://www.factorcode.org">Factor</a>.</p>
<p>Some vocabularies we will use, and a namespace:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">io</span> <span class="nn">io.encodings.binary</span> <span class="nn">io.files</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">text-or-binary</span>
</span></span></code></pre></div><p>Checking if any of the bytes are zero:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">includes-zeros?</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 </span><span class="nb">swap member? </span><span class="k">;
</span></span></span></code></pre></div><p>The first 32 characters (e.g., 0-31) of
<a href="https://www.asciidocs.com/">ASCII</a> are reserved for non-printing control
characters. Checking that a majority (over 85%) of characters are
printable (and assuming an empty sequence is printable):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">majority-printable?</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="no">t </span>] [
</span></span><span class="line"><span class="cl"> [ [ <span class="m">31 </span><span class="nb">> </span>] <span class="nb">count </span>] [ <span class="nb">length </span>] <span class="nb">bi / </span><span class="m">0.85 </span>
</span></span><span class="line"><span class="cl"> ] <span class="nb">if-empty </span><span class="k">;
</span></span></span></code></pre></div><p>Then, determining a sequence of bytes is text:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">text?</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ includes-zeros? <span class="nb">not </span>] [ majority-printable? ] <span class="nb">bi and </span><span class="k">;
</span></span></span></code></pre></div><p>And implementing the operation to check if a file is text or binary:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">text-file?</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> binary [ <span class="m">1024 </span><span class="nb">read </span>text? ] with-file-reader <span class="k">;
</span></span></span></code></pre></div><p>Using it is pretty easy:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/usr/share/dict/words"</span> text-file? <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">t
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/bin/sh"</span> text-file? <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="no">f
</span></span></span></code></pre></div><p>The code for this (and some tests) is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/text-or-binary/text-or-binary.factor">GitHub</a>.</p>
HAMURABI
https://re.factorcode.org/2010/08/hamurabi.html
Tue, 03 Aug 2010 17:24:00 -0700https://re.factorcode.org/2010/08/hamurabi.html<p>A few days ago, a programming
<a href="https://programmingpraxis.com/2010/07/27/hamurabi-bas">challenge</a> was
made to port a game called
<a href="https://en.wikipedia.org/wiki/Hamurabi">HAMURABI</a> from BASIC to a modern
programming language.</p>
<p>Obviously, I thought <a href="https://www.factorcode.org">Factor</a> would be a fun
contribution.</p>
<p><em>Warning: there are some spoilers below. If you plan on implementing
this on your own from scratch, you might not want to read further.</em></p>
<p>The game consists of a simple concept. You are governor of “ANCIENT
SUMERIA” and are responsible for certain yearly operations:</p>
<ul>
<li>managing the purchase or sale of land,</li>
<li>feeding your people from stored food,</li>
<li>and planting seeds to grow food for the future.</li>
</ul>
<p>Depending on how well you do, you might be lauded, hated, or even
impeached over the course of your 10-year term in office.</p>
<p>One way of thinking about a problem is to start from the outermost
process: welcome the user, create a game, run 10 years, and then finish
with a summary. This can be expressed pretty directly:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">hamurabi</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> welcome <game> <span class="m">10 </span>[ year ] <span class="nb">times </span>finish <span class="k">;
</span></span></span></code></pre></div><p>The main game logic is in the <code>year</code> word and consists of a series of
steps:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">year</span> <span class="nf">( </span><span class="nv">game</span> <span class="nf">-- </span><span class="nv">game</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">1 </span><span class="nb">+ </span>] change-year
</span></span><span class="line"><span class="cl"> report-status
</span></span><span class="line"><span class="cl"> update-randomness
</span></span><span class="line"><span class="cl"> trade-land
</span></span><span class="line"><span class="cl"> feed-people
</span></span><span class="line"><span class="cl"> plant-seeds
</span></span><span class="line"><span class="cl"> update-stores
</span></span><span class="line"><span class="cl"> update-births
</span></span><span class="line"><span class="cl"> update-deaths
</span></span><span class="line"><span class="cl"> check-plague
</span></span><span class="line"><span class="cl"> check-starvation <span class="k">;
</span></span></span></code></pre></div><p>The entire solution (about 250 lines of code) is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/hamurabi/hamurabi.factor">GitHub</a>.
Other solutions have been contributed including
<a href="https://pastebin.com/j32JaSEw">Java</a> (150 lines),
<a href="https://codepad.org/pc8FSfnQ">Lua</a> (185 lines), and <a href="https://lisp.pastebin.com/r1gL4zYT">Common
Lisp</a> (190 lines).</p>
<p>You can run it from the command line:</p>
<pre tabindex="0"><code>$ ./factor -run=hamurabi
</code></pre><p>Or, you can run it from the listener:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"hamurabi"</span> run
</span></span><span class="line"><span class="cl"> HAMURABI
</span></span><span class="line"><span class="cl"> CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA
</span></span><span class="line"><span class="cl">SUCCESSFULLY FOR A TEN-YEAR TERM OF OFFICE
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">HAMURABI: I BEG TO REPORT TO YOU,
</span></span><span class="line"><span class="cl">IN YEAR 1, <span class="m">0 </span>PEOPLE STARVED, <span class="m">5 </span>CAME TO THE CITY
</span></span><span class="line"><span class="cl">POPULATION IS NOW <span class="m">100.
</span></span></span><span class="line"><span class="cl">THE CITY NOW OWNS <span class="m">1000 </span>ACRES.
</span></span><span class="line"><span class="cl">YOU HARVESTED <span class="m">3 </span>BUSHELS PER ACRE.
</span></span><span class="line"><span class="cl">RATS ATE <span class="m">200 </span>BUSHELS.
</span></span><span class="line"><span class="cl">YOU NOW HAVE <span class="m">2800 </span>BUSHELS IN STORE.
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div>New Combinatoric Functions
https://re.factorcode.org/2010/07/new-combinatoric-functions.html
Sun, 25 Jul 2010 23:26:00 -0700https://re.factorcode.org/2010/07/new-combinatoric-functions.html<p><a href="https://en.wikipedia.org/wiki/Combinatorics">Combinatorics</a> can provide
some useful functions when working with sequences. In
<a href="https://www.factorcode.org">Factor</a>, these are mostly defined in the
<a href="https://docs.factorcode.org/content/vocab-math.combinatorics.html">math.combinatorics</a>
vocabulary.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USE:</span> <span class="nn">math.combinatorics</span>
</span></span></code></pre></div><p>Inspired by some functions from
<a href="https://richhickey.github.com/clojure-contrib/index.html#combinatorics">clojure.contrib</a>,
I recently contributed two additional combinatoric words to the Factor
project (although not with the same lazy semantics that the Clojure
version has).</p>
<h3 id="all-subsets">all-subsets:</h3>
<p>The first word, <code>all-subsets</code>, returns all
<a href="https://en.wikipedia.org/wiki/Subset">subsets</a> of a given sequence. This
can be calculated by iteratively taking <code>n</code> combinations of items from
the sequence, where <code>n</code> goes from <code>0</code> (the empty set) to <code>length</code> (the
sequence itself).</p>
<p>First, we observe how this works by experimenting with the
<a href="https://docs.factorcode.org/content/word-all-combinations,math.combinatorics.html">all-combinations</a>
word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1 2 </span>} <span class="m">0 </span>all-combinations <span class="m">.
</span></span></span><span class="line"><span class="cl">{ { } }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1 2 </span>} <span class="m">1 </span>all-combinations <span class="m">.
</span></span></span><span class="line"><span class="cl">{ { <span class="m">1 </span>} { <span class="m">2 </span>} }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1 2 </span>} <span class="m">2 </span>all-combinations <span class="m">.
</span></span></span><span class="line"><span class="cl">{ { <span class="m">1 2 </span>} }
</span></span></code></pre></div><p>By running it with various <code>n</code>, we have produced all of the subsets of
the <code>{ 1 2 }</code> sequence. Using a
<a href="https://docs.factorcode.org/content/word-%5B0__comma__b%5D,ranges.html">[0,b]</a>
range (from 0 to the length of the sequence), we
<a href="https://docs.factorcode.org/content/word-make,make.html">make</a> a
sequence of subsets:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-subsets</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">subsets</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>[0,b] [
</span></span><span class="line"><span class="cl"> [ <span class="nb">dupd </span>all-combinations [ , ] <span class="nb">each </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] { } make <span class="nb">nip </span><span class="k">;
</span></span></span></code></pre></div><p>The <code>all-subsets</code> word can then be demonstrated by:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1 2 3 </span>} all-subsets <span class="m">.
</span></span></span><span class="line"><span class="cl">{ { } { <span class="m">1 </span>} { <span class="m">2 </span>} { <span class="m">3 </span>} { <span class="m">1 2 </span>} { <span class="m">1 3 </span>} { <span class="m">2 3 </span>} { <span class="m">1 2 3 </span>} }
</span></span></code></pre></div><h3 id="selections">selections:</h3>
<p>Another useful function, <code>selections</code>, returns all the ways of taking
<code>n</code> (possibly the same) elements from a sequence.</p>
<p>First, we observe that there are two base cases:</p>
<ol>
<li>If we want all ways of taking <code>0</code> elements from the sequence, we
have only <code>{ }</code> (the empty sequence).</li>
<li>If we want all ways of taking <code>1</code> element from the sequence, we
essentially have a sequence for each element in the input sequence.</li>
</ol>
<p>If we take more elements from the sequence, we need to apply the
<a href="https://docs.factorcode.org/content/word-cartesian-product,sequences.html">cartesian-product</a>
word (which returns all possible pairs of elements from two sequences)
<code>n-1</code> times. For example, if we wanted to see all possible selections of
<code>2</code> elements from a sequence, run the cartesian-product once:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1 2 3 </span>} <span class="nb">dup cartesian-product concat </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> { <span class="m">1 1 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">1 2 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">1 3 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">2 1 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">2 2 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">2 3 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">3 1 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">3 2 </span>}
</span></span><span class="line"><span class="cl"> { <span class="m">3 3 </span>}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Using these observations, we can build the <code>selections</code> word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(selections)</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">selections</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dupd </span>[ <span class="nb">dup </span><span class="m">1 </span><span class="nb">> </span>] [
</span></span><span class="line"><span class="cl"> <span class="nb">swap pick cartesian-product </span>[
</span></span><span class="line"><span class="cl"> [ [ <span class="nb">dup length </span><span class="m">1 </span><span class="nb">> </span>[ flatten ] <span class="nb">when </span>, ] <span class="nb">each </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] { } make <span class="nb">swap </span><span class="m">1 </span><span class="nb">-
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while drop nip </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">selections</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">selections</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { <span class="m">0 </span>[ <span class="nb">drop </span>{ } ] }
</span></span><span class="line"><span class="cl"> { <span class="m">1 </span>[ <span class="nb">1array </span>] }
</span></span><span class="line"><span class="cl"> [ (selections) ]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div><p>This can be demonstrated by:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1 2 </span>} <span class="m">2 </span>selections <span class="m">.
</span></span></span><span class="line"><span class="cl">{ { <span class="m">1 1 </span>} { <span class="m">1 2 </span>} { <span class="m">2 1 </span>} { <span class="m">2 2 </span>} }
</span></span></code></pre></div><p><em>Note: we have defined this to take element order into account, so
<code>{ 1 2 }</code> and <code>{ 2 1 }</code> are different possible results. Also, it could
be argued that the result for <code>{ 1 2 3 } 1 selections</code> should be
<code>{ 1 2 3 } [ 1array ] map</code> – perhaps it should change to that in the
future.</em></p>
<p>This was
<a href="https://github.com/factor/factor/commit/f3174e9ad0e722d368860e113dceab627f2050f3">committed</a>
to the main repository recently.</p>
<p>Update: A comment by Jon Harper showed me a way to improve
<code>all-subsets</code>. Based on that, I also made some changes to <code>selections</code>
(perhaps it could be improved even more):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">all-subsets</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">subsets</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>[0,b] [ all-combinations ] <span class="nb">with map concat </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(selections)</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">selections</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ <span class="nb">1array </span>] <span class="nb">map dup </span>] [ <span class="m">1 </span><span class="nb">- </span>] <span class="nb">bi* </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">cartesian-product concat </span>[ { } <span class="nb">concat-as </span>] <span class="nb">map
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">with times </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">selections</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">selections</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb">> </span>[ (selections) ] [ <span class="nb">2drop </span>{ } ] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div>Parsing Configuration Files
https://re.factorcode.org/2010/07/parsing-configuration-files.html
Fri, 16 Jul 2010 23:07:00 -0700https://re.factorcode.org/2010/07/parsing-configuration-files.html<p>The other day I needed a parser for
<a href="https://en.wikipedia.org/wiki/INI_file">INI-style</a> configuration files.
When I couldn’t find a convenient <a href="https://www.factorcode.org">Factor</a>
vocabulary to do this, I decided to write one.</p>
<p>A basic configuration file could look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[owner]</span>
</span></span><span class="line"><span class="cl"><span class="na">name</span><span class="o">=</span><span class="s">John Doe</span>
</span></span><span class="line"><span class="cl"><span class="na">e-mail</span><span class="o">=</span><span class="s">[email protected]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[database]</span>
</span></span><span class="line"><span class="cl"><span class="na">host</span><span class="o">=</span><span class="s">127.0.0.1 # change to production when ready</span>
</span></span><span class="line"><span class="cl"><span class="na">port</span><span class="o">=</span><span class="s">1234</span>
</span></span><span class="line"><span class="cl"><span class="na">username</span><span class="o">=</span><span class="s">test</span>
</span></span><span class="line"><span class="cl"><span class="na">password</span><span class="o">=</span><span class="s">"a really long string"</span>
</span></span></code></pre></div><p>These configurations are essentially groups of name/value pairs, and can
be naturally expressed as an
<a href="https://docs.factorcode.org/content/article-assocs.html">assoc</a>. We will
be implementing a simple API for reading and writing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-ini</span> <span class="nf">( -- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-ini</span> <span class="nf">( </span><span class="nv">assoc</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">string>ini</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">ini>string</span> <span class="nf">( </span><span class="nv">assoc</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span></code></pre></div><p>This implementation uses these vocabularies:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">arrays</span> <span class="nn">assocs</span> <span class="nn">combinators</span> <span class="nn">formatting</span> <span class="nn">hashtables</span> <span class="nn">io</span>
</span></span><span class="line"><span class="cl"><span class="nn">io.streams.string</span> <span class="nn">kernel</span> <span class="nn">make</span> <span class="nn">math</span> <span class="nn">sequences</span> <span class="nn">strings</span>
</span></span><span class="line"><span class="cl"><span class="nn">strings.parser</span> <span class="k">;
</span></span></span></code></pre></div><p>Some utility words are used to trim spaces from tokens, extract strings
from section names (e.g., “[database]”), and remove comments from
lines:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">unspace</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="s">" \t\n\r"</span> <span class="nb">member? </span>] <span class="nb">trim </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">unwrap</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="nb">swap </span>[ <span class="nb">length </span><span class="m">1 </span><span class="nb">- </span>] <span class="nb">keep subseq </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">uncomment</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="sc">CHAR: # </span><span class="nb">over index </span>[ <span class="nb">head </span>] <span class="nb">when* </span><span class="k">;
</span></span></span></code></pre></div><p>There are a variety of parsing strategies we could use here. To keep
things simple, we will be parsing the configuration file line-by-line.
Also, we will make the assumption that each line contains either a
“[section]” or a “name=value” (but not both).</p>
<p>We know a line is a section if it starts with ‘[’ and ends with ‘]’:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">section?</span> <span class="nf">( </span><span class="nv">line</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">first </span><span class="sc">CHAR: [ </span><span class="nb">= </span>] [ <span class="nb">last </span><span class="sc">CHAR: ] </span><span class="nb">= </span>] <span class="nb">bi and </span><span class="k">;
</span></span></span></code></pre></div><p>The current section is parsed and stored as a two-element array
containing the name of the section and a vector of name/value pairs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">[section]</span> <span class="nf">( </span><span class="nv">line</span> <span class="nf">-- </span><span class="nv">section</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> unwrap unspace V{ } <span class="nb">clone 2array </span><span class="k">;
</span></span></span></code></pre></div><p>Each name/value is parsed and added to the vector of name/value pairs in
the current section:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">name=value</span> <span class="nf">( </span><span class="nv">section</span> <span class="nv">line</span> <span class="nf">-- </span><span class="nv">section'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="sc">CHAR: = </span><span class="nb">over index cut rest </span>[ unspace ] <span class="nb">bi@
</span></span></span><span class="line"><span class="cl"> <span class="nb">2array over second push </span><span class="k">;
</span></span></span></code></pre></div><p>We will be using the
<a href="https://docs.factorcode.org/content/article-namespaces-make.html">make</a>
words. When we encounter a new section, or the end of the file, we will
append the current section to the sequence of sections being built by
<code>make</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">section,</span> <span class="nf">( </span><span class="nv">section/f</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">first2 </span>>hashtable <span class="nb">2array </span>, ] <span class="nb">when* </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-line</span> <span class="nf">( </span><span class="nv">section</span> <span class="nv">line</span> <span class="nf">-- </span><span class="nv">section'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> uncomment unspace [
</span></span><span class="line"><span class="cl"> <span class="nb">dup </span>section?
</span></span><span class="line"><span class="cl"> [ <span class="nb">swap </span>section, [section] ] [ name=value ] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">unless-empty </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-ini</span> <span class="nf">( -- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="no">f </span>[ parse-line ] <span class="nb">each-line </span>section,
</span></span><span class="line"><span class="cl"> ] { } make >hashtable <span class="k">;
</span></span></span></code></pre></div><p>Implementing <code>write-ini</code> is pretty easy. It’s just a matter of iterating
over all values in the specified <code>assoc</code>, and printing them out with
some minor structure:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-ini</span> <span class="nf">( </span><span class="nv">assoc</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ <span class="s">"[%s]\n"</span> printf ] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> [ <span class="s">"%s=%s\n"</span> printf ] <span class="nb">assoc-each
</span></span></span><span class="line"><span class="cl"> <span class="nb">nl
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">assoc-each </span><span class="k">;
</span></span></span></code></pre></div><p>The <code>string>ini</code> and <code>ini>string</code> words are easy too. Both the
<code>read-ini</code> and <code>write-ini</code> words operate on input and output <code>streams</code>,
so we can use <a href="https://docs.factorcode.org/content/article-io.streams.string.html">string
streams</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">string>ini</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ read-ini ] with-string-reader <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">ini>string</span> <span class="nf">( </span><span class="nv">assoc</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ write-ini ] with-string-writer <span class="k">;
</span></span></span></code></pre></div><p>This was a really simple implementation. In addition to the basics, I
wanted to be able to support:</p>
<ul>
<li>Embedded escape characters (e.g., “\t”, “\n”, etc.).</li>
<li>Line continuations (e.g., multi-line values).</li>
<li>Java <a href="https://en.wikipedia.org/wiki/.properties">.properties</a> files.</li>
<li>Liberal parsing of minor formatting errors.</li>
<li>Support both ‘#’ and ‘;’ comment characters.</li>
<li>Quoted strings (e.g., name=“value”).</li>
</ul>
<p>You can find all that and more (along with tests and some documentation)
on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/ini-file/ini-file.factor">GitHub</a>.
I hope to contribute it to the main repository soon.</p>
Flipping text upside-down
https://re.factorcode.org/2010/07/flipping-text-upside-down.html
Wed, 14 Jul 2010 09:00:00 -0700https://re.factorcode.org/2010/07/flipping-text-upside-down.html<p>Over the last few years, there has been a recurring meme to flip text
upside-down. You can find
<a href="https://pne.livejournal.com/398399.html">posts</a> about it, online
<a href="https://www.fliptext.org/">text-flipping services</a>, and programming
libraries (e.g., for
<a href="https://search.cpan.org/~marcel/Text-UpsideDown/">Perl</a> and
<a href="https://www.splode.com/~friedman/software/emacs-lisp/src/upside-down.el">Emacs</a>).</p>
<p>All of these implementations seem to work the same way: find Unicode
characters that resemble upside-down version of the latin alphabet and
create a mapping that is used to turn text “upside-down”. Since
<a href="https://www.factorcode.org">Factor</a> has strong support for Unicode, I
thought it could use a library to flip strings.</p>
<p>What it should look like when we are done:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"abcdefghijklmnopqrstuvwxyz1234567890"</span> flip-text <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"068Ɫ95ᔭƐᄅ⇂zʎxʍʌnʇsɹbdouɯʃʞɾᴉɥᵷɟǝpɔqɐ"</span>
</span></span></code></pre></div><p>As is typical, we list our dependencies and create a namespace to hold
the new functions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">assocs</span> <span class="nn">kernel</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">flip-text</span>
</span></span></code></pre></div><p>Next, we will create the character mapping (simply hard-coding the
character lookups):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">CHARS</span> H{
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: a </span> <span class="m">0x0250 </span>}
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: b </span> <span class="sc">CHAR: q </span> }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: c </span> <span class="m">0x0254 </span>}
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: d </span> <span class="sc">CHAR: p </span> }
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: e </span> <span class="m">0x01DD </span>}
</span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: f </span> <span class="m">0x025F </span>}
</span></span><span class="line"><span class="cl"> ...
</span></span></code></pre></div><p>And then, since it is useful to make the <code>flip-text</code> word reversible
(e.g., return the original value if applied again), we will update the
mapping with the reverse entries:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">CHARS [ CHARS <span class="nb">set-at </span>] <span class="nb">assoc-each
</span></span></span></code></pre></div><p>Mapping a single character is pretty straight-forward (making sure to
pass the original character through if a mapping isn’t found):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">ch>flip</span> <span class="nf">( </span><span class="nv">ch</span> <span class="nf">-- </span><span class="nv">ch'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>CHARS <span class="nb">at </span>[ <span class="nb">nip </span>] <span class="nb">when* </span><span class="k">;
</span></span></span></code></pre></div><p>And then flipping a string of text is just a matter of flipping each
character and reversing the string:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">flip-text</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ ch>flip ] <span class="nb">map reverse </span><span class="k">;
</span></span></span></code></pre></div><p>The complete implementation for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/flip-text/flip-text.factor">GitHub</a>
account.</p>
Side by Side: Chi-Square Functions
https://re.factorcode.org/2010/07/side-by-side-chi-square-functions.html
Tue, 13 Jul 2010 10:14:00 -0700https://re.factorcode.org/2010/07/side-by-side-chi-square-functions.html<p>I noticed a <a href="https://www.machinelake.com/2009/11/25/side-by-side-python-common-lisp-clojure/">blog
post</a>
from the end of last year that compared Python, Common Lisp, and Clojure
implementations of a probability function used in bayesian spam filters.</p>
<p>The main point of the post seems to be exploring the syntax used by the
programming languages as well as some commentary on using library
functions for simplification. (In this case “simple” appears to mean
both understandable and low token count – an attribute descriptively
valued in Paul Graham’s <a href="https://www.paulgraham.com/arcchallenge.html">Arc
Challenge</a>).</p>
<p>The original <a href="https://www.linuxjournal.com/article/6467">article</a> that
inspired the author’s post gives a Python
<a href="https://www.linuxjournal.com/files/linuxjournal.com/linuxjournal/articles/064/6467/6467s2.html">function</a>
for calculating the probability of observing a value at least as extreme
in a <a href="https://en.wikipedia.org/wiki/Chi-square_distribution">chi-square
distribution</a> with
a given degrees of freedom:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">chi2P</span><span class="p">(</span><span class="n">chi</span><span class="p">,</span> <span class="n">df</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">assert</span> <span class="n">df</span> <span class="o">&</span> <span class="mi">1</span> <span class="o">==</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="n">m</span> <span class="o">=</span> <span class="n">chi</span> <span class="o">/</span> <span class="mf">2.0</span>
</span></span><span class="line"><span class="cl"> <span class="nb">sum</span> <span class="o">=</span> <span class="n">term</span> <span class="o">=</span> <span class="n">math</span><span class="o">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">m</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">df</span><span class="o">//</span><span class="mi">2</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">term</span> <span class="o">*=</span> <span class="n">m</span> <span class="o">/</span> <span class="n">i</span>
</span></span><span class="line"><span class="cl"> <span class="nb">sum</span> <span class="o">+=</span> <span class="n">term</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">min</span><span class="p">(</span><span class="nb">sum</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">)</span>
</span></span></code></pre></div><p>Using a few vocabularies:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">math.functions</span> <span class="nn">math.order</span> <span class="nn">ranges</span>
</span></span><span class="line"><span class="cl"><span class="nn">sequences</span> <span class="k">;
</span></span></span></code></pre></div><p>We can quickly translate the original function into
<a href="https://www.factorcode.org">Factor</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">chi2P</span> <span class="nf">( </span><span class="nv">chi</span> <span class="nv">df</span> <span class="nf">-- </span><span class="nv">result</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">even? </span>[ <span class="s">"odd degrees of freedom"</span> <span class="nb">throw </span>] <span class="nb">unless </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> [ <span class="m">2.0 </span><span class="nb">/ </span>] [ <span class="m">2 </span><span class="nb">/i </span>] <span class="nb">bi* </span>[1..b) [ <span class="nb">dupd / </span>] <span class="nb">map
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">neg </span>exp <span class="nb">dup </span>] <span class="nb">dip </span>[ <span class="nb">* </span>[ <span class="nb">+ </span>] <span class="nb">keep </span>] <span class="nb">each drop </span><span class="m">1.0 </span>min <span class="k">;
</span></span></span></code></pre></div><p>Written as a single word, and with some of the stack shuffling, its a
little hard to grok the function without mentally stepping through the
code. Instead, we can make a few small changes:</p>
<ul>
<li>Switch to using <a href="https://docs.factorcode.org/content/article-math-vectors.html">vector operations</a>.</li>
<li>Extract the “guts” of the <code>chi2P</code> word into an “internal” word.</li>
<li>Extract the assertion that degrees of freedom should be even into a new word.</li>
</ul>
<p>The new code looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">df-check</span> <span class="nf">( </span><span class="nv">df</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">even? </span>[ <span class="s">"odd degrees of freedom"</span> <span class="nb">throw </span>] <span class="nb">unless </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(chi2P)</span> <span class="nf">( </span><span class="nv">chi/2</span> <span class="nv">df/2</span> <span class="nf">-- </span><span class="nv">P</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [1..b) <span class="nb">dupd </span>n/v <span class="nb">swap neg </span>exp [ v*n <span class="nb">sum </span>] <span class="nb">keep + </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">chi2P</span> <span class="nf">( </span><span class="nv">chi</span> <span class="nv">df</span> <span class="nf">-- </span><span class="nv">P</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>df-check [ <span class="m">2.0 </span><span class="nb">/ </span>] [ <span class="m">2 </span><span class="nb">/i </span>] <span class="nb">bi* </span>(chi2P) <span class="m">1.0 </span>min <span class="k">;
</span></span></span></code></pre></div><p>To my eye, that looks “better”. Code can often be improved by smaller
functions, better word or argument names, and some thought to the visual
structure of the syntax. This was just a simple refactoring and (as with
most things in life) it could be improved further.</p>
Curious? Factor to the rescue!
https://re.factorcode.org/2010/07/curious-factor-to-the-rescue.html
Mon, 12 Jul 2010 10:31:00 -0700https://re.factorcode.org/2010/07/curious-factor-to-the-rescue.html<p>A lot of dynamic language users have strong interest in math
(particularly those who use functional programming languages). Some
months back I came across a fun <a href="https://citizen428.net/archives/421-Curious-Clojure-to-the-rescue!.html">blog
post</a>
about using <a href="https://clojure.org/">Clojure</a> to approximate <a href="https://en.wikipedia.org/wiki/E_(mathematical_constant)">Euler’s
Number</a>,
according to this observation:</p>
<blockquote>
<p><em>“Here is an example of e turning up unexpectedly. Select a random
number between 0 and 1. Now select another and add it to the first.
Keep doing this, piling on random numbers. How many random numbers, on
average, do you need to make the total greater than 1? Answer:
2.71828…”</em></p>
</blockquote>
<p>I wanted to contrast his solution with one using
<a href="https://www.factorcode.org">Factor</a>. We’ll use these vocabularies:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">random</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span></code></pre></div><p>We can use the
<a href="https://docs.factorcode.org/content/vocab-random.html">random</a>
vocabulary to create a random float between <code>0</code> and <code>1</code>, counting how
many numbers we need to add before the value is greater than <code>1</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">numbers-added</span> <span class="nf">( -- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0 0 </span>[ <span class="nb">dup </span><span class="m">1 </span><span class="nb">< </span>] [
</span></span><span class="line"><span class="cl"> [ <span class="m">1 </span><span class="nb">+ </span>] <span class="nb">dip </span><span class="m">0.0 1.0 </span>uniform-random-float <span class="nb">+
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">while drop </span><span class="k">;
</span></span></span></code></pre></div><p>Taking the average of a sequence of numbers is pretty easy:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">average</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">sum </span>] [ <span class="nb">length </span>] <span class="nb">bi / </span><span class="k">;
</span></span></span></code></pre></div><p>Similar to the original blog post, we’ll want to estimate the value of
<code>e</code> by running <code>numbers-added</code> a certain number of times and then taking
the average of the resulting sequence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">approximate-e</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">approx</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ numbers-added ] <span class="nb">replicate </span>average <span class="k">;
</span></span></span></code></pre></div><p>Running this from one thousand to one million times gives us an
increasingly accurate approximation of <code>e</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> { <span class="m">1000 10000 100000 1000000 </span>}
</span></span><span class="line"><span class="cl"> [ approximate-e <span class="nb">>float </span>] <span class="nb">map </span><span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">2.694 2.7186 2.72149 2.716958 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USING:</span> <span class="nn">math.constants</span> <span class="k">; </span>e <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">2.718281828459045
</span></span></span></code></pre></div><p>This could be improved by virtualizing the sequences and then performing
an incremental average as we generate each of <code>n</code> approximations. That
would likely result in both memory and speed improvements (although I
haven’t tried it yet).</p>
<p>The code for this is on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/euler/euler.factor">GitHub</a>.</p>
Where am I? The 10:10 code...
https://re.factorcode.org/2010/07/where-am-i-the-10-10-code.html
Tue, 06 Jul 2010 19:34:00 -0700https://re.factorcode.org/2010/07/where-am-i-the-10-10-code.html<p>A couple months ago, I came across a <a href="https://blog.jgc.org/2010/06/1010-code.html">blog
post</a> discussing an encoding
of geographic coordinates that might be “easier” to remember or exchange
than latitude/longitude pairs.</p>
<p>The encoding, known as “10:10”, is able to identify to a 10 meter
precision any location on the planet using a 10 character code. Without
going into the merits of the scheme, I will just note that there is some
good discussion in the comments section about ideas for its improvement.</p>
<p>Under the assumption that most programming blog posts are invitations to
port things into <a href="https://www.factorcode.org">Factor</a>, I decided to
implement the 10:10 code. Unfortunately, the provided Javascript code
uses relatively meaningless variable names, so my implementation suffers
via inheritance. Essentially, the code boils down into a couple steps:</p>
<ol>
<li>Calculate a single <code>p</code> number representing a latitude/longitude
pair.</li>
<li>Calculate a <code>tt</code> number representing a version of <code>p</code> modified by a
checksum.</li>
<li>Generate a 10:10 code by iteratively indexing into the alphabet.</li>
</ol>
<p>We will separate each of these steps into words that can be tested
individually and then combined to compute the 10:10 codes.</p>
<p>We will need some vocabularies and a namespace:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">math.functions</span> <span class="nn">ranges</span> <span class="nn">memoize</span> <span class="nn">sequences</span>
</span></span><span class="line"><span class="cl"><span class="nn">strings</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">ten-ten</span>
</span></span></code></pre></div><p>The 10:10 code uses a 29 character alphabet, so we will need to define
it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">ALPHABET</span> <span class="s">"ABCDEFGHJKMNPQRVWXY0123456789"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MEMO:</span> <span class="nf">BASE</span> <span class="nf">( -- </span><span class="nv">base</span> <span class="nf">) </span>ALPHABET <span class="nb">length </span><span class="k">;
</span></span></span></code></pre></div><p>First, we compute the <code>p</code> value:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">p</span> <span class="nf">( </span><span class="nv">lat</span> <span class="nv">lon</span> <span class="nf">-- </span><span class="nv">p</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">90 </span><span class="nb">+ </span>] [ <span class="m">180 </span><span class="nb">+ </span>] <span class="nb">bi*
</span></span></span><span class="line"><span class="cl"> [ <span class="m">10000 </span><span class="nb">* </span>floor <span class="nb">>fixnum </span>] <span class="nb">bi@
</span></span></span><span class="line"><span class="cl"> [ <span class="m">3600000 </span><span class="nb">* </span>] <span class="nb">dip + </span><span class="k">;
</span></span></span></code></pre></div><p>Then, we calculate and add the checksum to create a <code>tt</code> value:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">tt</span> <span class="nf">( </span><span class="nv">p</span> <span class="nf">-- </span><span class="nv">tt</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ BASE <span class="nb">* </span>] <span class="nb">keep </span><span class="m">10 </span>[1..b) [
</span></span><span class="line"><span class="cl"> [ BASE <span class="nb">/mod </span>] <span class="nb">dip *
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map nip sum </span>BASE <span class="nb">mod + </span>floor <span class="k">;
</span></span></span></code></pre></div><p>Next, we convert the <code>tt</code> value to a string (inserting spaces for
legibility):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">tt>string</span> <span class="nf">( </span><span class="nv">tt</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">10 </span>[ BASE <span class="nb">/mod </span>ALPHABET <span class="nb">nth </span>] <span class="nb">replicate </span>
</span></span><span class="line"><span class="cl"> <span class="nb">nip reverse >string
</span></span></span><span class="line"><span class="cl"> [ <span class="sc">CHAR: \s </span><span class="m">3 </span>] <span class="nb">dip insert-nth
</span></span></span><span class="line"><span class="cl"> [ <span class="sc">CHAR: \s </span><span class="m">7 </span>] <span class="nb">dip insert-nth </span><span class="k">;
</span></span></span></code></pre></div><p>And, finally, putting it all together:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">ten-ten</span> <span class="nf">( </span><span class="nv">lat</span> <span class="nv">lon</span> <span class="nf">-- </span><span class="nv">tt</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> p tt tt>string <span class="k">;
</span></span></span></code></pre></div><p>When I run <code>51.09559 1.12207 ten-ten</code> (the included example for the
location of the Eurotunnel in the UK), it gives me a value of
<code>MEQ N6G 7NY5</code>. This is not the same as the original author, but matches
a version written in C# by another commenter as well as a version I
wrote in Python to validate my results.</p>
<p>You can find my implementation on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/ten-ten/ten-ten.factor">GitHub</a>
account.</p>
Rolling Dice
https://re.factorcode.org/2010/07/rolling-dice.html
Thu, 01 Jul 2010 13:10:00 -0700https://re.factorcode.org/2010/07/rolling-dice.html<p><em>Update: I noticed that
<a href="https://docs.factorcode.org/content/word-random,random.html">random</a> is
<code>[0,b)</code> (from <code>0</code> to <code>n-1</code>). The code has been fixed to make it <code>[1..b]</code>
(e.g., return a number from 1 to n).</em></p>
<p>I was curious about the ability to define new syntax for
<a href="https://www.factorcode.org">Factor</a>, and to learn a little about how the
<a href="https://docs.factorcode.org/content/article-parser-lexer.html">lexer</a>
works. Inspired by a recent project that I’ve been working on, I thought
it would be interesting to define a simple DSL for calculating
<a href="https://en.wikipedia.org/wiki/Dice">dice</a> rolls.</p>
<p>We need a way to describe a dice roll. There are various methods used in
<a href="https://code.activestate.com/recipes/573437-multisided-dice-roller-dsl/">Python</a>
or
<a href="https://weblog.jamisbuck.org/2006/9/27/1d6-more-reasons-to-love-ruby">Ruby</a>.
A common example of a common short-hand description is <code>4d8</code> –
specifying how many times (<code>4</code>) to roll a dice with a number (<code>8</code>) of
sides.</p>
<p>Let’s setup a vocabulary and a list of dependencies that will be used to
implement this functionality:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">fry</span> <span class="nn">kernel</span> <span class="nn">lexer</span> <span class="nn">math</span> <span class="nn">math.parser</span> <span class="nn">peg.ebnf</span> <span class="nn">random</span>
</span></span><span class="line"><span class="cl"><span class="nn">sequences</span> <span class="nn">strings</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">dice</span>
</span></span></code></pre></div><p>We will be using
<a href="https://docs.factorcode.org/content/article-peg.ebnf.html">EBNF</a> parsers
similar to what was used in <a href="https://re.factorcode.org/2009/08/calculating-with-ebnf.html">building a
calculator</a>.
First we will implement support for basic (e.g., <code>4d8</code>) rolls.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">EBNF: parse-roll
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">number = </span>([0-9])+ => [[ <span class="nb">>string </span>string>number ]]
</span></span><span class="line"><span class="cl">dice <span class="nb">= </span><span class="s">"d"</span> <span class="nb">number </span> => [[ <span class="nb">second </span>'[ _ random ] ]]
</span></span><span class="line"><span class="cl">roll <span class="nb">= number </span>dice => [[ <span class="nb">first2 </span>'[ <span class="m">0 </span>_ [ @ <span class="nb">+ </span><span class="m">1 </span><span class="nb">+ </span>] <span class="nb">times </span>] ]]
</span></span><span class="line"><span class="cl"><span class="nb">error </span> <span class="nb">= </span>.* => [[ <span class="s">"unknown dice"</span> <span class="nb">throw </span>]]
</span></span><span class="line"><span class="cl">total <span class="nb">= </span>roll | <span class="nb">error
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">;EBNF
</span></span></code></pre></div><p>We can see how this works by trying it out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"4d8"</span> parse-roll <span class="m">.
</span></span></span><span class="line"><span class="cl">[ <span class="m">0 4 </span>[ <span class="m">8 </span>random <span class="nb">+ </span><span class="m">1 </span><span class="nb">+ </span>] <span class="nb">times </span>]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"4d8"</span> parse-roll <span class="nb">call </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">15
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"foo"</span> parse-roll
</span></span><span class="line"><span class="cl">unknown dice
</span></span></code></pre></div><p>We can now define a piece of
<a href="https://docs.factorcode.org/content/word-SYNTAX__colon__,syntax.html">SYNTAX:</a>
for dice rolls:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYNTAX: </span>ROLL: scan parse-roll <span class="nb">append </span><span class="k">;
</span></span></span></code></pre></div><p>And using it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> ROLL: 4d8 <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">13
</span></span></span></code></pre></div><p>This could be extended to support other things such as “base” numbers
(e.g., <code>4d8+10</code>), negative rolls, ranges, and better handling of parse
errors. The code for this is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/dice/dice.factor">GitHub</a>.</p>
Evenly Partition an Integer
https://re.factorcode.org/2010/05/evenly-partition-an-integer.html
Mon, 24 May 2010 20:23:00 -0700https://re.factorcode.org/2010/05/evenly-partition-an-integer.html<p><em>Update: I just noticed after re-watching Slava’s <a href="https://www.youtube.com/watch?v=f_0QlhYlS8g">Google Tech
Talk</a> that he discusses his
vector version of this word (which I originally was calling
“distribute”) at 34:20. Very cool.</em></p>
<p>I came across a
<a href="https://stackoverflow.com/questions/577427/evenly-divide-in-c">question</a>
on <a href="https://stackoverflow.com">Stack Overflow</a> about how to evenly divide
an integer.</p>
<p>The discussion was not particularly interesting, but it reminded me of a
similar problem that I had to solve, and decided to implement in
<a href="https://factorcode.org">Factor</a>. This was early in my experience with
Factor, and the conversations on IRC proved helpful for understanding
how to structure my Factor code, which I want to share.</p>
<p>This type of problem is part of <a href="https://en.wikipedia.org/wiki/Partition_(number_theory)">number
theory</a>. There
is some discussion on <a href="https://mathworld.wolfram.com/Partition.html">Wolfram
Mathworld</a> describing the
various ways that a positive integer can be partitioned into the sum of
some number of other positive integers.</p>
<p>In my particular case, I needed an even division with the result to be
produced in such a way that the rounding affect was distributed across
the result set. For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">3 1 </span>n-partition <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">3 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">3 3 </span>n-partition <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">1 1 1 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">5 3 </span>n-partition <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">2 1 2 </span>}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">3 5 </span>n-partition <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">1 0 1 0 1 </span>}
</span></span></code></pre></div><p>In an applicative language like <a href="https://www.python.org">Python</a>, you
might write this algorithm like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">n_partition</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">delta</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">/</span> <span class="n">n</span>
</span></span><span class="line"><span class="cl"> <span class="n">last</span><span class="p">,</span> <span class="n">total</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mf">0.0</span>
</span></span><span class="line"><span class="cl"> <span class="n">l</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">total</span> <span class="o">+=</span> <span class="n">delta</span>
</span></span><span class="line"><span class="cl"> <span class="n">value</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">round</span><span class="p">(</span><span class="n">total</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="n">l</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">value</span> <span class="o">-</span> <span class="n">last</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">last</span> <span class="o">=</span> <span class="n">value</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">l</span>
</span></span></code></pre></div><p>My first version in Factor solved the problem, but was terribly ugly and
non-idiomatic:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">n-partition</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">) </span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">/ </span>] <span class="nb">keep </span><span class="m">0 </span><span class="nb"><array> </span>[ <span class="m">0 0 </span>] <span class="nb">dip </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">+ </span>[ [ <span class="nb">dup </span>] <span class="nb">dip + </span>] <span class="nb">dip </span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>round ] <span class="nb">dip 2dup -
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>] <span class="nb">dip </span>
</span></span><span class="line"><span class="cl"> ] <span class="nb">map nip nip nip </span><span class="k">;
</span></span></span></code></pre></div><p>One sign that you are “doing it wrong” in Factor is frequent usage of
stack manipulation using <a href="https://docs.factorcode.org/content/article-shuffle-words.html">shuffle
words</a>.
Another sign is creating large words that would be better when broken up
into smaller pieces. My solution had both characteristics. Worse, I
could barely read it.</p>
<p>The Factor developers are very responsive to questions from beginners,
and are helpful in providing an experienced eye to guide newcomers in
learning better ways to write their programs. You can reach them on the
<a href="https://concatenative.org/wiki/view/Factor/Mailing%20list">mailing list</a>
or on the
<a href="https://concatenative.org/wiki/view/Concatenative%20IRC%20channel">#concatenative</a>
IRC channel. Often it is useful to <a href="https://paste.factorcode.org/">paste
code</a> so that others can see and comment
on it. I thought I’d ask for help, and see if I could learn how to
improve.</p>
<p>I received some iterative feedback showing each step along the way
towards improving the original code, by using higher-level words and
concepts:</p>
<ol>
<li>First, extract the logic inside <code>map</code> into its own word:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(n-partition)</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nv">d</span> <span class="nf">-- </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nv">d</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">+ </span>[ [ <span class="nb">dup </span>] <span class="nb">dip + </span>] <span class="nb">dip </span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>round ] <span class="nb">dip 2dup -
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>] <span class="nb">dip </span><span class="k">; </span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">n-partition</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">) </span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">/ </span>] <span class="nb">keep </span><span class="m">0 </span><span class="nb"><array> </span>[ <span class="m">0 0 </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> [ (n-partition) ] <span class="nb">map nip nip nip </span><span class="k">;
</span></span></span></code></pre></div><ol start="2">
<li>Instead of the first <code>+</code>, use <code>drop</code> since there is always a zero there.</li>
<li>Instead of <code>[ dup ] dip</code>, use <code>dupd</code>.</li>
<li>Instead of <code>[ drop ] dip</code>, use <code>nip</code>.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(n-partition)</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nv">d</span> <span class="nf">-- </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nv">d</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">drop </span>[ <span class="nb">dupd + </span>] <span class="nb">dip </span>[ <span class="nb">dup </span>round ] <span class="nb">dip 2dup - nip </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">n-partition</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">) </span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">/ </span>] <span class="nb">keep </span><span class="m">0 </span><span class="nb"><array> </span>[ <span class="m">0 0 </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> [ (n-partition) ] <span class="nb">map nip nip nip </span><span class="k">;
</span></span></span></code></pre></div><ol start="5">
<li>Realize that <code>[ foo ] dip [ bar ] dip</code> is the same as
<code>[ foo bar ] dip</code>.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(n-partition)</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nv">d</span> <span class="nf">-- </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nv">d</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">drop </span>[ <span class="nb">dupd + dup </span>round ] <span class="nb">dip 2dup - nip </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">n-partition</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">) </span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">/ </span>] <span class="nb">keep </span><span class="m">0 </span><span class="nb"><array> </span>[ <span class="m">0 0 </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> [ (n-partition) ] <span class="nb">map nip nip nip </span><span class="k">;
</span></span></span></code></pre></div><ol start="6">
<li>Realize that <code>2dup - nip</code> is <code>dupd -</code>.</li>
<li>Realize that <code>dupd</code> is <code>[ dup ] dip</code> and apply the <code>[ foo bar ] dip</code>
rule again.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(n-partition)</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nv">d</span> <span class="nf">-- </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nv">d</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">drop </span>[ <span class="nb">dupd + dup </span>round <span class="nb">dup </span>] <span class="nb">dip - </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">n-partition</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">) </span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">/ </span>] <span class="nb">keep </span><span class="m">0 </span><span class="nb"><array> </span>[ <span class="m">0 0 </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> [ (n-partition) ] <span class="nb">map nip nip nip </span><span class="k">;
</span></span></span></code></pre></div><ol start="8">
<li>Now, extract the <code>drop</code> out and put it inside the <code>map</code>.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(n-partition)</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nf">-- </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nv">d</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dupd + dup </span>round <span class="nb">dup </span>] <span class="nb">dip - </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">n-partition</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">) </span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">/ </span>] <span class="nb">keep </span><span class="m">0 </span><span class="nb"><array> </span>[ <span class="m">0 0 </span>] <span class="nb">dip </span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>(n-partition) ] <span class="nb">map nip nip nip </span><span class="k">;
</span></span></span></code></pre></div><ol start="9">
<li>Realize that <code>[ drop foo ] map</code> is <code>[ foo ] replicate</code>.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(n-partition)</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nf">-- </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nv">d</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dupd + dup </span>round <span class="nb">dup </span>] <span class="nb">dip - </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">n-partition</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">) </span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">/ </span>] <span class="nb">keep </span><span class="m">0 </span><span class="nb"><array> </span>[ <span class="m">0 0 </span>] <span class="nb">dip </span>
</span></span><span class="line"><span class="cl"> [ (n-partition) ] <span class="nb">replicate nip nip nip </span><span class="k">;
</span></span></span></code></pre></div><ol start="10">
<li>Now, <code>replicate</code> doesn’t care what elements of the sequence are,
only its length, so you may as well be using “n” instead of an array
of “n” zeroes, so we can remove the <code>0 <array></code>.</li>
<li>Finally, <code>[ foo ] keep [ bar ] dip</code> is the same as
<code>[ foo bar ] keep</code>.</li>
</ol>
<p>This produces our final “cleaned up” version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(n-partition)</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nf">-- </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nv">d</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dupd + dup </span>round <span class="nb">dup </span>] <span class="nb">dip - </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">n-partition</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">/ </span><span class="m">0 0 </span>] <span class="nb">keep </span>[ (n-partition) ] <span class="nb">replicate nip nip nip </span><span class="k">;
</span></span></span></code></pre></div><p>Afterwards, I discussed with Slava other alternatives. One idea was to
use <a href="https://docs.factorcode.org/content/article-locals.html">locals</a> for
the <code>(n-partition)</code> word, but it was not much of an improvement by
itself (perhaps better if we introduced meaningful names instead of
<code>a b c d</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">::</span> <span class="nf">(n-partition)</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nf">-- </span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span> <span class="nv">d</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> a a b <span class="nb">+ dup </span>round <span class="nb">dup </span>c <span class="nb">- </span><span class="k">;
</span></span></span></code></pre></div><p>Another idea was to work on an array of values and then apply <a href="https://docs.factorcode.org/content/article-math-vectors-arithmetic.html">vector
arithmetic</a>
on it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">percentages</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">) </span>[ [1..b] ] <span class="nb">keep </span>v/n <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">steps</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">) </span>percentages n*v <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">rounded</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq'</span> <span class="nf">) </span>[ round ] <span class="nb">map </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">differences</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq'</span> <span class="nf">) </span><span class="nb">dup </span><span class="m">0 </span><span class="nb">prefix </span>v- <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">n-partition</span> <span class="nf">( </span><span class="nv">x</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">) </span>steps rounded differences <span class="k">;
</span></span></span></code></pre></div><p>To some extent, this is a question of aesthetics. I happen to like this
last version (that uses vectors) the best, but I can see reasons why the
original cleaned up version might be attractive too.</p>
Creating Fake Data
https://re.factorcode.org/2010/05/creating-fake-data.html
Mon, 17 May 2010 13:01:00 -0700https://re.factorcode.org/2010/05/creating-fake-data.html<p>A few days ago there was a
<a href="https://news.ycombinator.com/item?id=1351663">post</a> on <a href="https://news.ycombinator.com">Hacker
News</a> about a project called
<a href="https://github.com/marak/Faker.js/">Faker.js</a>. Basically, the project is
a Javascript clone of libraries in <a href="https://faker.rubyforge.org/">Ruby</a>
and
<a href="https://search.cpan.org/~jasonk/Data-Faker-0.07/lib/Data/Faker.pm">Perl</a>
for creating fake data for names, phone numbers, e-mails, street
addresses, company information, etc.</p>
<p>I took a look at the code, and got inspired to create a
<a href="https://www.factorcode.org">Factor</a> version.</p>
<p>First, some useful vocabularies:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">ascii</span> <span class="nn">combinators</span> <span class="nn">fry</span> <span class="nn">kernel</span> <span class="nn">make</span> <span class="nn">math</span> <span class="nn">random</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span></code></pre></div><p>All three implementations seem to use simple random selection to
generate the “fake” information. For example, each has a long list of
valid first and last names. It looks sort of like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">FIRST-NAME</span> {
</span></span><span class="line"><span class="cl"> <span class="s">"Aaliyah"</span> <span class="s">"Aaron"</span> <span class="s">"Abagail"</span> <span class="s">"Abbey"</span> <span class="s">"Abbie"</span> <span class="s">"Abbigail"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Abby"</span> <span class="s">"Abdiel"</span> <span class="s">"Abdul"</span> <span class="s">"Abdullah"</span> <span class="s">"Abe"</span> <span class="s">"Abel"</span> <span class="s">"Abelardo"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Abigail"</span> <span class="s">"Abigale"</span> <span class="s">"Abigayle"</span> <span class="s">"Abner"</span> <span class="s">"Abraham"</span> <span class="s">"Ada"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Adah"</span> <span class="s">"Adalberto"</span> <span class="s">"Adaline"</span> <span class="s">"Adam"</span> <span class="s">"Adan"</span> <span class="s">"Addie"</span> <span class="s">"Addison"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Adela"</span> <span class="s">"Adelbert"</span> <span class="s">"Adele"</span> <span class="s">"Adelia"</span> <span class="s">"Adeline"</span> <span class="s">"Adell"</span>
</span></span><span class="line"><span class="cl"> ...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">LAST-NAME</span> {
</span></span><span class="line"><span class="cl"> <span class="s">"Abbott"</span> <span class="s">"Abernathy"</span> <span class="s">"Abshire"</span> <span class="s">"Adams"</span> <span class="s">"Altenwerth"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Anderson"</span> <span class="s">"Ankunding"</span> <span class="s">"Armstrong"</span> <span class="s">"Auer"</span> <span class="s">"Aufderhar"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Bahringer"</span> <span class="s">"Bailey"</span> <span class="s">"Balistreri"</span> <span class="s">"Barrows"</span> <span class="s">"Bartell"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Bartoletti"</span> <span class="s">"Barton"</span> <span class="s">"Bashirian"</span> <span class="s">"Batz"</span> <span class="s">"Bauch"</span> <span class="s">"Baumbach"</span>
</span></span><span class="line"><span class="cl"> ...
</span></span></code></pre></div><p>Given a long list of possible names, generating a fake name is no more
complicated than:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> FIRST-NAME random LAST-NAME random <span class="s">" "</span> <span class="nb">glue </span><span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"Greyson Barrows"</span>
</span></span></code></pre></div><p>Similarly, creating phone numbers is no more complicated than a list of
typical phone number patterns, combined with a word that performs
substitution of random numbers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">PHONE-NUMBER</span> {
</span></span><span class="line"><span class="cl"> <span class="s">"###-###-####"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"(###)###-####"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"1-###-###-####"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"###.###.####"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"###-###-####"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"(###)###-####"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"1-###-###-####"</span>
</span></span><span class="line"><span class="cl"> ...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(numbers)</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span><span class="sc">CHAR: # </span><span class="nb">= </span>[ <span class="nb">drop </span><span class="s">"0123456789"</span> random ] <span class="nb">when </span>] <span class="nb">map </span><span class="k">;
</span></span></span></code></pre></div><p>Generating a fake phone number (without performing any kind of
validation on area codes or local numbers) is as easy as:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> PHONE-NUMBER random (numbers) <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"352-327-9815"</span>
</span></span></code></pre></div><p>For added flavor, the author chose to include “business bullshit”
generation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">5 </span>[ fake-bs <span class="m">. </span>] <span class="nb">times
</span></span></span><span class="line"><span class="cl"><span class="s">"leverage 24/7 models"</span>
</span></span><span class="line"><span class="cl"><span class="s">"deploy ubiquitous vortals"</span>
</span></span><span class="line"><span class="cl"><span class="s">"maximize holistic channels"</span>
</span></span><span class="line"><span class="cl"><span class="s">"exploit real-time niches"</span>
</span></span><span class="line"><span class="cl"><span class="s">"unleash proactive mindshare"</span>
</span></span></code></pre></div><p>And “product catch phrase” generation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">5 </span>[ fake-catch-phrase <span class="m">. </span>] <span class="nb">times
</span></span></span><span class="line"><span class="cl"><span class="s">"Reverse-engineered value-added toolset"</span>
</span></span><span class="line"><span class="cl"><span class="s">"Diverse systemic concept"</span>
</span></span><span class="line"><span class="cl"><span class="s">"Ergonomic holistic pricing structure"</span>
</span></span><span class="line"><span class="cl"><span class="s">"Persevering local interface"</span>
</span></span><span class="line"><span class="cl"><span class="s">"Intuitive human-resource time-frame"</span>
</span></span></code></pre></div><p>Useful for scale testing websites, practical jokes, and probably less
innocent purposes. You can see the full version on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/fake-data/fake-data.factor">GitHub</a>
account.</p>
Time Spans for Humans
https://re.factorcode.org/2010/05/time-spans-for-humans.html
Fri, 14 May 2010 09:37:00 -0700https://re.factorcode.org/2010/05/time-spans-for-humans.html<p>When dealing with time spans or
<a href="https://docs.factorcode.org/content/word-duration,calendar.html">durations</a>,
it is nice to be able to render elapsed time in a way that is easy for
the human eye to read.</p>
<p>In some cases, this could mean translating the way you might say it or
write it (<em>two years, ten days, four hours, thirty minutes, and fifteen
seconds</em>) or a more concise form (<em>2y 10d 4h 30m 15s</em>). The concise form
has become popular in some web applications, and solutions exist for
both in most languages including
<a href="https://mganesh.blogspot.com/2009/02/python-human-readable-time-span-give.html">python</a>,
<a href="https://search.cpan.org/~avif/Time-Duration-1.06/Duration.pm">perl</a>, and
<a href="https://commons.apache.org/lang/api-release/org/apache/commons/lang/time/DurationFormatUtils.html">java</a>.
I thought I would build a word in <a href="https://www.factorcode.org">Factor</a> to
calculate it.</p>
<p>First, we need some other vocabularies:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">kernel</span> <span class="nn">make</span> <span class="nn">math</span> <span class="nn">math.parser</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span></code></pre></div><p>The
<a href="https://docs.factorcode.org/content/article-namespaces-make.html">make</a>
vocabulary can be used to execute a quotation that builds a sequence of
values at runtime. This can be a little confusing for those new to
Factor. Basically, you provide it a quotation that periodically emits
values (by calling the <code>,</code> word) that are collected into the sequence
type you provide. For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="m">1 </span>, <span class="m">2 </span>, <span class="m">3 </span>, ] { } make <span class="m">.
</span></span></span><span class="line"><span class="cl">{ <span class="m">1 2 3 </span>}
</span></span></code></pre></div><p>Next, we need an algorithm. One way is to iteratively
<a href="https://docs.factorcode.org/content/word-__slash__mod,math.html">divmod</a>
the number of seconds into each category (e.g., years, weeks, days,
hours, minutes, seconds) and count it if the number in the category is
non-zero.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">elapsed-time</span> <span class="nf">( </span><span class="nv">seconds</span> <span class="nf">-- </span><span class="nv">string</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb">< </span>[ <span class="s">"negative seconds"</span> <span class="nb">throw </span>] <span class="nb">when </span>[
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> { <span class="m">60 </span><span class="s">"s"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">60 </span><span class="s">"m"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">24 </span><span class="s">"h"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">7 </span><span class="s">"d"</span> }
</span></span><span class="line"><span class="cl"> { <span class="m">52 </span><span class="s">"w"</span> }
</span></span><span class="line"><span class="cl"> { <span class="no">f </span><span class="s">"y"</span> }
</span></span><span class="line"><span class="cl"> } [
</span></span><span class="line"><span class="cl"> [ <span class="nb">first </span>[ <span class="nb">/mod </span>] [ <span class="nb">dup </span>] <span class="nb">if* </span>] [ <span class="nb">second </span>] <span class="nb">bi swap
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb">> </span>[ number>string <span class="nb">prepend </span>, ] [ <span class="nb">2drop </span>] <span class="nb">if
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each drop
</span></span></span><span class="line"><span class="cl"> ] { } make [ <span class="s">"0s"</span> ] [ <span class="nb">reverse </span><span class="s">" "</span> <span class="nb">join </span>] <span class="nb">if-empty </span><span class="k">;
</span></span></span></code></pre></div><p>And then to see it work:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">123456 </span>elapsed-time <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"1d 10h 17m 36s"</span>
</span></span></code></pre></div>Relative Time for Humans
https://re.factorcode.org/2010/05/relative-time-for-humans.html
Thu, 13 May 2010 19:11:00 -0700https://re.factorcode.org/2010/05/relative-time-for-humans.html<p>Many applications deal with events that have occurred at different
points in time. Often, these times are relatively recent, such as a list
of emails in an inbox or a list of status updates on a social network.
When we talk about when an event occurred, it is sometimes useful to be
precise (e.g., <em>Thu May 13 19:12:02 2010</em>), and other times it is better
to be a bit softer (e.g., <em>about a minute ago</em>).</p>
<p>This function gets built from time-to-time in various languages: for
example in
<a href="https://blogmag.net/blog/read/103/Nice_looking_elapsed_time_with_SQLAlchemy">Python</a>
and
<a href="https://37signals.com/svn/posts/1557-javascript-makes-relative-times-compatible-with-caching">Javascript</a>.
I thought it might be fun to see how simple it could be in
<a href="https://www.factorcode.org">Factor</a>.</p>
<p>A very useful type of <a href="https://docs.factorcode.org/content/article-conditionals.html">conditional
combinator</a>
is called <code>cond</code>. It applies to a sequence of quotations. The first
elements in the sequence are pairs of quotations. The combinator
proceeds to call the first quotation in each pair until a true value is
yielded, at which point it calls the second quotation in that pair and
stops iterating. A final quotation can be provided to handle the case
where none of the pairs returned a true value.</p>
<p>In typical applicative languages, this would be provided by an “if/else
if/else” chain. It is a general pattern and is used in Factor to
implement <code>case</code> (a type of <a href="https://en.wikipedia.org/wiki/Switch_statement">switch
statement</a>).</p>
<p>Using a few vocabularies:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">combinators</span> <span class="nn">formatting</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="k">;
</span></span></span></code></pre></div><p>We will write a word that takes the number of seconds that something
happened in the past, and calculates how long ago it was in “human
terms”:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">relative-time</span> <span class="nf">( </span><span class="nv">seconds</span> <span class="nf">-- </span><span class="nv">string</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb">< </span>[ <span class="s">"negative seconds"</span> <span class="nb">throw </span>] <span class="nb">when </span>{
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">1 </span><span class="nb">< </span>] [ <span class="nb">drop </span><span class="s">"just now"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">60 </span><span class="nb">< </span>] [ <span class="nb">drop </span><span class="s">"less than a minute ago"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">120 </span><span class="nb">< </span>] [ <span class="nb">drop </span><span class="s">"about a minute ago"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">2700 </span><span class="nb">< </span>] [ <span class="m">60 </span><span class="nb">/ </span><span class="s">"%d minutes ago"</span> sprintf ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">5400 </span><span class="nb">< </span>] [ <span class="nb">drop </span><span class="s">"about an hour ago"</span> ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">86400 </span><span class="nb">< </span>] [ <span class="m">3600 </span><span class="nb">/ </span><span class="s">"%d hours ago"</span> sprintf ] }
</span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">172800 </span><span class="nb">< </span>] [ <span class="nb">drop </span><span class="s">"1 day ago"</span> ] }
</span></span><span class="line"><span class="cl"> [ <span class="m">86400 </span><span class="nb">/ </span><span class="s">"%d days ago"</span> sprintf ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="k">;
</span></span></span></code></pre></div><p>Fairly concise as it turns out. If it wasn’t for the minor stack
manipulation, you might say this was getting very close to the essence
of the algorithm.</p>
What time is it? Part 3
https://re.factorcode.org/2010/04/what-time-is-it-part-3.html
Thu, 29 Apr 2010 17:59:00 -0700https://re.factorcode.org/2010/04/what-time-is-it-part-3.html<p>In <a href="https://re.factorcode.org/2010/04/what-time-is-it-part-1.html">Part
1</a>,
we implemented a DAYTIME server using TCP. In <a href="https://re.factorcode.org/2010/04/what-time-is-it-part-1.html">Part
2</a>,
we implemented a TIME server using UDP.</p>
<p>Today, we will implement a simple
<a href="https://en.wikipedia.org/wiki/Network_Time_Protocol">NTP</a> client using
UDP.</p>
<p>The NTP (Network Time Protocol) specification has been evolving since
1985. Most of the focus has been on algorithms for synchronizing the
clocks of computers to varying precisions. Currently the IETF is
drafting version 4, but has only formally documented up to version 3 in
<a href="https://tools.ietf.org/html/rfc1305">RFC 1305</a>. For use cases where less
precision is required, version 4 of a variant called SNTP is documented
in <a href="https://tools.ietf.org/html/rfc4330">RFC 4330</a>.</p>
<p>We will begin by referencing a set of vocabularies we depend upon:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">arrays</span> <span class="nn">calendar</span> <span class="nn">combinators</span> <span class="nn">destructors</span>
</span></span><span class="line"><span class="cl"><span class="nn">formatting</span> <span class="nn">kernel</span> <span class="nn">io</span> <span class="nn">io.sockets</span> <span class="nn">math</span> <span class="nn">pack</span> <span class="nn">random</span> <span class="nn">sequences</span> <span class="k">;
</span></span></span></code></pre></div><p>To parse and manipulate the NTP responses, we need to refer to the
specification. These are often helpfully documented in ascii diagrams.
The latest specification includes this description of a NTP packet:</p>
<pre tabindex="0"><code>1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|LI | VN |Mode | Stratum | Poll | Precision |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Root Delay |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Root Dispersion |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reference Identifier |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Reference Timestamp (64) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Originate Timestamp (64) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Receive Timestamp (64) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Transmit Timestamp (64) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Key Identifier (optional) (32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| |
| Message Digest (optional) (128) |
| |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
</code></pre><p>The specification contains detailed information about each of these
fields and how they should be interpreted and used. For now, we will
just create a tuple class to hold the values contained in a typical NTP
packet:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">ntp</span> <span class="nv">leap</span> <span class="nv">version</span> <span class="nv">mode</span> <span class="nv">stratum</span> <span class="nv">poll</span> <span class="nv">precision</span>
</span></span><span class="line"><span class="cl"><span class="nv">root-delay</span> <span class="nv">root-dispersion</span> <span class="nv">ref-id</span> <span class="nv">ref-timestamp</span>
</span></span><span class="line"><span class="cl"><span class="nv">orig-timestamp</span> <span class="nv">recv-timestamp</span> <span class="nv">tx-timestamp</span> <span class="k">;
</span></span></span></code></pre></div><p>Timestamps in the NTP protocol are stored in 64-bits. The first 32-bits
are an integer number of seconds, and the last 32-bits are a fractional
number of seconds. This gives the protocol a theoretical precision of
233 picoseconds. Most clocks in use by NTP servers are not that
accurate, so some of the precision is wasted. Currently, this timestamp
should be simply interpreted as a number of seconds since January 1,
1900 (this will change slightly when the 64-bit value overflows in the
year 2036).</p>
<p>To parse each timestamp, we will want to convert it into a number of
seconds and then use the
<a href="https://docs.factorcode.org/content/vocab-calendar.html">calendar</a>
vocabulary to manipulate it into a
<a href="https://docs.factorcode.org/content/word-timestamp%2Ccalendar.html">timestamp</a>
object:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(time)</span> <span class="nf">( </span><span class="nv">sequence</span> <span class="nf">-- </span><span class="nv">timestamp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">first </span>] [ <span class="nb">second </span><span class="m">32 </span><span class="nb">2^ / </span>] <span class="nb">bi + </span>seconds
</span></span><span class="line"><span class="cl"> <span class="m">1900 1 1 0 0 0 </span>instant <timestamp> time+ <span class="k">;
</span></span></span></code></pre></div><p>Now that we have that, parsing the NTP packet is fairly direct:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(ntp)</span> <span class="nf">( </span><span class="nv">payload</span> <span class="nf">-- </span><span class="nv">ntp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"CCCcIIIIIIIIIII"</span> unpack-be {
</span></span><span class="line"><span class="cl"> [ <span class="nb">first </span><span class="m">-6 </span><span class="nb">shift </span><span class="m">0x3 </span><span class="nb">bitand </span>] <span class="c">! leap</span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">first </span><span class="m">-3 </span><span class="nb">shift </span><span class="m">0x7 </span><span class="nb">bitand </span>] <span class="c">! version</span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">first </span><span class="m">0x7 </span><span class="nb">bitand </span>] <span class="c">! mode</span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">second </span>] <span class="c">! stratum</span>
</span></span><span class="line"><span class="cl"> [ <span class="nb">third </span>] <span class="c">! poll</span>
</span></span><span class="line"><span class="cl"> [ [ <span class="m">3 </span>] <span class="nb">dip nth </span>] <span class="c">! precision</span>
</span></span><span class="line"><span class="cl"> [ [ <span class="m">4 </span>] <span class="nb">dip nth </span><span class="m">16 </span><span class="nb">2^ / </span>] <span class="c">! root-delay</span>
</span></span><span class="line"><span class="cl"> [ [ <span class="m">5 </span>] <span class="nb">dip nth </span><span class="m">16 </span><span class="nb">2^ / </span>] <span class="c">! root-dispersion</span>
</span></span><span class="line"><span class="cl"> [ [ <span class="m">6 </span>] <span class="nb">dip nth </span>] <span class="c">! ref-id</span>
</span></span><span class="line"><span class="cl"> [ [ { <span class="m">7 8 </span>} ] <span class="nb">dip nths </span>(time) ] <span class="c">! ref-timestamp</span>
</span></span><span class="line"><span class="cl"> [ [ { <span class="m">9 10 </span>} ] <span class="nb">dip nths </span>(time) ] <span class="c">! orig-timestamp</span>
</span></span><span class="line"><span class="cl"> [ [ { <span class="m">11 12 </span>} ] <span class="nb">dip nths </span>(time) ] <span class="c">! recv-timestamp</span>
</span></span><span class="line"><span class="cl"> [ [ { <span class="m">13 14 </span>} ] <span class="nb">dip nths </span>(time) ] <span class="c">! tx-timestamp</span>
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span>ntp <span class="nb">boa </span><span class="k">;
</span></span></span></code></pre></div><p>The simplest client request is a 48-byte packet encoded with no
timestamp information, but only the mode of operation (e.g., 3 or
“client”):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">REQUEST</span> B{ <span class="m">0x1b 0 0 0 0 0 0 0
</span></span></span><span class="line"><span class="cl"> <span class="m">0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
</span></span></span><span class="line"><span class="cl"> <span class="m">0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
</span></span></span><span class="line"><span class="cl"> <span class="m">0 0 0 0 0 0 0 0 </span>}
</span></span></code></pre></div><p>We will use the
<a href="https://docs.factorcode.org/content/word-__lt__datagram__gt__,io.sockets.html"><datagram></a>
word from the
<a href="https://docs.factorcode.org/content/vocab-io.sockets.html">io.sockets</a>
vocabulary to send and receive UDP packets. Note that the
<a href="https://docs.factorcode.org/content/word-resolve-host,io.sockets.html">resolve-host</a>
word is used to lookup the IP addresses for domain names, and picks one
to use at random.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><ntp></span> <span class="nf">( </span><span class="nv">host</span> <span class="nf">-- </span><span class="nv">ntp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">123 </span><inet> resolve-host [ inet4? ] <span class="nb">filter </span>random
</span></span><span class="line"><span class="cl"> <span class="no">f </span><span class="m">0 </span><inet4> <datagram> [
</span></span><span class="line"><span class="cl"> [ REQUEST ] <span class="nb">2dip </span>[ send ] [ receive <span class="nb">drop </span>] <span class="nb">bi </span>(ntp)
</span></span><span class="line"><span class="cl"> ] with-disposal <span class="k">;
</span></span></span></code></pre></div><p>We can make some convenience words for well-known hosts:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">default-ntp</span> <span class="nf">( -- </span><span class="nv">ntp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"pool.ntp.org"</span> <ntp> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">local-ntp</span> <span class="nf">( -- </span><span class="nv">ntp</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"localhost"</span> <ntp> <span class="k">;
</span></span></span></code></pre></div><p>Putting this all together, a NTP request/response can look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> default-ntp <span class="m">.
</span></span></span><span class="line"><span class="cl">T{ ntp
</span></span><span class="line"><span class="cl"> { leap <span class="m">0 </span>}
</span></span><span class="line"><span class="cl"> { version <span class="m">3 </span>}
</span></span><span class="line"><span class="cl"> { mode <span class="m">4 </span>}
</span></span><span class="line"><span class="cl"> { stratum <span class="m">3 </span>}
</span></span><span class="line"><span class="cl"> { poll <span class="m">0 </span>}
</span></span><span class="line"><span class="cl"> { precision <span class="m">-8 </span>}
</span></span><span class="line"><span class="cl"> { root-delay 6453/65536 }
</span></span><span class="line"><span class="cl"> { root-dispersion 4379/65536 }
</span></span><span class="line"><span class="cl"> { ref-id <span class="m">2903084642 </span>}
</span></span><span class="line"><span class="cl"> { ref-timestamp
</span></span><span class="line"><span class="cl"> T{ timestamp
</span></span><span class="line"><span class="cl"> { year <span class="m">2010 </span>}
</span></span><span class="line"><span class="cl"> { month <span class="m">4 </span>}
</span></span><span class="line"><span class="cl"> { day <span class="m">30 </span>}
</span></span><span class="line"><span class="cl"> { minute <span class="m">25 </span>}
</span></span><span class="line"><span class="cl"> { <span class="nb">second </span><span class="m">55+819995115/2147483648 </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> { orig-timestamp
</span></span><span class="line"><span class="cl"> T{ timestamp { year <span class="m">1900 </span>} { month <span class="m">1 </span>} { day <span class="m">1 </span>} }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> { recv-timestamp
</span></span><span class="line"><span class="cl"> T{ timestamp
</span></span><span class="line"><span class="cl"> { year <span class="m">2010 </span>}
</span></span><span class="line"><span class="cl"> { month <span class="m">4 </span>}
</span></span><span class="line"><span class="cl"> { day <span class="m">30 </span>}
</span></span><span class="line"><span class="cl"> { minute <span class="m">36 </span>}
</span></span><span class="line"><span class="cl"> { <span class="nb">second </span><span class="m">19+729484793/4294967296 </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> { tx-timestamp
</span></span><span class="line"><span class="cl"> T{ timestamp
</span></span><span class="line"><span class="cl"> { year <span class="m">2010 </span>}
</span></span><span class="line"><span class="cl"> { month <span class="m">4 </span>}
</span></span><span class="line"><span class="cl"> { day <span class="m">30 </span>}
</span></span><span class="line"><span class="cl"> { minute <span class="m">36 </span>}
</span></span><span class="line"><span class="cl"> { <span class="nb">second </span><span class="m">19+732746657/4294967296 </span>}
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>This could be improved by parsing some of the fields as “human-readable”
(like the leap indicator, mode, stratum, and reference id) and adding
support for handling unresponsive servers or dropped packets. From here,
one could learn the clock synchronization algorithms, or even use this
as the basis to begin implementing a NTP server.</p>
What time is it? Part 2
https://re.factorcode.org/2010/04/what-time-is-it-part-2.html
Mon, 26 Apr 2010 11:57:00 -0700https://re.factorcode.org/2010/04/what-time-is-it-part-2.html<p>In <a href="https://re.factorcode.org/2010/04/what-time-is-it-part-1.html">Part
1</a>,
we implemented a server using the human-readable DAYTIME protocol.</p>
<p>One of the drawbacks of human-readable time is the challenge of parsing
the text into an object that can be manipulated by computers. This is
made harder when there is no clear specification for the format of the
text (some conventions like <a href="https://tools.ietf.org/html/rfc822">RFC 822</a>
exist, but the DAYTIME protocol leaves this detail unspecified).</p>
<p>Also in 1983, the TIME protocol (specified in <a href="https://tools.ietf.org/html/rfc868">RFC
868</a>) was developed. It was intended
to be used by machines (rather than humans) to retrieve the current
time. As with the DAYTIME protocol, it supports both TCP and UDP. Below,
we will implement a TIME server using UDP in
<a href="https://www.factorcode.org">Factor</a>.</p>
<p>These are the vocabularies that we will be using:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">arrays</span> <span class="nn">destructors</span> <span class="nn">io.sockets</span> <span class="nn">kernel</span> <span class="nn">math</span> <span class="nn">pack</span> <span class="nn">system</span> <span class="k">;
</span></span></span></code></pre></div><p>The timestamp representation used as a payload is a 32-bit binary number
of seconds since January 1, 1900. Most computers now use a 64-bit number
of milliseconds since January 1, 1970 (referred to as the “epoch”) to
store the current time. Factor allows access to the current time as a
number of microseconds since the epoch using the
<a href="https://docs.factorcode.org/content/word-system-micros,system.html">system-micros</a>
word.</p>
<p>We need to first create a word that will return the number of seconds
since 1900. There were 2,208,988,800 seconds between January 1, 1900 and
January 1, 1970. We will use that to adjust the current time:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">TIME1970</span> <span class="m">2208988800
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">seconds-since-1900</span> <span class="nf">( -- </span><span class="nv">n</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> system-micros <span class="m">1000000 </span><span class="nb">/ >fixnum </span>TIME1970 <span class="nb">+ </span><span class="k">;
</span></span></span></code></pre></div><p>Jumping ahead slightly, we will create a word that receives a packet
from a client (usually empty) and then sends the current time as a
32-bit binary number of seconds since 1900 back to the client. The
<a href="https://docs.factorcode.org/content/vocab-io.sockets.html">io.sockets</a>
vocabulary provides some words for creating
<a href="https://docs.factorcode.org/content/word-__lt__datagram__gt__,io.sockets.html">datagram</a>
servers (for UDP).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">serve-time</span> <span class="nf">( </span><span class="nv">datagram</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ receive <span class="nb">nip </span>] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> [ seconds-since-1900 <span class="nb">1array </span><span class="s">"i"</span> pack ] <span class="nb">2dip
</span></span></span><span class="line"><span class="cl"> send <span class="k">;
</span></span></span></code></pre></div><p>According to the specification, the TIME server should run on port 37,
but that typically requires administrative privileges, so we will run it
on port 3700 for ease of testing. It is also best practices to bind to a
specific network interface but, for now, we will listen for packets from
any interface.</p>
<p>By running this code in the listener, we can test a datagram port that
handles a single packet:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="no">f </span><span class="m">3700 </span><inet4> <datagram> [
</span></span><span class="line"><span class="cl"> serve-time
</span></span><span class="line"><span class="cl"> ] with-disposal
</span></span></code></pre></div><p>And then using <a href="https://netcat.sourceforge.net/">netcat</a>, we can send an
empty UDP packet to the server, which should respond with the current
time in binary (which is why it is hard to read):</p>
<pre tabindex="0"><code>$ nc -u 127.0.0.1 3700
?X?
</code></pre><p>Putting this all together, we can create a word that will loop forever,
serving the current time in this manner:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">time-server</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span><span class="m">3700 </span><inet4> <datagram> [
</span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>serve-time <span class="no">t </span>] <span class="nb">loop drop
</span></span></span><span class="line"><span class="cl"> ] with-disposal <span class="k">;
</span></span></span></code></pre></div><p>The resolution of this protocol is low (since it is number of seconds),
limiting the use-cases it is capable of supporting. Over the years, much
better solutions have become standardized.</p>
What time is it? Part 1
https://re.factorcode.org/2010/04/what-time-is-it-part-1.html
Wed, 14 Apr 2010 18:27:00 -0700https://re.factorcode.org/2010/04/what-time-is-it-part-1.html<p>Time is a harder-than-it-looks concept in most computing systems.</p>
<p>Every machine has a clock, every clock has a time, and every time has a
near infinite number of representations. The challenges include mastery
of such topics as timezones, calendars, encodings, localizations,
offsets, precision, drift, geographic latencies, atomic clocks, even GPS
synchronization.</p>
<p>Back in the early 1980’s, one of the tasks for building computer
networks involved designing protocols by which applications could
determine the current time. Over the years, these protocols have grown
in features and complexity.</p>
<p>But back in 1983, in <a href="https://www.faqs.org/rfcs/rfc867.html">RFC 867</a>,
the IETF standardized a very simple human-readable time protocol called
DAYTIME to solve the basic question: “what time is it?”. The
specification supports both TCP and UDP implementations, but we will
implement just the TCP portion in <a href="https://www.factorcode.org">Factor</a>.</p>
<p>First, we need some vocabularies:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">calendar</span> <span class="nn">formatting</span> <span class="nn">kernel</span> <span class="nn">io</span>
</span></span><span class="line"><span class="cl"><span class="nn">io.encodings.ascii</span> <span class="nn">io.servers.connection</span> <span class="k">;
</span></span></span></code></pre></div><p>Using the
<a href="https://docs.factorcode.org/content/vocab-formatting.html">formatting</a>
vocabulary that I wrote, printing the current time is easy with
<a href="https://docs.factorcode.org/content/word-strftime,formatting.html">strftime</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> now <span class="s">"%c"</span> strftime <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"Wed Apr 14 18:13:51 2010"</span>
</span></span></code></pre></div><p>The
<a href="https://docs.factorcode.org/content/word-threaded-server,io.servers.connection.html">threaded-server</a>
type can then be used to accept connections from clients and output the
current time before closing the connection:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><daytime-server></span> <span class="nf">( </span><span class="nv">port</span> <span class="nf">-- </span><span class="nv">server</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> ascii <threaded-server>
</span></span><span class="line"><span class="cl"> <span class="nb">swap </span>>>insecure
</span></span><span class="line"><span class="cl"> <span class="s">"daytime.server"</span> >>name
</span></span><span class="line"><span class="cl"> [ now <span class="s">"%c"</span> strftime <span class="nb">print flush </span>] >>handler <span class="k">;
</span></span></span></code></pre></div><p>According to the specification, the DAYTIME server should run on port
13, but that typically requires administrative privileges, so we will
run it on port 1300 for this test.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="m">1300 </span><daytime-server> start-server
</span></span></code></pre></div><p>And to test it, using <a href="https://netcat.sourceforge.net/">netcat</a>:</p>
<pre tabindex="0"><code>$ nc localhost 1300
Wed Apr 14 17:17:53 2010
$ nc localhost 1300
Wed Apr 14 17:17:53 2010
$ nc localhost 1300
Wed Apr 14 17:17:54 2010
$ nc localhost 1300
Wed Apr 14 17:17:54 2010
$ nc localhost 1300
Wed Apr 14 17:17:55 2010
</code></pre><p>Characteristic of some of the early, and quite rapid, development on the
internet, the specification is a little loose as to what format the time
should be represented in. Other protocols developed later will be much
more specific about the form and character of the time representations
they contain.</p>
Connecting to Memcached
https://re.factorcode.org/2010/04/connecting-to-memcached.html
Sat, 03 Apr 2010 22:12:00 -0700https://re.factorcode.org/2010/04/connecting-to-memcached.html<p>The <a href="https://memcached.org/">Memcached</a> project provides a “distributed
memory object caching system”, allowing quick storage and lookup
functionality for key-value pairs. It is both free and highly useful and
a necessary component to scaling most high traffic websites.</p>
<p>Libraries are available in <a href="https://code.google.com/p/memcached/wiki/Clients">many
languages</a>, but not
<a href="https://www.factorcode.org">Factor</a>. I thought it would be fun to fix
that and document some of the steps.</p>
<p>The first step is to understand what protocols are supported (in this
case both
<a href="https://github.com/memcached/memcached/blob/master/doc/protocol.txt">text</a>
and
<a href="https://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol">binary</a>)
and over what mediums (either UDP or TCP). An example of the text
protocol looks like:</p>
<pre tabindex="0"><code>$ telnet localhost 11211
Trying ::1...
Connected to localhost.
Escape character is '^]'.
set foo 0 60 3
bar
STORED
get foo
VALUE foo 0 3
bar
quit
Connection closed by foreign host.
</code></pre><p>We will start by implementing the binary protocol (which is more
efficient than the text one) over TCP.</p>
<p>First, we will list the needed vocabularies and begin a namespace for
the client implementation.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">accessors</span> <span class="nn">arrays</span> <span class="nn">assocs</span> <span class="nn">byte-arrays</span> <span class="nn">combinators</span> <span class="nn">fry</span>
</span></span><span class="line"><span class="cl"><span class="nn">io</span> <span class="nn">io.encodings.binary</span> <span class="nn">io.sockets</span> <span class="nn">kernel</span> <span class="nn">make</span> <span class="nn">math</span> <span class="nn">math.parser</span>
</span></span><span class="line"><span class="cl"><span class="nn">namespaces</span> <span class="nn">pack</span> <span class="nn">random</span> <span class="nn">sequences</span> <span class="nn">strings</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">memcached</span>
</span></span></code></pre></div><p>The
<a href="https://docs.factorcode.org/content/vocab-io.sockets.html">io.sockets</a>
vocabulary provides some nice words for doing simple networking. One of
those words is
<a href="https://docs.factorcode.org/content/word-with-client,io.sockets.html">with-client</a>,
which connects to a specified address and then runs a quotation with the
input and output streams configured to read and write to the connected
socket. We will use this to provide a structure for making Memcached
requests:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">SYMBOL:</span> <span class="nf">memcached-server</span>
</span></span><span class="line"><span class="cl"><span class="s">"127.0.0.1"</span> <span class="m">11211 </span><inet> memcached-server <span class="nb">set-global
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">with-memcached</span> <span class="nf">( </span><span class="nv">quot</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> memcached-server <span class="nb">get-global
</span></span></span><span class="line"><span class="cl"> binary [ <span class="nb">call </span>] with-client <span class="k">; inline
</span></span></span></code></pre></div><p>Requests are made to the Memcached server by specifying a command type,
and occasionally providing other fields:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">request</span> <span class="nv">cmd</span> <span class="nv">key</span> <span class="nv">val</span> <span class="nv">extra</span> <span class="nv">opaque</span> <span class="nv">cas</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><request></span> <span class="nf">( </span><span class="nv">cmd</span> <span class="nf">-- </span><span class="nv">request</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">""</span> <span class="s">""</span> <span class="s">""</span> random-32 <span class="m">0 </span><span class="no">\ request</span> <span class="nb">boa </span><span class="k">;
</span></span></span></code></pre></div><p>The header to every request is 24 bytes, and includes a magic byte, the
command, the key length, the extra header length, the total body length
including the value, an opaque value, and a field used for CAS (compare
and swap):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">send-header</span> <span class="nf">( </span><span class="nv">request</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ cmd>> ]
</span></span><span class="line"><span class="cl"> [ key>> <span class="nb">length </span>]
</span></span><span class="line"><span class="cl"> [ extra>> <span class="nb">length </span>]
</span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ key>> <span class="nb">length </span>]
</span></span><span class="line"><span class="cl"> [ extra>> <span class="nb">length </span>]
</span></span><span class="line"><span class="cl"> [ val>> <span class="nb">length </span>] <span class="nb">tri + +
</span></span></span><span class="line"><span class="cl"> ]
</span></span><span class="line"><span class="cl"> [ opaque>> ]
</span></span><span class="line"><span class="cl"> [ cas>> ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave
</span></span></span><span class="line"><span class="cl"> '[ <span class="m">0x80 </span>_ _ _ <span class="m">0 0 </span>_ _ _ ] <span class="s">"CCSCCSIIQ"</span> pack-be <span class="nb">write </span><span class="k">;
</span></span></span></code></pre></div><p>Now that we have that, sending a request is straight-forward:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(send)</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ >byte-array <span class="nb">write </span>] <span class="nb">unless-empty </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">send-request</span> <span class="nf">( </span><span class="nv">request</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ send-header ]
</span></span><span class="line"><span class="cl"> [ extra>> (send) ]
</span></span><span class="line"><span class="cl"> [ key>> (send) ]
</span></span><span class="line"><span class="cl"> [ val>> (send) ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave flush </span><span class="k">;
</span></span></span></code></pre></div><p>The response packet from the server has a similar structure to the
request, so we can start by parsing the header:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-header</span> <span class="nf">( -- </span><span class="nv">header</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"CCSCCSIIQ"</span> [ packed-length <span class="nb">read </span>] [ unpack-be ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>We can check the header for some error conditions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">check-magic</span> <span class="nf">( </span><span class="nv">header</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">first </span><span class="m">0x81 </span><span class="nb">= </span>[ <span class="s">"bad magic"</span> <span class="nb">throw </span>] <span class="nb">unless </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">check-status</span> <span class="nf">( </span><span class="nv">header</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <span class="m">5 </span>] <span class="nb">dip nth </span>{
</span></span><span class="line"><span class="cl"> { <span class="m">0x01 </span>[ <span class="s">"key not found"</span> <span class="nb">throw </span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x02 </span>[ <span class="s">"key exists"</span> <span class="nb">throw </span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x03 </span>[ <span class="s">"value too large"</span> <span class="nb">throw </span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x04 </span>[ <span class="s">"invalid arguments"</span> <span class="nb">throw </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x05 </span>[ <span class="s">"item not stored"</span> <span class="nb">throw </span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x06 </span>[ <span class="s">"value not numeric"</span> <span class="nb">throw </span>] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x81 </span>[ <span class="s">"unknown command"</span> <span class="nb">throw </span> ] }
</span></span><span class="line"><span class="cl"> { <span class="m">0x82 </span>[ <span class="s">"out of memory"</span> <span class="nb">throw </span> ] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div><p>We need some words to optionally read the key and value if they are
present:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(read)</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">str</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">0 </span><span class="nb">> </span>[ <span class="nb">read >string </span>] [ <span class="nb">drop </span><span class="s">""</span> ] <span class="nb">if </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-key</span> <span class="nf">( </span><span class="nv">header</span> <span class="nf">-- </span><span class="nv">key</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="m">2 </span>] <span class="nb">dip nth </span>(read) <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-val</span> <span class="nf">( </span><span class="nv">header</span> <span class="nf">-- </span><span class="nv">val</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ <span class="m">6 </span>] <span class="nb">dip nth </span>] [ [ <span class="m">2 </span>] <span class="nb">dip nth </span>] <span class="nb">bi - </span>(read) <span class="k">;
</span></span></span></code></pre></div><p>And now we have everything we need to read the response:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">read-response</span> <span class="nf">( -- </span><span class="nv">val</span> <span class="nv">key</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> read-header {
</span></span><span class="line"><span class="cl"> [ check-magic ]
</span></span><span class="line"><span class="cl"> [ check-status ]
</span></span><span class="line"><span class="cl"> [ read-key ]
</span></span><span class="line"><span class="cl"> [ read-val ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave swap </span><span class="k">;
</span></span></span></code></pre></div><p>Believe it or not, but this is all we really need to implement the
various Memcached commands, including the two most basic: <code>GET</code> and
<code>SET</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">m/get</span> <span class="nf">( </span><span class="nv">key</span> <span class="nf">-- </span><span class="nv">val</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="m">0x00 </span><request> <span class="nb">swap </span>>>key
</span></span><span class="line"><span class="cl"> send-request read-response <span class="nb">drop </span><span class="m">4 </span><span class="nb">tail </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">m/set</span> <span class="nf">( </span><span class="nv">val</span> <span class="nv">key</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="m">0x01 </span><request> <span class="nb">swap </span>>>key <span class="nb">swap </span>>>val
</span></span><span class="line"><span class="cl"> { <span class="m">0 0 </span>} <span class="s">"II"</span> pack-be >>extra <span class="c">! flags exp</span>
</span></span><span class="line"><span class="cl"> send-request read-response <span class="nb">2drop </span><span class="k">;
</span></span></span></code></pre></div><p>Replicating the telnet session from the beginning of the article from
the <a href="https://www.factorcode.org">Factor</a> listener:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"bar"</span> <span class="s">"foo"</span> m/set ] with-memcached
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"foo"</span> m/get ] with-memcached <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"bar"</span>
</span></span></code></pre></div><p>This could be improved in several ways, for example: handling servers
that stop responding, keeping the network connections open over several
sets of requests (or use UDP), and allowing access to some of the more
advanced parts of the Memcached protocol (including flags and expiration
timers).</p>
<p>It is available on my
<a href="https://github.com/mrjbq7/re-factor/tree/master/memcached/">GitHub</a>, and
hopefully will be pulled into the main repository soon.</p>
How to Write a Spelling Corrector
https://re.factorcode.org/2010/03/how-to-write-a-spelling-corrector.html
Fri, 26 Mar 2010 21:22:00 -0700https://re.factorcode.org/2010/03/how-to-write-a-spelling-corrector.html<p>Several years ago, I came across an article about how to write a simple
<a href="https://norvig.com/spell-correct.html">spelling corrector</a> by <a href="https://norvig.com/">Peter
Norvig</a>. It occurred to me recently, that he was
missing an implementation in <a href="https://www.factorcode.org">Factor</a>, so I
thought to contribute one.</p>
<p><em>Note: I try to keep the structure of this implementation close to his
original Python, so that it can be more easily understood, and more
easily tested in parts.</em></p>
<p>The premise behind his spelling correction algorithm is that when a word
is mistyped, it is “close” to the actual word intended. For each
incorrect word, the algorithm looks for all the correctly spelled words
that can be made by one or two “fixes” (e.g., deleting an extra letter,
swapping two letters, inserting a missing letter, etc.). This gives you
the list of possible corrections.</p>
<p>Now, it wouldn’t be Peter Norvig if it didn’t have probabilities in it.
And sure enough, he introduces a very simple solution for the “most
likely correct word”, which is to pick from the list of possible
corrections, the word that is most commonly used in the given language.</p>
<p>We will first define a vocabulary to place our words, and list the other
vocabularies that ours will depend upon:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">ascii</span> <span class="nn">arrays</span> <span class="nn">assocs</span> <span class="nn">combinators</span> <span class="nn">fry</span> <span class="nn">kernel</span>
</span></span><span class="line"><span class="cl"><span class="nn">memoize</span> <span class="nn">make</span> <span class="nn">math</span> <span class="nn">math.order</span> <span class="nn">ranges</span> <span class="nn">io.encodings.ascii</span>
</span></span><span class="line"><span class="cl"><span class="nn">io.files</span> <span class="nn">sequences</span> <span class="nn">sorting</span> <span class="nn">splitting</span> <span class="nn">strings</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">spelling</span>
</span></span></code></pre></div><p>To begin our implementation, we need to generate a list of words and
frequencies of occurrence. He compiled a file called
<a href="https://norvig.com/big.txt">big.txt</a> that contains about a million words
from various public domain sources, which we will use also.</p>
<ol>
<li>Read the contents of the big.txt file.</li>
<li>Split the text into words, using lowercase for case-insensitivity.</li>
<li>Count the occurrences of words in the text.</li>
</ol>
<p>This is accomplished through the following words:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">words</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">words</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> >lower [ letter? <span class="nb">not </span>] split-when [ <span class="nb">empty? not </span>] <span class="nb">filter </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">train</span> <span class="nf">( </span><span class="nv">words</span> <span class="nf">-- </span><span class="nv">counts</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> H{ } <span class="nb">clone </span>[ '[ _ <span class="nb">inc-at </span>] <span class="nb">each </span>] <span class="nb">keep </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MEMO:</span> <span class="nf">NWORDS</span> <span class="nf">( -- </span><span class="nv">counts</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"vocab:spelling/big.txt"</span> ascii file-contents words train <span class="k">;
</span></span></span></code></pre></div><p>Next, we need a word to calculate the possible edits that are one step
removed by either:</p>
<ul>
<li>Deleting a necessary character.</li>
<li>Transposing two characters.</li>
<li>Replacing an incorrect character with a correct one.</li>
<li>Insering a missing character.</li>
</ul>
<p><em>Note: this is one place where Python’s use of list comprehensions seem
to create a much more concise and readable implementation.</em></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">alphabet</span> <span class="o">=</span> <span class="s1">'abcdefghijklmnopqrstuvwxyz'</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">edits1</span><span class="p">(</span><span class="n">word</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">splits</span> <span class="o">=</span> <span class="p">[(</span><span class="n">word</span><span class="p">[:</span><span class="n">i</span><span class="p">],</span> <span class="n">word</span><span class="p">[</span><span class="n">i</span><span class="p">:])</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">word</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="n">deletes</span> <span class="o">=</span> <span class="p">[</span><span class="n">a</span> <span class="o">+</span> <span class="n">b</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span> <span class="k">for</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">splits</span> <span class="k">if</span> <span class="n">b</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="n">transposes</span> <span class="o">=</span> <span class="p">[</span><span class="n">a</span> <span class="o">+</span> <span class="n">b</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">b</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">b</span><span class="p">[</span><span class="mi">2</span><span class="p">:]</span> <span class="k">for</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">splits</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">b</span><span class="p">)</span><span class="o">></span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="n">replaces</span> <span class="o">=</span> <span class="p">[</span><span class="n">a</span> <span class="o">+</span> <span class="n">c</span> <span class="o">+</span> <span class="n">b</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span> <span class="k">for</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">splits</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">alphabet</span> <span class="k">if</span> <span class="n">b</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="n">inserts</span> <span class="o">=</span> <span class="p">[</span><span class="n">a</span> <span class="o">+</span> <span class="n">c</span> <span class="o">+</span> <span class="n">b</span> <span class="k">for</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">splits</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">alphabet</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">set</span><span class="p">(</span><span class="n">deletes</span> <span class="o">+</span> <span class="n">transposes</span> <span class="o">+</span> <span class="n">replaces</span> <span class="o">+</span> <span class="n">inserts</span><span class="p">)</span>
</span></span></code></pre></div><p>One equivalent implementation in Factor works like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">CONSTANT:</span> <span class="nf">ALPHABET</span> <span class="s">"abcdefghijklmnopqrstuvwxyz"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">splits</span> <span class="nf">( </span><span class="nv">word</span> <span class="nf">-- </span><span class="nv">sequence</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> <span class="nb">dup length </span>[0,b] [ <span class="nb">dupd cut 2array </span>, ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] { } make <span class="nb">nip </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">deletes</span> <span class="nf">( </span><span class="nv">sequence</span> <span class="nf">-- </span><span class="nv">sequence'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> [ <span class="nb">first </span>] [ <span class="nb">second </span><span class="m">1 </span><span class="nb">short tail </span>] <span class="nb">bi append
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">transposes</span> <span class="nf">( </span><span class="nv">sequence</span> <span class="nf">-- </span><span class="nv">sequence'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">second length </span><span class="m">1 </span><span class="nb">> </span>] <span class="nb">filter </span>[
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ <span class="nb">first </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">second </span><span class="m">1 </span><span class="nb">swap ?nth </span>[ <span class="nb">1string </span>] [ <span class="s">""</span> ] <span class="nb">if* </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">second </span><span class="m">0 </span><span class="nb">swap ?nth </span>[ <span class="nb">1string </span>] [ <span class="s">""</span> ] <span class="nb">if* </span>]
</span></span><span class="line"><span class="cl"> [ <span class="nb">second </span><span class="m">2 </span><span class="nb">short tail </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave append append append
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">replaces</span> <span class="nf">( </span><span class="nv">sequence</span> <span class="nf">-- </span><span class="nv">sequence'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">second length </span><span class="m">0 </span><span class="nb">> </span>] <span class="nb">filter </span>[
</span></span><span class="line"><span class="cl"> <span class="nb">first2 </span>'[ <span class="nb">1string </span>_ <span class="nb">swap </span>_ <span class="m">1 </span><span class="nb">short tail 3append </span>]
</span></span><span class="line"><span class="cl"> ALPHABET <span class="nb">swap </span>{ } <span class="nb">map-as
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map concat </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">inserts</span> <span class="nf">( </span><span class="nv">sequence</span> <span class="nf">-- </span><span class="nv">sequence'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [
</span></span><span class="line"><span class="cl"> '[ <span class="nb">1string </span>_ <span class="nb">swap join </span>] ALPHABET <span class="nb">swap </span>{ } <span class="nb">map-as
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">map concat </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">edits1</span> <span class="nf">( </span><span class="nv">word</span> <span class="nf">-- </span><span class="nv">edits</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> splits {
</span></span><span class="line"><span class="cl"> [ deletes ]
</span></span><span class="line"><span class="cl"> [ transposes ]
</span></span><span class="line"><span class="cl"> [ replaces ]
</span></span><span class="line"><span class="cl"> [ inserts ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave append append append </span><span class="k">;
</span></span></span></code></pre></div><p>We can then make a word to calculate strings that are of two steps
removed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">edits2</span> <span class="nf">( </span><span class="nv">word</span> <span class="nf">-- </span><span class="nv">edits</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> edits1 [ edits1 ] <span class="nb">map concat </span><span class="k">;
</span></span></span></code></pre></div><p>And another word for filtering a list of words to only those that are
known:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">known-words</span> <span class="nf">( </span><span class="nv">words</span> <span class="nf">-- </span><span class="nv">words'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> NWORDS '[ _ <span class="nb">at </span>] <span class="nb">filter </span><span class="k">; inline
</span></span></span></code></pre></div><p>Putting all of this together, we can calculate a list of possible word
corrections of the fewest steps away, sorted by the frequency of
occurrence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">corrections</span> <span class="nf">( </span><span class="nv">word</span> <span class="nf">-- </span><span class="nv">words</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup 1array </span>known-words
</span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>edits1 known-words ] <span class="nb">when-empty
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>edits2 known-words ] <span class="nb">when-empty
</span></span></span><span class="line"><span class="cl"> [ [ NWORDS <span class="nb">at </span><span class="m">1 </span><span class="nb">or </span>] <span class="nb">bi@ </span>>=< ] sort <span class="nb">nip </span><span class="k">;
</span></span></span></code></pre></div><p>And then a word to choose the most likely correction, or <code>f</code> if no
corrections are available:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">correct</span> <span class="nf">( </span><span class="nv">word</span> <span class="nf">-- </span><span class="nv">word'/f</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> corrections <span class="nb">dup length </span><span class="m">0 </span><span class="nb">> </span>[ <span class="nb">first </span>] [ <span class="nb">drop </span><span class="no">f </span>] <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>Using it is pretty straightforward:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"speling"</span> correct <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="s">"spelling"</span>
</span></span></code></pre></div><p>The whole solution comes in at 58 lines of code, although could probably
be reduced in various ways.</p>
<p>There are always ways to improve algorithms, and this is no exception.
Peter Norvig talks about some of those including a larger set of
training text, using sentence context to choose from the possible list
of word corrections, and focusing on performance.</p>
Working with CGI: Part 4
https://re.factorcode.org/2010/03/working-with-cgi-part-4.html
Mon, 08 Mar 2010 15:15:00 -0800https://re.factorcode.org/2010/03/working-with-cgi-part-4.html<p>In parts
<a href="https://re.factorcode.org/2010/01/working-with-cgi-part-1.html">1</a>,
<a href="https://re.factorcode.org/2010/02/working-with-cgi-part-2.html">2</a>,
and
<a href="https://re.factorcode.org/2010/02/working-with-cgi-part-3.html">3</a>,
we learned about parsing CGI requests using
<a href="https://www.factorcode.org/">Factor</a>.</p>
<p>I wanted to take a moment and talk about using templates to develop web
sites. Many web developers will suggest that using
<a href="https://en.wikipedia.org/wiki/Web_template_system">templates</a> is a good
way to separate presentation and content, making it easier for teams to
work together to build a website, as well as other benefits.</p>
<p>There is no magic to most template systems. Fundamentally, these systems
take some text (usually in a hybrid “template language”), compile it
into code, using the compiled template to output a dynamic page.</p>
<p>In Factor, the
<a href="https://docs.factorcode.org/content/vocab-html.templates.html">html.templates</a>
vocabulary allows access to several HTML template implementations
including
<a href="https://docs.factorcode.org/content/vocab-html.templates.chloe.html">Chloe</a>
and
<a href="https://docs.factorcode.org/content/vocab-html.templates.fhtml.html">FHTML</a>.
Some differences between the two include:</p>
<ul>
<li>requiring XML (Chloe) or HTML (FHTML)</li>
<li>embedding data (Chloe) or Factor code (FHTML)</li>
</ul>
<p>Depending on your use-case, you might have a preference for one versus
the other. If neither suite your purpose, you could even implement your
own with a modest amount of work.</p>
<p>Embedding a template into a Factor CGI script is pretty easy. Here is an
example of using the
<a href="https://docs.factorcode.org/content/vocab-html.templates.fhtml.html">FHTML</a>
vocabulary to parse a template and then call it to render a page.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="cp">#! /path/to/factor</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">USE:</span> <span class="nn">io</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="s">"Content-type: text/html\n\n"</span> <span class="nb">print
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">USE:</span> <span class="nn">html.templates.fhtml</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="s">"""
</span></span></span><span class="line"><span class="cl"><span class="s"><% USING: calendar formatting math math.parser io ; %>
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s"><html>
</span></span></span><span class="line"><span class="cl"><span class="s"> <head><title>Simple Embedded Factor Example</title></head>
</span></span></span><span class="line"><span class="cl"><span class="s"> <body>
</span></span></span><span class="line"><span class="cl"><span class="s"> The time is <% now "%c" strftime write %>
</span></span></span><span class="line"><span class="cl"><span class="s"> <br>
</span></span></span><span class="line"><span class="cl"><span class="s"> <% 5 [ %><p>I like repetition</p><% ] times %>
</span></span></span><span class="line"><span class="cl"><span class="s"> </body>
</span></span></span><span class="line"><span class="cl"><span class="s"></html>
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">"""</span> parse-template call( -- )
</span></span></code></pre></div><p>When run from Factor, or accessed as a CGI script, this will print a
page something like the following:</p>
<pre tabindex="0"><code>The time is Mon Mar 08 23:12:47 2010.
I like repetition
I like repetition
I like repetition
I like repetition
I like repetition
</code></pre><p>Many large web applications are built with some kind of template engine,
frequently using smart caching of compiled templates for performance,
and usually storing the template files separately from the CGI script
that loads and calls them.</p>
<p>But, it all basically starts with this.</p>
Working with CGI: Part 3
https://re.factorcode.org/2010/02/working-with-cgi-part-3.html
Wed, 03 Feb 2010 21:00:00 -0800https://re.factorcode.org/2010/02/working-with-cgi-part-3.html<p>In <a href="https://re.factorcode.org/2010/02/working-with-cgi-part-2.html">Part
2</a>,
we implemented simple parsing of <code>QUERY_STRING</code> and handling of the
<code>GET</code> request method.</p>
<p>In getting to the conclusion, I skipped describing an important
convention of HTTP application development. Specifically, that <code>GET</code>
requests should be idempotent. Because of this, as well as privacy
concerns, it is frequently common practice to submit HTML forms with a
<code>POST</code> request.</p>
<p>According to the <code>POST</code> convention, the request data is placed in the
message body and usually “URL-encoded” (as described in <a href="https://www.ietf.org/rfc/rfc2396.txt">RFC
2396</a>). This is similar to how
certain characters are “escaped” when included in a URL (for example,
spaces are represented by <code>%20</code>).</p>
<p>To properly parse these types of <code>POST</code> requests, we will need to parse
a few other environment variables that are provided to the CGI script.
First, we need a way to parse “content types”. As described in <a href="https://www.ietf.org/rfc/rfc2616.txt">RFC
2616</a> (the specification for
HTTP/1.1), this represents a mime-type and optional parameters.</p>
<p>For example, a server can specify that the response type will be HTML
inside of a UTF-8 character encoding by including the following in the
HTTP response headers:</p>
<pre tabindex="0"><code>Content-Type: text/html; charset=utf-8
</code></pre><p>To parse this, we can simple separate the mime-type and parse the
parameters:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(content-type)</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">params</span> <span class="nv">media/type</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">";"</span> split <span class="nb">unclip </span>[
</span></span><span class="line"><span class="cl"> [ H{ } <span class="nb">clone </span>] [ <span class="nb">first </span>(query-string) ] <span class="nb">if-empty
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">dip </span><span class="k">;
</span></span></span></code></pre></div><p>When we submit a form, the values are included in the body and provided
to the CGI script using a mime-type of
“application/x-www-form-urlencoded”. The contents are provided by
parameters encoded in the message body. (Technically, some of the
parameters could also be specified in the URL).</p>
<p>We can define a function that will parse the <code>CONTENT_LENGTH</code>, read the
specified number of bytes from the stream, and then assemble and parse
the URL-encoded query string:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(urlencoded)</span> <span class="nf">( -- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"CONTENT_LENGTH"</span> os-env <span class="s">"0"</span> <span class="nb">or </span>string>number
</span></span><span class="line"><span class="cl"> <span class="nb">read </span>[ <span class="s">""</span> ] [ <span class="s">"&"</span> <span class="nb">append </span>] <span class="nb">if-empty
</span></span></span><span class="line"><span class="cl"> <span class="s">"QUERY_STRING"</span> os-env [ <span class="nb">append </span>] <span class="nb">when* </span>(query-string) <span class="k">;
</span></span></span></code></pre></div><p>These two words are sufficient to parse <code>POST</code> requests. However, it’s
worth noting that some forms can be submitted with a mime-type of
“multipart/form-data”, which is used for uploading files to servers. We
will put a placeholder word that can remind us to come back to this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(multipart)</span> <span class="nf">( -- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"multipart unsupported"</span> <span class="nb">throw </span><span class="k">;
</span></span></span></code></pre></div><p>Now that we have that, we can write the implement the parsing routine:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-post</span> <span class="nf">( -- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"CONTENT_TYPE"</span> os-env <span class="s">""</span> <span class="nb">or </span>(content-type) {
</span></span><span class="line"><span class="cl"> { <span class="s">"multipart/form-data"</span> [ (multipart) ] }
</span></span><span class="line"><span class="cl"> { <span class="s">"application/x-www-form-urlencoded"</span> [ (urlencoded) ] }
</span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>parse-get ]
</span></span><span class="line"><span class="cl"> } <span class="nb">case nip </span><span class="k">;
</span></span></span></code></pre></div><p>And then extend our <code><cgi-form></code> word to handle <code>POST</code> requests:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><cgi-form></span> <span class="nf">( -- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"REQUEST_METHOD"</span> os-env <span class="s">"GET"</span> <span class="nb">or </span>>upper {
</span></span><span class="line"><span class="cl"> { <span class="s">"GET"</span> [ parse-get ] }
</span></span><span class="line"><span class="cl"> { <span class="s">"POST"</span> [ parse-post ] }
</span></span><span class="line"><span class="cl"> [ <span class="s">"Unknown request method"</span> <span class="nb">throw </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div><p>And simple as that, we can now change our form method from “get” to
“post”, and our CGI scripts will continue to work.</p>
Working with CGI: Part 2
https://re.factorcode.org/2010/02/working-with-cgi-part-2.html
Tue, 02 Feb 2010 19:36:00 -0800https://re.factorcode.org/2010/02/working-with-cgi-part-2.html<p>In <a href="https://re.factorcode.org/2010/01/working-with-cgi-part-1.html">Part
1</a>,
we created a simple debugging script to print out the environment the
CGI script is being executed within.</p>
<p>Many CGI scripts are simple “form handlers”. These scripts take input
via an HTML form and generate a dynamic response. We are going to write
a CGI script that will be able to parse input from an HTML form
submission.</p>
<p>The most common type of HTTP request is the <code>GET</code> method. The web
browser sends a <code>URL</code> to the server and requests the server “get” the
contents of the URL and send it back. When HTML forms are submitted
using the <code>GET</code> method, the form elements are “URL encoded” and passed
to the server as the “query string” part of the URL.</p>
<p>For example, if I had a “calculator” application to add two numbers
(e.g., “x+y”), you could imagine getting the result of <code>2+3</code> by calling:</p>
<pre tabindex="0"><code>https://server/add?x=2&y=3
</code></pre><p>We need a word that will parse the <code>QUERY_STRING</code> and return a map of
submitted parameters. Luckily, <a href="https://www.factorcode.org">Factor</a> has
such a word in the
<a href="https://docs.factorcode.org/content/vocab-urls.encoding.html">urls.encoding</a>
vocabulary:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"x=2&y=3"</span> query>assoc <span class="m">.
</span></span></span><span class="line"><span class="cl">H{ { <span class="s">"x"</span> <span class="s">"2"</span> } { <span class="s">"y"</span> <span class="s">"3"</span> } }
</span></span></code></pre></div><p>For our use case, the
<a href="https://docs.factorcode.org/content/word-query__gt__assoc,urls.encoding.html">query>assoc</a>
word isn’t quite what we need. For one thing, it handles empty strings
in an odd way:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">""</span> query>assoc <span class="m">.
</span></span></span><span class="line"><span class="cl">H{ { <span class="s">""</span> <span class="no">f </span>} }
</span></span></code></pre></div><p>Also, it doesn’t handle multiple inputs with the same name consistently
with single inputs. If a parameter is represented in the query string
multiple times, it will appear in the result as a list of values.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"a=2&a=3"</span> query>assoc <span class="m">.
</span></span></span><span class="line"><span class="cl">H{ { <span class="s">"a"</span> { <span class="s">"2"</span> <span class="s">"3"</span> } } }
</span></span></code></pre></div><p>So to “fix” this, we will develop a word that filters out <code>f</code> values,
and returns both single and multiple parameters as sequences.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(query-string)</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> query>assoc [ <span class="nb">nip </span>] <span class="nb">assoc-filter
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup string? </span>[ <span class="nb">1array </span>] <span class="nb">when </span>] <span class="nb">assoc-map </span><span class="k">;
</span></span></span></code></pre></div><p>Now that we have our building blocks, we can begin supporting the <code>GET</code>
request. Let’s start by designing the API. We want to parse the request
method, handle the <code>GET</code> method, and return the parameters submitted.
The <code>REQUEST_METHOD</code> and <code>QUERY_STRING</code> are available as environment
variables:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">parse-get</span> <span class="nf">( -- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"QUERY_STRING"</span> os-env <span class="s">""</span> <span class="nb">or </span>(query-string) <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><cgi-form></span> <span class="nf">( -- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"REQUEST_METHOD"</span> os-env <span class="s">"GET"</span> <span class="nb">or </span>>upper {
</span></span><span class="line"><span class="cl"> { <span class="s">"GET"</span> [ parse-get ] }
</span></span><span class="line"><span class="cl"> [ <span class="s">"Unknown request method"</span> <span class="nb">throw </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div><p>Since frequently we will only need to worry about the first parameter
value (ignoring subsequent values if present), we can make a simple
version that can be optionally used:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf"><cgi-simple-form></span> <span class="nf">( -- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <cgi-form> [ <span class="nb">first </span>] <span class="nb">assoc-map </span><span class="k">;
</span></span></span></code></pre></div><p>Putting all of this together, we can build something useful: a
<a href="https://re.factorcode.org/2009/06/brainfck.html">Brainfuck</a>
interpreter accessible from a web page!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">assocs</span> <span class="nn">brainfuck</span> <span class="nn">cgi</span> <span class="nn">formatting</span> <span class="nn">io</span> <span class="nn">kernel</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="s">"Content-type: text/html\n\n"</span> <span class="nb">write
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="s">"code"</span> <cgi-simple-form> <span class="nb">at
</span></span></span><span class="line"><span class="cl"><span class="s">"""
</span></span></span><span class="line"><span class="cl"><span class="s">++++++++++[>+++++++>++++++++++>+++>+<<<<-]
</span></span></span><span class="line"><span class="cl"><span class="s">>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.
</span></span></span><span class="line"><span class="cl"><span class="s">------.--------.>+.>.
</span></span></span><span class="line"><span class="cl"><span class="s">"""</span> <span class="nb">or dup </span>get-brainfuck
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="s">"""
</span></span></span><span class="line"><span class="cl"><span class="s"><html>
</span></span></span><span class="line"><span class="cl"><span class="s"><head><title>Brainfuck</title></head>
</span></span></span><span class="line"><span class="cl"><span class="s"><body>
</span></span></span><span class="line"><span class="cl"><span class="s"><form method='get'>
</span></span></span><span class="line"><span class="cl"><span class="s"><textarea id="text" name="code" cols="80" rows="15">
</span></span></span><span class="line"><span class="cl"><span class="s">%s
</span></span></span><span class="line"><span class="cl"><span class="s"></textarea><br>
</span></span></span><span class="line"><span class="cl"><span class="s"><input type="submit" value="Submit">
</span></span></span><span class="line"><span class="cl"><span class="s"><input type="reset" value="Reset">
</span></span></span><span class="line"><span class="cl"><span class="s"></form>
</span></span></span><span class="line"><span class="cl"><span class="s"><pre>%s</pre>
</span></span></span><span class="line"><span class="cl"><span class="s"></body>
</span></span></span><span class="line"><span class="cl"><span class="s"></html>
</span></span></span><span class="line"><span class="cl"><span class="s">"""</span> printf
</span></span></code></pre></div><p>If nothing is specified, this will happily calculate and then print
“Hello World!”, otherwise it will compute the result of the code
provided.</p>
Working with CGI: Part 1
https://re.factorcode.org/2010/01/working-with-cgi-part-1.html
Wed, 20 Jan 2010 20:24:00 -0800https://re.factorcode.org/2010/01/working-with-cgi-part-1.html<p>While <a href="https://www.factorcode.org">Factor</a> can be used to develop many
different kinds of programs, some uses just aren’t as common as others,
for many reasons.</p>
<p>One such case is its use to develop
<a href="https://en.wikipedia.org/wiki/Common_Gateway_Interface">CGI</a> scripts.
The “Common Gateway Interface” (sometimes called “CGI/1.1”) is the
documented version of conventions developed for web programming in the
late 1990’s. If you are curious, you can read <a href="https://www.ietf.org/rfc/rfc3875.txt">RFC
3875</a> for more details.</p>
<p>The way it works is simple. The web server:</p>
<ol>
<li>receives an HTTP request</li>
<li>parses the request headers and payload, and then</li>
<li>calls an application with the request details, which then</li>
<li>renders a response that is then sent back to the client</li>
</ol>
<p>When developing and testing CGI scripts, it is useful to understand the
environment that your program will be running within. For this, we can
build a simple Factor program that prints the environment variables it
is called with to HTML that can be rendered in a web browser.</p>
<p>Our CGI script should be executable, and on a UNIX system (like Mac OS
or Linux) should contain a
<a href="https://en.wikipedia.org/wiki/Shebang_(Unix)">shebang</a> which indicates
what program should process the files contents. This is used to call the
Factor interpreter with our CGI script. Note that the shebang has a
space after it, which is not required by most interpreters, but is by
Factor:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="cp">#! /path/to/factor</span>
</span></span></code></pre></div><p>The vocabularies that we will be using:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">assocs</span> <span class="nn">environment</span> <span class="nn">kernel</span> <span class="nn">io</span> <span class="nn">namespaces</span> <span class="nn">sequences</span>
</span></span><span class="line"><span class="cl"><span class="nn">sorting</span> <span class="k">;
</span></span></span></code></pre></div><p>The first response from a CGI script is typically the HTTP headers,
including the type of content that is being returned. Your script could
return any content, including images, audio, or video. But in this case,
we will just return plain HTML:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="s">"Content-type: text/html\n\n"</span> <span class="nb">print
</span></span></span></code></pre></div><p>We can then print the HTML header and begin the body:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="s">"""
</span></span></span><span class="line"><span class="cl"><span class="s"><html>
</span></span></span><span class="line"><span class="cl"><span class="s"><head>
</span></span></span><span class="line"><span class="cl"><span class="s"><title>Debug</title>
</span></span></span><span class="line"><span class="cl"><span class="s"></head>
</span></span></span><span class="line"><span class="cl"><span class="s"><body>
</span></span></span><span class="line"><span class="cl"><span class="s"><pre>
</span></span></span><span class="line"><span class="cl"><span class="s">"""</span> <span class="nb">print
</span></span></span></code></pre></div><p>Next, we will get all the environment variables available to our process
and print them, sorted alphabetically:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">os-envs <span class="nb">>alist </span>sort-keys [
</span></span><span class="line"><span class="cl"> [ <span class="s">"<b>"</span> <span class="nb">write first write </span><span class="s">"</b>"</span> <span class="nb">write </span>]
</span></span><span class="line"><span class="cl"> [ <span class="s">" = "</span> <span class="nb">write second write nl </span>] <span class="nb">bi
</span></span></span><span class="line"><span class="cl">] <span class="nb">each
</span></span></span></code></pre></div><p>And then finish the HTML document with closing tags:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="s">"""
</span></span></span><span class="line"><span class="cl"><span class="s"></pre>
</span></span></span><span class="line"><span class="cl"><span class="s"></body>
</span></span></span><span class="line"><span class="cl"><span class="s"></html>
</span></span></span><span class="line"><span class="cl"><span class="s">"""</span> <span class="nb">print
</span></span></span></code></pre></div><p>If you run this program from the shell, it will print your local user
environment. But, when run from a web server, it prints the CGI script’s
environment. According to the CGI specification, certain environment
variables are used to pass the HTTP request details to the CGI program.
Some of the commonly used ones include:</p>
<table>
<tbody>
<tr class="odd">
<td><code>DOCUMENT_ROOT</code></td>
<td>The root directory of your server</td>
</tr>
<tr class="even">
<td><code>HTTP_COOKIE</code></td>
<td>The visitor's cookie, if one is set</td>
</tr>
<tr class="odd">
<td><code>HTTP_HOST</code></td>
<td>The hostname of the page being attempted</td>
</tr>
<tr class="even">
<td><code>HTTP_REFERER</code></td>
<td>The URL of the page that called your program</td>
</tr>
<tr class="odd">
<td><code>HTTP_USER_AGENT</code></td>
<td>The browser type of the visitor</td>
</tr>
<tr class="even">
<td><code>HTTPS</code></td>
<td>"on" if the program is being called through a secure server</td>
</tr>
<tr class="odd">
<td><code>PATH</code></td>
<td>The system path your server is running under</td>
</tr>
<tr class="even">
<td><code>QUERY_STRING</code></td>
<td>The query string (see GET, below)</td>
</tr>
<tr class="odd">
<td><code>REMOTE_ADDR</code></td>
<td>The IP address of the visitor</td>
</tr>
<tr class="even">
<td><code>REMOTE_HOST</code></td>
<td>The hostname of the visitor (if your server has reverse-name-lookups on; otherwise this is the IP address again)</td>
</tr>
<tr class="odd">
<td><code>REMOTE_PORT</code></td>
<td>The port the visitor is connected to on the web server</td>
</tr>
<tr class="even">
<td><code>REMOTE_USER</code></td>
<td>The visitor's username (for .htaccess-protected pages)</td>
</tr>
<tr class="odd">
<td><code>REQUEST_METHOD</code></td>
<td>GET or POST</td>
</tr>
<tr class="even">
<td><code>REQUEST_URI</code></td>
<td>The interpreted pathname of the requested document or CGI (relative to the document root)</td>
</tr>
<tr class="odd">
<td><code>SCRIPT_FILENAME</code></td>
<td>The full pathname of the current CGI</td>
</tr>
<tr class="even">
<td><code>SCRIPT_NAME</code></td>
<td>The interpreted pathname of the current CGI (relative to the document root)</td>
</tr>
<tr class="odd">
<td><code>SERVER_ADMIN</code></td>
<td>The email address for your server's webmaster</td>
</tr>
<tr class="even">
<td><code>SERVER_NAME</code></td>
<td>Your server's fully qualified domain name</td>
</tr>
<tr class="odd">
<td><code>SERVER_PORT</code></td>
<td>The port number your server is listening on</td>
</tr>
<tr class="even">
<td><code>SERVER_SOFTWARE</code></td>
<td>The server software you're using (e.g. Apache)</td>
</tr>
</tbody>
</table>
<p>This is a useful fact for testing, since you can easily simulate the
request that the web server will be sending to your CGI script by
configuring the environment in the appropriate way. More to come on that
later…</p>
Counting Word Frequencies
https://re.factorcode.org/2009/12/counting-word-frequencies.html
Wed, 30 Dec 2009 11:50:00 -0800https://re.factorcode.org/2009/12/counting-word-frequencies.html<p>One of my favorite memes right now is for people to write small programs
in various languages to compare and contrast and learn from one another.</p>
<p>Recently, someone blogged an example of counting word frequencies in a
collection of
<a href="https://kdd.ics.uci.edu/databases/20newsgroups/20newsgroups.html">newsgroup</a>
articles. The initial implementation was written in <a href="https://blogs.sourceallies.com/2009/12/word-counts-example-in-ruby-and-scala/">Ruby and
Scala</a>,
with someone else implementing a
<a href="https://www.bestinclass.dk/index.php/2009/12/clojure-vs-ruby-scala-transient-newsgroups">Clojure</a>
solution. These solutions are compared for lines of code as well as time
to run.</p>
<p>The basic idea is to iterate over a bunch of files, where each
represents a newsgroup posting and is organized by newsgroup into
directories. Split each file into words, and then increment a count per
word found (comparing case-insensitively).</p>
<p>First, we need a test that is equivalent to the “<code>\w</code>” regular
expression. In <a href="https://asciidocs.com/">ASCII</a>, this is essentially
<code>a-zA-Z0-9_</code>. We use a <a href="https://docs.factorcode.org/content/article-combinators.short-circuit.html">short-circuit
combinator</a>
to break early if one of the tests succeeds.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">\w?</span> <span class="nf">( </span><span class="nv">ch</span> <span class="nf">-- </span><span class="nv">?</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> { [ Letter? ] [ digit? ] [ <span class="sc">CHAR: _ </span><span class="nb">= </span>] } 1|| <span class="k">; inline
</span></span></span></code></pre></div><p>We can then build a word to split a sequence of characters.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">split-words</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">seq'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ \w? <span class="nb">not </span>] split-when <span class="nb">harvest </span><span class="k">;
</span></span></span></code></pre></div><p>This now leaves the main task of counting and aggregating the word
counts. The Ruby and Scala examples do this sequentially, but the
Clojure example tries to do things in parallel. We are going to keep it
simple, and do things sequentially.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">count-words</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="no">f </span>recursive-directory-files H{ } <span class="nb">clone </span>[
</span></span><span class="line"><span class="cl"> '[
</span></span><span class="line"><span class="cl"> ascii file-contents >lower
</span></span><span class="line"><span class="cl"> split-words [ _ <span class="nb">inc-at </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">keep </span><span class="k">;
</span></span></span></code></pre></div><p>We need a word to generate the desired output (tab-delimited words and
counts).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">print-words</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">first2 </span><span class="s">"%s\t%d\n"</span> printf ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>And another word to do the actual writing of output to files.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">write-count</span> <span class="nf">( </span><span class="nv">assoc</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> <span class="nb">>alist </span>[
</span></span><span class="line"><span class="cl"> [ <span class="s">"/tmp/counts-decreasing-factor"</span> ascii ] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> '[ _ sort-values <span class="nb">reverse </span>print-words ] with-file-writer
</span></span><span class="line"><span class="cl"> ] [
</span></span><span class="line"><span class="cl"> [ <span class="s">"/tmp/counts-alphabetical-factor"</span> ascii ] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> '[ _ sort-keys print-words ] with-file-writer
</span></span><span class="line"><span class="cl"> ] <span class="nb">bi </span><span class="k">;
</span></span></span></code></pre></div><p>Unfortunately, performance isn’t quite what I was hoping. I tested this
on a 2.8 GHz MacBook Pro. Ruby (using 1.8.7) runs in roughly 41 seconds,
Factor runs in 20 seconds, and Python (using 2.6.1) runs in 13 seconds.</p>
<p>I was sort of hoping Factor would come in under the typical scripting
languages, and I’d love to get feedback on how to improve it.</p>
<p>For reference, the Python version that I wrote is:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">defaultdict</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">operator</span> <span class="kn">import</span> <span class="n">itemgetter</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">root</span> <span class="o">=</span> <span class="s1">'/tmp/20_newsgroups'</span>
</span></span><span class="line"><span class="cl"><span class="c1">#root = '/tmp/mini_newsgroups'</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">t0</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">counts</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">int</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">dirpath</span><span class="p">,</span> <span class="n">dirname</span><span class="p">,</span> <span class="n">filenames</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="n">root</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">filenames</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">dirpath</span><span class="p">,</span> <span class="n">filename</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">word</span> <span class="ow">in</span> <span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s1">'\w+'</span><span class="p">,</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()):</span>
</span></span><span class="line"><span class="cl"> <span class="n">counts</span><span class="p">[</span><span class="n">word</span><span class="o">.</span><span class="n">lower</span><span class="p">()]</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="s2">"Writing counts in decreasing order"</span>
</span></span><span class="line"><span class="cl"><span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'counts-decreasing-python'</span><span class="p">,</span> <span class="s1">'w'</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">counts</span><span class="o">.</span><span class="n">items</span><span class="p">(),</span> <span class="n">key</span><span class="o">=</span><span class="n">itemgetter</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span> <span class="o">>></span> <span class="n">f</span><span class="p">,</span> <span class="s1">'</span><span class="si">%s</span><span class="se">\t</span><span class="si">%d</span><span class="s1">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="s2">"Writing counts in decreasing order"</span>
</span></span><span class="line"><span class="cl"><span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'counts-alphabetical-python'</span><span class="p">,</span> <span class="s1">'w'</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">counts</span><span class="o">.</span><span class="n">items</span><span class="p">(),</span> <span class="n">key</span><span class="o">=</span><span class="n">itemgetter</span><span class="p">(</span><span class="mi">0</span><span class="p">)):</span>
</span></span><span class="line"><span class="cl"> <span class="nb">print</span> <span class="o">>></span> <span class="n">f</span><span class="p">,</span> <span class="s1">'</span><span class="si">%s</span><span class="se">\t</span><span class="si">%d</span><span class="s1">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="s1">'Finished in </span><span class="si">%s</span><span class="s1"> seconds'</span> <span class="o">%</span> <span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">t0</span><span class="p">)</span>
</span></span></code></pre></div>Recursively Listing Files
https://re.factorcode.org/2009/12/recursively-listing-files.html
Tue, 29 Dec 2009 20:38:00 -0800https://re.factorcode.org/2009/12/recursively-listing-files.html<p><em>Update: It was pointed out to me that the
<a href="https://docs.factorcode.org/content/word-recursive-directory-files%2Cio.directories.search.html"><code>recursive-directory-files</code></a>
word in <code>io.directories.search</code> solves this problem. Good to know, and
the below article can be thought of as a learning exercise! :)</em></p>
<p><a href="https://www.factorcode.org">Factor</a> has several vocabularies for
interacting with the filesystem, and quite a lot of work has been done
on making these useful. Some interesting blog posts from early 2009
include:</p>
<ul>
<li><a href="https://code-factor.blogspot.com/2009/01/files-and-file-systems-in-factor-part-1.html">Files and file-systems in Factor, part
1</a></li>
<li><a href="https://code-factor.blogspot.com/2009/01/files-and-file-systems-in-factor-part-2.html">Files and file-systems in Factor, part
2</a></li>
</ul>
<p>One feature that I haven’t found yet, is a word to simply (and
recursively) list files within a directory. This is often useful with
the intent of processing some (or all) of the files in some way.</p>
<p>This feature exists in other language standard libraries with different
names. In Python, this is called <code>os.walk()</code>. In Perl, this is called
<code>File::Find</code>. Usually, even if it doesn’t exist, it can be built
relatively easily.</p>
<p>I’m going to show you how to create a word to do that with Factor.</p>
<p>Many words within the standard library are implemented by a public word
that defers to a private word for part of the functionality. In this
case, I want to separate the “setup” functionality of the word, from the
“work” functionality.</p>
<p>Let’s define a word <code>list-files</code> that will recursively list all the
files within a directory. First, we need to create a growable sequence
(in this case a <code>vector</code>) to hold the result, and then we want to call a
word that will be used to do the actual work.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">list-files</span> <span class="nf">( </span><span class="nv">path</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ V{ } <span class="nb">clone </span>] <span class="nb">dip </span>(list-files) <span class="k">;
</span></span></span></code></pre></div><p>For each path that we process, we will want to handle differently
depending on what type of file the path points to:</p>
<ol>
<li>If a symbolic link, we want to read the link and recurse into it.</li>
<li>If a directory, we want to recurse into each of the files within it.</li>
<li>If a regular file, we want to add it to the list of files.</li>
</ol>
<p>Using some of the words in <code>io.directories</code>, <code>io.files</code>,
<code>io.files.info</code>, <code>io.files.links</code>, and <code>io.files.types</code>, we can build
such a function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">(list-files)</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nv">path</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> normalize-path <span class="nb">dup </span>link-info type>> {
</span></span><span class="line"><span class="cl"> { +symbolic-link+ [ read-link (list-files) ] }
</span></span><span class="line"><span class="cl"> { +directory+ [
</span></span><span class="line"><span class="cl"> [ directory-files ] <span class="nb">keep
</span></span></span><span class="line"><span class="cl"> '[ normalize-path _ <span class="nb">prepend </span>(list-files) ] <span class="nb">each </span>] }
</span></span><span class="line"><span class="cl"> { +regular-file+ [ <span class="nb">over push </span>] }
</span></span><span class="line"><span class="cl"> [ <span class="s">"unsupported"</span> <span class="nb">throw </span>]
</span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="k">;
</span></span></span></code></pre></div><p>We can setup a directory structure like so:</p>
<pre tabindex="0"><code>$ tree -f /tmp/foo
/tmp/foo
|-- /tmp/foo/bar
`-- /tmp/foo/baz
`-- /tmp/foo/baz/foo
1 directory, 2 files
</code></pre><p>And then use our new function from Factor:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/tmp/foo"</span> list-files <span class="m">.
</span></span></span><span class="line"><span class="cl">V{ <span class="s">"/tmp/foo/bar"</span> <span class="s">"/tmp/foo/baz/foo"</span> }
</span></span></code></pre></div><p>This could be improved further by handling file permissions issues,
infinite recursion, and lazily generating the list of files (for better
performance with large directory trees).</p>
Generating Text in Factor
https://re.factorcode.org/2009/12/generating-text-in-factor.html
Sun, 27 Dec 2009 08:25:00 -0800https://re.factorcode.org/2009/12/generating-text-in-factor.html<p>Some months back, I came across a few blog posts about generating text
using algorithms. A simple algorithm was implemented in
<a href="https://www.fatvat.co.uk/2009/01/generating-text.html">Clojure</a> and
<a href="https://www.fatvat.co.uk/2009/09/generating-text-in-haskell.html">Haskell</a>.</p>
<p>Basically, the idea is to:</p>
<ol>
<li>Read in a text document.</li>
<li>Count the frequency of word pairs in the document.</li>
<li>Pick a starting word.</li>
<li>Generate random text, using the word pair probabilities.</li>
</ol>
<p>I wanted to see what a simple <a href="https://www.factorcode.org">Factor</a>
implementation would look like, and thought I would share one below.</p>
<p>First, create a list of words from some lines of text. In
<a href="https://www.factorcode.org">Factor</a>, it is easy to write a processing
word that can then be used from standard input, files, or other input
streams.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">split-words</span> <span class="nf">( -- </span><span class="nv">sequence</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> V{ } <span class="nb">clone </span>[
</span></span><span class="line"><span class="cl"> <span class="s">"\r\n\t.,\" "</span> split [ <span class="nb">over push </span>] <span class="nb">each
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each-line harvest </span><span class="k">;
</span></span></span></code></pre></div><p>Next, create a sequence of all word pairs (including the pair linking
the tail to the head of the list) from the input sequence.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">word-pairs</span> <span class="nf">( </span><span class="nv">sequence</span> <span class="nf">-- </span><span class="nv">sequence'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">1 </span><span class="nb">head-slice append
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="m">1 </span><span class="nb">tail-slice zip </span><span class="k">;
</span></span></span></code></pre></div><p>Next, we need a map of word pairs, for random sampling by frequency of
occurrence. I have chosen to use words in the text as keys, and have the
values be a sequence of all words that ever followed it in the text.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">word-map</span> <span class="nf">( </span><span class="nv">sequence</span> <span class="nf">-- </span><span class="nv">assoc</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ H{ } <span class="nb">clone </span>] <span class="nb">dip </span>word-pairs [
</span></span><span class="line"><span class="cl"> [ <span class="nb">second </span>] [ <span class="nb">first </span>] <span class="nb">bi pick push-at
</span></span></span><span class="line"><span class="cl"> ] <span class="nb">each </span><span class="k">;
</span></span></span></code></pre></div><p>This makes sampling words with the proper probability quite simple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">next-word</span> <span class="nf">( </span><span class="nv">word</span> <span class="nv">assoc</span> <span class="nf">-- </span><span class="nv">word'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">at </span>random <span class="k">;
</span></span></span></code></pre></div><p>We can now generate paragraphs of random text with the “feel” of the
original document.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wordgen-from</span> <span class="nf">( </span><span class="nv">start</span> <span class="nv">n</span> <span class="nf">-- </span><span class="nv">string</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ [ <span class="nb">1vector </span>] <span class="nb">keep </span>split-words word-map ] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> [ [ next-word <span class="nb">dup pick push </span>] <span class="nb">keep </span>] <span class="nb">times
</span></span></span><span class="line"><span class="cl"> <span class="nb">2drop </span><span class="s">" "</span> <span class="nb">join </span><span class="k">;
</span></span></span></code></pre></div><p>And for convenience, as in the original blog posts, we can start
generating from a common English word.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wordgen</span> <span class="nf">( </span><span class="nv">n</span> <span class="nf">-- </span><span class="nv">string</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"the"</span> <span class="nb">swap </span>wordgen-from <span class="k">;
</span></span></span></code></pre></div><p>Putting all of this together, we can make 250 words from
<a href="https://www.gutenberg.org/etext/345">Dracula</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"/tmp/345.txt"</span> ascii [ <span class="m">250 </span>wordgen ] with-file-reader <span class="m">.
</span></span></span></code></pre></div><blockquote>
<p><em>“the bank notes of electronic works based on the same time to you are
fading and it came I too much Without taking a quick he had struck by
our eyes and she was not asleep twice when she sank down again There
were striving to think yet when the Huns This to get to her all at her
on it really Lucy’s father Goodbye my own time when she look at the
facts before them away and his diary which is out Look! Look! Look!
The bright as ever since then we had said Dr Van Helsing will be a
chair with you said Yes! And it was a pistol shot up to write for a
woman can love and added Friend John and pain nor a long enough You
were so seeing it high tide altogether I awoke She is confined within
a room lit by the old sweet that the temptation of lion-like disdain
His cries are great love must go there would tear my eyes the colour
of him and quiet; but still Renfield sitting on one ready Madam Mina
Harker had retired early Lucy and there ran downstairs then we get out
his fair accuracy when I found that my dear do our visit to oppose him
can look had a measure of the other since I secured and seemed to tell
upon your all our furs and the face I put in his eyes never be This
afternoon she was that such horrors when the”</em></p>
</blockquote>
<p>Not quite Bram Stoker, but just enough to taste the flavor. More
<a href="https://en.wikipedia.org/wiki/N-gram">complex</a> algorithms exist and can
be used for
<a href="https://scholarlykitchen.sspnet.org/2009/06/10/nonsense-for-dollars/">fun</a>
and
<a href="https://www.nytimes.com/2008/04/14/business/media/14link.html">profit</a>.
Similar ideas can even be applied to other areas, such as
<a href="https://news.bbc.co.uk/2/hi/science/nature/240642.stm">music</a>.</p>
Calculating with EBNF
https://re.factorcode.org/2009/08/calculating-with-ebnf.html
Wed, 12 Aug 2009 16:14:00 -0700https://re.factorcode.org/2009/08/calculating-with-ebnf.html<p><a href="https://www.factorcode.org">Factor</a> has a syntax for
<a href="https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_Form">EBNF</a> parsing
grammars, implemented in the
<a href="https://docs.factorcode.org/content/vocab-peg.ebnf.html">peg.ebnf</a>
vocabulary.</p>
<p>Several useful vocabularies are partially implemented using EBNF syntax,
including (among many) the formatting, globs, urls, regexp, brainfuck,
javascript, and smalltalk vocabularies.</p>
<p>I thought I’d walk through the creation of a small macro for parsing
“calculator” expressions, as a small demonstration of their utility.</p>
<p>First, the goal. We’d like to be able to parse, and compute, the
following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"2+2"</span> calc <span class="m">.
</span></span></span><span class="line"><span class="cl"><span class="m">4
</span></span></span></code></pre></div><p>So, first we need to define a parsing grammar for numbers. Since these
can be represented as an optional sign (for negative numbers), a whole
number, and optional digits (for floating point numbers), we start with
this structure:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">sign <span class="nb">= </span><span class="s">"-"</span>
</span></span><span class="line"><span class="cl">whole <span class="nb">= </span>([0-9])*
</span></span><span class="line"><span class="cl">digit <span class="nb">= </span><span class="s">"."</span> ([0-9])*
</span></span><span class="line"><span class="cl"><span class="nb">number = </span>sign? whole digit?
</span></span></code></pre></div><p>We would like the grammar to parse each component as a string, if
specified, concatenate it together, and then convert the resulting
string to a number, to be used by the rest of the grammar in
calculations.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">sign <span class="nb">= </span><span class="s">"-"</span>
</span></span><span class="line"><span class="cl">=> [[ <span class="nb">>string </span>]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">whole <span class="nb">= </span>([0-9])*
</span></span><span class="line"><span class="cl">=> [[ <span class="nb">>string </span>]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">digit <span class="nb">= </span><span class="s">"."</span> ([0-9])*
</span></span><span class="line"><span class="cl">=> [[ [ <span class="nb">>string </span>] <span class="nb">map concat </span>]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">number = </span>sign? whole digit?
</span></span><span class="line"><span class="cl">=> [[ [ <span class="no">f </span><span class="nb">eq? not </span>] <span class="nb">filter concat </span>string>number ]]
</span></span></code></pre></div><p>The calculator needs to support operations, so we start with the basic
four: addition, subtraction, multiplication, and division. These map the
characters “+”, “-”, “*”, and “/” to the words implementing those
functions in Factor.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">add <span class="nb">= </span><span class="s">"+"</span> => [[ [ <span class="nb">+ </span>] ]]
</span></span><span class="line"><span class="cl">sub <span class="nb">= </span><span class="s">"-"</span> => [[ [ <span class="nb">- </span>] ]]
</span></span><span class="line"><span class="cl">mul <span class="nb">= </span><span class="s">"*"</span> => [[ [ <span class="nb">* </span>] ]]
</span></span><span class="line"><span class="cl">div <span class="nb">= </span><span class="s">"/"</span> => [[ [ <span class="nb">/ </span>] ]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">ops <span class="nb">= </span>add|sub|mul|div
</span></span></code></pre></div><p>We define an expression to be two numbers combined by an operator, which
changes infix to postfix notation, using the
<a href="https://docs.factorcode.org/content/vocab-fry.html">fry</a> vocabulary to
create a quotation that will perform the intended computation.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">expr <span class="nb">= number </span>ops <span class="nb">number
</span></span></span><span class="line"><span class="cl">=> [[ [ <span class="nb">first </span>] [ <span class="nb">third </span>] [ <span class="nb">second </span>] <span class="nb">tri </span>'[ _ _ @ ] ]]
</span></span></code></pre></div><p>Putting this all together, we have our calculator:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">fry</span> <span class="nn">kernel</span> <span class="nn">macros</span> <span class="nn">peg.ebnf</span>
</span></span><span class="line"><span class="cl"><span class="nn">math</span> <span class="nn">math.parser</span> <span class="nn">sequences</span> <span class="nn">strings</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">EBNF: parse-calc
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">sign <span class="nb">= </span><span class="s">"-"</span> => [[ <span class="nb">>string </span>]]
</span></span><span class="line"><span class="cl">whole <span class="nb">= </span>([0-9])* => [[ <span class="nb">>string </span>]]
</span></span><span class="line"><span class="cl">digit <span class="nb">= </span><span class="s">"."</span> ([0-9])* => [[ [ <span class="nb">>string </span>] <span class="nb">map concat </span>]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">number = </span>sign? whole digit?
</span></span><span class="line"><span class="cl">=> [[ [ <span class="no">f </span><span class="nb">eq? not </span>] <span class="nb">filter concat </span>string>number ]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">add <span class="nb">= </span><span class="s">"+"</span> => [[ [ <span class="nb">+ </span>] ]]
</span></span><span class="line"><span class="cl">sub <span class="nb">= </span><span class="s">"-"</span> => [[ [ <span class="nb">- </span>] ]]
</span></span><span class="line"><span class="cl">mul <span class="nb">= </span><span class="s">"*"</span> => [[ [ <span class="nb">* </span>] ]]
</span></span><span class="line"><span class="cl">div <span class="nb">= </span><span class="s">"/"</span> => [[ [ <span class="nb">/ </span>] ]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">ops <span class="nb">= </span>add|sub|mul|div
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">expr <span class="nb">= number </span>ops <span class="nb">number
</span></span></span><span class="line"><span class="cl">=> [[ [ <span class="nb">first </span>] [ <span class="nb">third </span>] [ <span class="nb">second </span>] <span class="nb">tri </span>'[ _ _ @ ] ]]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">;EBNF
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MACRO:</span> <span class="nf">calc</span> <span class="nf">( </span><span class="nv">string</span> <span class="nf">-- ) </span>parse-calc <span class="k">;
</span></span></span></code></pre></div><p>You can use the
<a href="https://docs.factorcode.org/content/vocab-macros.expander.html">macros.expander</a>
vocabulary to see the code that is created when you compile the original
example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> [ <span class="s">"2+2"</span> calc ] expand-macros <span class="m">.
</span></span></span><span class="line"><span class="cl">[ <span class="m">2 2 </span>[ <span class="nb">+ </span>] <span class="nb">call </span>]
</span></span></code></pre></div><p>This model can be reasonably extended to support other operators (such
as “mod”, “shift”, “pow”, etc.), functions (such as “sin”, “sqrt”,
etc.), spaces in the expressions, chained expressions, grouped
expressions, input validation, compile-time calculations, and other
useful features. But, then it wouldn’t be so simple…</p>
IPInfoDB
https://re.factorcode.org/2009/06/ipinfodb.html
Fri, 26 Jun 2009 18:27:00 -0700https://re.factorcode.org/2009/06/ipinfodb.html<p>Providing location-based services is all the rage these days. On the
newer mobile devices, there are services that use GPS and nearby
cellular towers to improve your user experience with the knowledge of
where you are at any moment.</p>
<p>In the wild internet, there hasn’t been many convenient ways to do this,
but a recent website called <a href="https://www.ipinfodb.com">IPInfoDB</a>
(previously known as “IP Location Tools”), provides an
<a href="https://ipinfodb.com/ip_location_api.php">API</a> for mapping an IP address
to a physical address. The API consists of a PHP page that returns an
XML response that contains your location, or the location of an IP
address that you provide. They also seem to support CSV and JSON
responses.</p>
<p>For example, to lookup the location of an IP address used by
<a href="https://www.google.com">google.com</a>:</p>
<pre tabindex="0"><code>https://ipinfodb.com/ip_query.php?ip=74.125.45.100
</code></pre><p>Curious to see how easily it would be to use this API from
<a href="https://www.factorcode.org">Factor</a>, I wrote a vocabulary for accessing
IPInfoDB.</p>
<p>First, we define the characteristics of an <code>ip-info</code> tuple.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">TUPLE:</span> <span class="nc">ip-info</span> <span class="nv">ip</span> <span class="nv">country-code</span> <span class="nv">country-name</span> <span class="nv">region-code</span>
</span></span><span class="line"><span class="cl"><span class="nv">region-name</span> <span class="nv">city</span> <span class="nv">zip-code</span> <span class="nv">latitude</span> <span class="nv">longitude</span> <span class="nv">gmtoffset</span>
</span></span><span class="line"><span class="cl"><span class="nv">dstoffset</span> <span class="k">;
</span></span></span></code></pre></div><p>The API returns XML response, which we need to convert into an <code>ip-info</code>
object. For this, we use the excellent XML parsing vocabulary that has
seen a lot of improvements recently.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">find-tag</span> <span class="nf">( </span><span class="nv">tag</span> <span class="nv">name</span> <span class="nf">-- </span><span class="nv">value</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> deep-tag-named children>string <span class="k">; inline
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">xml>ip-info</span> <span class="nf">( </span><span class="nv">xml</span> <span class="nf">-- </span><span class="nv">info</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> [ ip-info <span class="nb">new </span>] <span class="nb">dip
</span></span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> [ <span class="s">"Ip"</span> find-tag >>ip ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"CountryCode"</span> find-tag >>country-code ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"CountryName"</span> find-tag >>country-name ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"RegionCode"</span> find-tag >>region-code ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"RegionName"</span> find-tag >>region-name ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"City"</span> find-tag >>city ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"ZipPostalCode"</span> find-tag >>zip-code ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"Latitude"</span> find-tag >>latitude ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"Longitude"</span> find-tag >>longitude ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"Gmtoffset"</span> find-tag >>gmtoffset ]
</span></span><span class="line"><span class="cl"> [ <span class="s">"Dstoffset"</span> find-tag >>dstoffset ]
</span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span><span class="k">;
</span></span></span></code></pre></div><p>With that done, it is very easy to make an http request to retrieve
one’s own location:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">locate-my-ip</span> <span class="nf">( -- </span><span class="nv">info</span> <span class="nf">) </span>
</span></span><span class="line"><span class="cl"> <span class="s">"https://ipinfodb.com/ip_query.php"</span>
</span></span><span class="line"><span class="cl"> http-get string>xml xml>ip-info <span class="nb">nip </span><span class="k">;
</span></span></span></code></pre></div><p>Or determine the location of any arbitrary IP:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">locate-ip</span> <span class="nf">( </span><span class="nv">ip</span> <span class="nf">-- </span><span class="nv">info</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">"https://ipinfodb.com/ip_query.php?ip="</span> <span class="nb">prepend
</span></span></span><span class="line"><span class="cl"> http-get string>xml xml>ip-info <span class="nb">nip </span><span class="k">;
</span></span></span></code></pre></div><p>The example I showed earlier would be:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"74.125.45.100"</span> locate-ip <span class="m">.
</span></span></span><span class="line"><span class="cl">T{ ip-info
</span></span><span class="line"><span class="cl"> { ip <span class="s">"74.125.45.100"</span> }
</span></span><span class="line"><span class="cl"> { country-code <span class="s">"US"</span> }
</span></span><span class="line"><span class="cl"> { country-name <span class="s">"United States"</span> }
</span></span><span class="line"><span class="cl"> { region-code <span class="s">"06"</span> }
</span></span><span class="line"><span class="cl"> { region-name <span class="s">"California"</span> }
</span></span><span class="line"><span class="cl"> { city <span class="s">"Mountain View"</span> }
</span></span><span class="line"><span class="cl"> { zip-code <span class="s">"94043"</span> }
</span></span><span class="line"><span class="cl"> { latitude <span class="s">"37.4192"</span> }
</span></span><span class="line"><span class="cl"> { longitude <span class="s">"-122.057"</span> }
</span></span><span class="line"><span class="cl"> { gmtoffset <span class="s">"-8.0"</span> }
</span></span><span class="line"><span class="cl"> { dstoffset <span class="s">"-7.0"</span> }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Presumably, if this code were to be used more frequently, it would need
some error handling for when the http server is not available, or
non-responsive.</p>
<p>Anyway, this vocabulary is available on my
<a href="https://www.github.com/mrjbq7/factor">GitHub</a></p>
Brainf*ck
https://re.factorcode.org/2009/06/brainf-ck.html
Sun, 07 Jun 2009 15:45:00 -0700https://re.factorcode.org/2009/06/brainf-ck.html<p>The <a href="https://en.wikipedia.org/wiki/Brainfuck">Brainfuck</a> programming
language is a curious invention. Seemingly useful only for proving
oneself as a True Geek at a party, it could also be useful for
educational purposes.</p>
<p>The first programming example in any language is typically “Hello,
world!”. In Brainfuck, this could be written as:</p>
<pre tabindex="0"><code>++++++++++[>+++++++>++++++++++>+++>+<<<<-]
>++.>+.+++++++..+++.>++.<<+++++++++++++++
.>.+++.------.--------.>+.>.
</code></pre><p>For fun, I thought I would build a Brainfuck compiler for
<a href="https://www.factorcode.org/">Factor</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">brainfuck</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="s">"
</span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[>+++++++>++++++++++>+++>+<<<<-]
</span></span></span><span class="line"><span class="cl"><span class="s"> >++.>+.+++++++..+++.>++.<<+++++++++++++++
</span></span></span><span class="line"><span class="cl"><span class="s"> .>.+++.------.--------.>+.>.
</span></span></span><span class="line"><span class="cl"><span class="s"> "</span> run-brainfuck
</span></span><span class="line"><span class="cl">Hello World!
</span></span></code></pre></div><p>Behind the scene, the Brainfuck code is being compiled into proper
Factor using a macro that parses the Brainfuck code string. When
translated into Factor, the “Hello, world!” example becomes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><brainfuck>
</span></span><span class="line"><span class="cl"><span class="m">10 </span>(+) [ (?) ] [
</span></span><span class="line"><span class="cl"> <span class="m">1 </span>(>) <span class="m">7 </span>(+) <span class="m">1 </span>(>) <span class="m">10 </span>(+) <span class="m">1 </span>(>) <span class="m">3 </span>(+)
</span></span><span class="line"><span class="cl"> <span class="m">1 </span>(>) <span class="m">1 </span>(+) <span class="m">4 </span>(<) <span class="m">1 </span>(-)
</span></span><span class="line"><span class="cl">] <span class="nb">while
</span></span></span><span class="line"><span class="cl"><span class="m">1 </span>(>) <span class="m">2 </span>(+) (.) <span class="m">1 </span>(>) <span class="m">1 </span>(+) (.)
</span></span><span class="line"><span class="cl"><span class="m">7 </span>(+) (.) (.) <span class="m">3 </span>(+) (.) <span class="m">1 </span>(>) <span class="m">2 </span>(+) (.)
</span></span><span class="line"><span class="cl"><span class="m">2 </span>(<) <span class="m">15 </span>(+) (.) <span class="m">1 </span>(>) (.) <span class="m">3 </span>(+)
</span></span><span class="line"><span class="cl">(.) <span class="m">6 </span>(-) (.) <span class="m">8 </span>(-) (.) <span class="m">1 </span>(>) <span class="m">1 </span>(+) (.)
</span></span><span class="line"><span class="cl"><span class="m">1 </span>(>) (.) <span class="nb">drop flush
</span></span></span></code></pre></div><p>I made only a slight optimization, which you might notice above, to
collapse a series of identical operators together into a single call to
the operator word, while staying true to the original set of Brainfuck
operators.</p>
<p>Some fun examples of Brainfuck in the <code>brainfuck-tests.factor</code> unit
tests include addition, multiplication, division, uppercase, and a cat
utility.</p>
<p>It is available on my <a href="https://www.github.com/mrjbq7/factor">GitHub</a>, and
hopefully will be pulled into the main repository soon.</p>
cd factor
https://re.factorcode.org/2009/02/cd-factor.html
Wed, 25 Feb 2009 01:43:00 -0800https://re.factorcode.org/2009/02/cd-factor.html<p>When working with <a href="https://www.factorcode.org">Factor</a> source code, you
might find yourself moving between several directories on the
filesystem, navigating and editing several files.</p>
<p>Due to source code being kept inside <code>core</code>, <code>basis</code>, <code>extra</code>, <code>work</code>,
and other locations, it is not always a simple matter of moving up and
down directories to find the files and vocabularies that you are looking
for.</p>
<p>Today, I wrote a bit of <code>bash</code> script to simplify, and improve, this
part of the development process.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># change directories to a factor module</span>
</span></span><span class="line"><span class="cl">cdfactor<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="nv">code</span><span class="o">=</span><span class="k">$(</span><span class="nb">printf</span> <span class="s2">"USING: io io.pathnames vocabs vocabs.loader ; "</span>
</span></span><span class="line"><span class="cl"> <span class="nb">printf</span> <span class="s2">"\"%s\" <vocab> vocab-source-path (normalize-path) print"</span> <span class="nv">$1</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"> <span class="nb">echo</span> <span class="nv">$code</span> > <span class="nv">$HOME</span>/.cdfactor
</span></span><span class="line"><span class="cl"> <span class="nv">fn</span><span class="o">=</span><span class="k">$(</span>factor <span class="nv">$HOME</span>/.cdfactor<span class="k">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">dn</span><span class="o">=</span><span class="k">$(</span>dirname <span class="nv">$fn</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"> <span class="nb">echo</span> <span class="nv">$dn</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">[</span> -z <span class="s2">"</span><span class="nv">$dn</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl"> <span class="nb">echo</span> <span class="s2">"Warning: directory '</span><span class="nv">$1</span><span class="s2">' not found"</span> 1><span class="p">&</span><span class="m">2</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span>
</span></span><span class="line"><span class="cl"> <span class="nb">cd</span> <span class="nv">$dn</span>
</span></span><span class="line"><span class="cl"> <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>This function takes a vocabulary as argument. When run, it generates a
temporary Factor script that is executed to find the pathname to the
source file. It then changes directories into the directory containing
the source file.</p>
<p>For example, if you want to switch to the <code>io.files</code> vocab, just run:</p>
<pre tabindex="0"><code>$ cdfactor io.files
</code></pre><p><em>Note: this requires <code>factor</code> to be on the <code>$PATH</code>.</em></p>
Readline support for Factor
https://re.factorcode.org/2009/02/readline-support-for-factor.html
Wed, 18 Feb 2009 15:05:00 -0800https://re.factorcode.org/2009/02/readline-support-for-factor.html<p>When using <a href="https://www.factorcode.org">Factor</a> from the command line,
you might notice that it lacks many of the features of typical REPL’s
offered by other languages. For example, using arrow keys, home, end,
delete, and backspace to navigate and edit text, and having proper
history support for using previous lines of text.</p>
<p>These features are often implemented by using the <a href="https://cnswww.cns.cwru.edu/php/chet/readline/rltop.html">GNU
Readline</a>
library.</p>
<p>But, how do you use readline with Factor? The easiest way is to use the
<code>rlwrap</code> command, which wraps an arbitrary command in readline.</p>
<p>To install it on Linux, run <code>sudo yum install rlwrap</code> (for RPM-based
distros) or <code> sudo apt-get install rlwrap</code> (for Debian-based distros).</p>
<p>On the Mac, you can install it with the
<a href="https://www.macports.org">MacPorts</a> system by running
<code>sudo port install rlwrap</code>.</p>
<p>Once installed, add <code>rlwrap</code> in front of your <code>factor</code> command. For
instance:</p>
<pre tabindex="0"><code>$ rlwrap ./factor
</code></pre><p>You should find interacting with Factor to be much easier.</p>
GCD
https://re.factorcode.org/2008/12/gcd.html
Tue, 23 Dec 2008 16:22:00 -0800https://re.factorcode.org/2008/12/gcd.html<p>The <a href="https://www.factorcode.org">Factor</a> IRC channel (<code>#concatenative</code>)
can be a helpful resource when trying to learn how to code in
<a href="https://www.factorcode.org">Factor</a>. Today, someone asked for help
implementing the <a href="https://en.wikipedia.org/wiki/Greatest_common_divisor">Greatest Common
Denominator</a>
algorithm in Factor. There are a variety of algorithms for solving for
the GCD of two numbers (and many are documented on the
<a href="https://en.wikipedia.org">Wikipedia</a> entry).</p>
<p>One possible solution uses the <a href="https://en.wikipedia.org/wiki/Euclidean_algorithm">Euclidean
Algorithm</a>, which is
implemented like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">gcd</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">a</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">gcd</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="n">a</span> <span class="o">%</span> <span class="n">b</span><span class="p">)</span>
</span></span></code></pre></div><p>We can translate that fairly directly to a recursive
<a href="https://www.factorcode.org">Factor</a> word:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">gcd</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">c</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">dup zero?
</span></span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>]
</span></span><span class="line"><span class="cl"> [ [ <span class="nb">mod </span>] <span class="nb">keep swap </span>gcd ]
</span></span><span class="line"><span class="cl"> <span class="nb">if </span><span class="k">;
</span></span></span></code></pre></div><p>It’s worth noting that <a href="https://www.factorcode.org">Factor</a> has a builtin
word <code>gcd</code> in the <code>math</code> vocabulary calculating this problem.</p>
<p>To learn how it works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">math</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="no">\ gcd</span> help
</span></span></code></pre></div><p>To see how it is implemented:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="kn">USE:</span> <span class="nn">math</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">scratchpad</span> <span class="no">\ gcd</span> see
</span></span></code></pre></div>Beginning Factor
https://re.factorcode.org/2008/12/beginning-factor.html
Mon, 15 Dec 2008 10:26:00 -0800https://re.factorcode.org/2008/12/beginning-factor.html<p>One of the <a href="https://www.factorcode.org/">Factor</a> bloggers posted a good
introduction to stack mechanics, shuffling, and combinators.</p>
<ul>
<li><a href="https://elasticdog.com/2008/11/beginning-factor-introduction/">Beginning Factor:
Introduction</a></li>
<li><a href="https://elasticdog.com/2008/12/beginning-factor-shufflers-and-combinators/">Beginning Factor: Shufflers &
Combinators</a></li>
</ul>
wp.factor
https://re.factorcode.org/2008/12/wp-factor.html
Tue, 09 Dec 2008 11:01:00 -0800https://re.factorcode.org/2008/12/wp-factor.html<p>I came across a <a href="https://ptrace.fefe.de/wp/">benchmark</a> for comparing
languages today. It did not contain a version for
<a href="https://www.factorcode.org/">Factor</a>, so I thought I would contribute
one.</p>
<p>The idea is fairly straightforward, and in the words of the author:</p>
<pre tabindex="0"><code>read stdin, tokenize into words
for each word count how often it occurs
output words and counts, sorted in descending order by count
</code></pre><p>My attempt is below:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="kn">USING:</span> <span class="nn">arrays</span> <span class="nn">assocs</span> <span class="nn">kernel</span> <span class="nn">io</span> <span class="nn">math</span> <span class="nn">math.parser</span>
</span></span><span class="line"><span class="cl"><span class="nn">prettyprint</span> <span class="nn">sequences</span> <span class="nn">splitting</span> <span class="nn">sorting</span> <span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">IN:</span> <span class="nn">wp</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">count-words</span> <span class="nf">( </span><span class="nv">assoc</span> <span class="nv">string</span> <span class="nf">-- </span><span class="nv">assoc'</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="s">" "</span> split <span class="nb">harvest </span>[ <span class="nb">over inc-at </span>] <span class="nb">each </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">sort-assoc</span> <span class="nf">( </span><span class="nv">assoc</span> <span class="nf">-- </span><span class="nv">seq</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> <span class="nb">>alist </span>sort-values <span class="nb">reverse </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">print-results</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- )
</span></span></span><span class="line"><span class="cl"> [ number>string <span class="s">" "</span> <span class="nb">glue print </span>] <span class="nb">assoc-each </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">wp</span> <span class="nf">( -- )
</span></span></span><span class="line"><span class="cl"> H{ } <span class="nb">clone
</span></span></span><span class="line"><span class="cl"> [ [ count-words ] <span class="nb">each-line </span>]
</span></span><span class="line"><span class="cl"> [ sort-assoc print-results ]
</span></span><span class="line"><span class="cl"> <span class="nb">bi drop </span><span class="k">;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">MAIN:</span> <span class="nf">wp</span>
</span></span></code></pre></div><p>You can run this from factor by putting it in a file called <code>wp.factor</code>
and running from the shell:</p>
<pre tabindex="0"><code>$ cat file.txt | ./factor -run=wp
</code></pre>Clamp
https://re.factorcode.org/2008/10/clamp.html
Thu, 02 Oct 2008 17:49:00 -0700https://re.factorcode.org/2008/10/clamp.html<p>One occasionally useful snippet of code when working with numbers is the
<a href="https://en.wikipedia.org/wiki/Clamping_%28graphics%29">clamp</a> function
for restricting a number to a range of values (either the minimum,
maximum, or somewhere in-between).</p>
<p>In a language like Python or C or Java, one might write this function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">clamp</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">max</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="nb">min</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="n">value</span><span class="p">))</span>
</span></span></code></pre></div><p>In <a href="https://www.factorcode.org/">Factor</a>, this is written in a similar,
but more terse, manner:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">clamp</span> <span class="nf">( </span><span class="nv">a</span> <span class="nv">value</span> <span class="nv">b</span> <span class="nf">-- </span><span class="nv">x</span> <span class="nf">)
</span></span></span><span class="line"><span class="cl"> min max <span class="k">;
</span></span></span></code></pre></div><p>Since the arguments are located on the stack, the “min” word operates on
“value” and “b”, placing the “result” on the stack, then the “max” word
operates on “a” and “result”, placing the final result on the stack.</p>
<p>Simple. Elegant.</p>
Factor
https://re.factorcode.org/2008/10/factor.html
Thu, 02 Oct 2008 14:02:00 -0700https://re.factorcode.org/2008/10/factor.html<p><a href="https://www.factorcode.org/">Factor</a> is a high-level language written by
Slava Pestov with many interesting characteristics. </p>
<p>It is stack-based with high-level features including dynamic types,
extensible syntax, macros, and garbage collection. On a practical side,
Factor has a mature library, supports many different platforms, and has
been extensively documented. Similar in many ways to other
<a href="https://www.concatenative.org">concatenative</a> programming languages, the
syntax could be considered terse, but powerful.</p>
<p>It is a very different language than most with a lot of potential for
its future. </p>
<p>This blog will contain articles and code related to Factor, and should
serve as both a learning guide and exploration of this language.</p>